mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
[red-knot] More Type
constructors (#14227)
This commit is contained in:
parent
c9b84e2a85
commit
e598240f04
5 changed files with 84 additions and 115 deletions
|
@ -478,6 +478,18 @@ impl<'db> Type<'db> {
|
|||
Self::SubclassOf(SubclassOfType { class })
|
||||
}
|
||||
|
||||
pub fn string_literal(db: &'db dyn Db, string: &str) -> Self {
|
||||
Self::StringLiteral(StringLiteralType::new(db, string))
|
||||
}
|
||||
|
||||
pub fn bytes_literal(db: &'db dyn Db, bytes: &[u8]) -> Self {
|
||||
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
|
||||
}
|
||||
|
||||
pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self {
|
||||
Self::Tuple(TupleType::new(db, elements))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn negate(&self, db: &'db dyn Db) -> Type<'db> {
|
||||
IntersectionBuilder::new(db).add_negative(*self).build()
|
||||
|
@ -1458,7 +1470,7 @@ impl<'db> Type<'db> {
|
|||
Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db),
|
||||
Type::StringLiteral(_) | Type::LiteralString => *self,
|
||||
Type::KnownInstance(known_instance) => {
|
||||
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db)))
|
||||
Type::string_literal(db, known_instance.repr(db))
|
||||
}
|
||||
// TODO: handle more complex types
|
||||
_ => KnownClass::Str.to_instance(db),
|
||||
|
@ -1470,17 +1482,15 @@ impl<'db> Type<'db> {
|
|||
#[must_use]
|
||||
pub fn repr(&self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
Type::IntLiteral(number) => Type::StringLiteral(StringLiteralType::new(db, {
|
||||
number.to_string().into_boxed_str()
|
||||
})),
|
||||
Type::BooleanLiteral(true) => Type::StringLiteral(StringLiteralType::new(db, "True")),
|
||||
Type::BooleanLiteral(false) => Type::StringLiteral(StringLiteralType::new(db, "False")),
|
||||
Type::StringLiteral(literal) => Type::StringLiteral(StringLiteralType::new(db, {
|
||||
format!("'{}'", literal.value(db).escape_default()).into_boxed_str()
|
||||
})),
|
||||
Type::IntLiteral(number) => Type::string_literal(db, &number.to_string()),
|
||||
Type::BooleanLiteral(true) => Type::string_literal(db, "True"),
|
||||
Type::BooleanLiteral(false) => Type::string_literal(db, "False"),
|
||||
Type::StringLiteral(literal) => {
|
||||
Type::string_literal(db, &format!("'{}'", literal.value(db).escape_default()))
|
||||
}
|
||||
Type::LiteralString => Type::LiteralString,
|
||||
Type::KnownInstance(known_instance) => {
|
||||
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db)))
|
||||
Type::string_literal(db, known_instance.repr(db))
|
||||
}
|
||||
// TODO: handle more complex types
|
||||
_ => KnownClass::Str.to_instance(db),
|
||||
|
@ -1738,42 +1748,28 @@ impl<'db> KnownInstanceType<'db> {
|
|||
}
|
||||
|
||||
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||
match (self, name) {
|
||||
(Self::TypeVar(typevar), "__name__") => Symbol::Type(
|
||||
Type::StringLiteral(StringLiteralType::new(db, typevar.name(db).as_str())),
|
||||
Boundness::Bound,
|
||||
),
|
||||
(Self::TypeVar(typevar), "__bound__") => Symbol::Type(
|
||||
typevar
|
||||
.upper_bound(db)
|
||||
let ty = match (self, name) {
|
||||
(Self::TypeVar(typevar), "__name__") => Type::string_literal(db, typevar.name(db)),
|
||||
(Self::TypeVar(typevar), "__bound__") => typevar
|
||||
.upper_bound(db)
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or_else(|| KnownClass::NoneType.to_instance(db)),
|
||||
(Self::TypeVar(typevar), "__constraints__") => {
|
||||
let tuple_elements: Vec<Type<'db>> = typevar
|
||||
.constraints(db)
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or(KnownClass::NoneType.to_instance(db)),
|
||||
Boundness::Bound,
|
||||
),
|
||||
(Self::TypeVar(typevar), "__constraints__") => Symbol::Type(
|
||||
Type::Tuple(TupleType::new(
|
||||
db,
|
||||
typevar
|
||||
.constraints(db)
|
||||
.map(|constraints| {
|
||||
constraints
|
||||
.iter()
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.collect::<Box<_>>()
|
||||
})
|
||||
.unwrap_or_else(|| std::iter::empty().collect::<Box<_>>()),
|
||||
)),
|
||||
Boundness::Bound,
|
||||
),
|
||||
(Self::TypeVar(typevar), "__default__") => Symbol::Type(
|
||||
typevar
|
||||
.default_ty(db)
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
|
||||
Boundness::Bound,
|
||||
),
|
||||
_ => self.instance_fallback(db).member(db, name),
|
||||
}
|
||||
.collect();
|
||||
Type::tuple(db, &tuple_elements)
|
||||
}
|
||||
(Self::TypeVar(typevar), "__default__") => typevar
|
||||
.default_ty(db)
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
|
||||
_ => return self.instance_fallback(db).member(db, name),
|
||||
};
|
||||
ty.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2544,11 +2540,8 @@ impl<'db> Class<'db> {
|
|||
/// The member resolves to a member on the class itself or any of its proper superclasses.
|
||||
pub(crate) fn class_member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||
if name == "__mro__" {
|
||||
let tuple_elements: Box<_> = self.iter_mro(db).map(Type::from).collect();
|
||||
return Symbol::Type(
|
||||
Type::Tuple(TupleType::new(db, tuple_elements)),
|
||||
Boundness::Bound,
|
||||
);
|
||||
let tuple_elements: Vec<Type<'db>> = self.iter_mro(db).map(Type::from).collect();
|
||||
return Type::tuple(db, &tuple_elements).into();
|
||||
}
|
||||
|
||||
if name == "__class__" {
|
||||
|
@ -2880,10 +2873,10 @@ mod tests {
|
|||
Ty::Any => Type::Any,
|
||||
Ty::Todo => Type::Todo,
|
||||
Ty::IntLiteral(n) => Type::IntLiteral(n),
|
||||
Ty::StringLiteral(s) => Type::StringLiteral(StringLiteralType::new(db, s)),
|
||||
Ty::StringLiteral(s) => Type::string_literal(db, s),
|
||||
Ty::BooleanLiteral(b) => Type::BooleanLiteral(b),
|
||||
Ty::LiteralString => Type::LiteralString,
|
||||
Ty::BytesLiteral(s) => Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes())),
|
||||
Ty::BytesLiteral(s) => Type::bytes_literal(db, s.as_bytes()),
|
||||
Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db),
|
||||
Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db),
|
||||
Ty::TypingLiteral => Type::KnownInstance(KnownInstanceType::Literal),
|
||||
|
@ -2903,8 +2896,8 @@ mod tests {
|
|||
builder.build()
|
||||
}
|
||||
Ty::Tuple(tys) => {
|
||||
let elements: Box<_> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
Type::Tuple(TupleType::new(db, elements))
|
||||
let elements: Vec<Type> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
Type::tuple(db, &elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@ mod tests {
|
|||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::stdlib::typing_symbol;
|
||||
use crate::types::{global_symbol, KnownClass, StringLiteralType, UnionBuilder};
|
||||
use crate::types::{global_symbol, KnownClass, UnionBuilder};
|
||||
use crate::ProgramSettings;
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
|
@ -775,7 +775,7 @@ mod tests {
|
|||
.build();
|
||||
assert_eq!(ty, s);
|
||||
|
||||
let literal = Type::StringLiteral(StringLiteralType::new(&db, "a"));
|
||||
let literal = Type::string_literal(&db, "a");
|
||||
let expected = IntersectionBuilder::new(&db)
|
||||
.add_positive(s)
|
||||
.add_negative(literal)
|
||||
|
@ -878,7 +878,7 @@ mod tests {
|
|||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_positive(s)
|
||||
.add_negative(Type::StringLiteral(StringLiteralType::new(&db, "a")))
|
||||
.add_negative(Type::string_literal(&db, "a"))
|
||||
.add_negative(t)
|
||||
.build();
|
||||
assert_eq!(ty, Type::Never);
|
||||
|
@ -912,7 +912,7 @@ mod tests {
|
|||
let db = setup_db();
|
||||
|
||||
let t_p = KnownClass::Int.to_instance(&db);
|
||||
let t_n = Type::StringLiteral(StringLiteralType::new(&db, "t_n"));
|
||||
let t_n = Type::string_literal(&db, "t_n");
|
||||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_positive(t_p)
|
||||
|
|
|
@ -337,9 +337,7 @@ mod tests {
|
|||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::types::{
|
||||
global_symbol, BytesLiteralType, SliceLiteralType, StringLiteralType, Type, UnionType,
|
||||
};
|
||||
use crate::types::{global_symbol, SliceLiteralType, Type, UnionType};
|
||||
use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings};
|
||||
|
||||
fn setup_db() -> TestDb {
|
||||
|
@ -385,12 +383,12 @@ mod tests {
|
|||
Type::Unknown,
|
||||
Type::IntLiteral(-1),
|
||||
global_symbol(&db, mod_file, "A").expect_type(),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, "A")),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, [0u8].as_slice())),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, [7u8].as_slice())),
|
||||
Type::string_literal(&db, "A"),
|
||||
Type::bytes_literal(&db, &[0u8]),
|
||||
Type::bytes_literal(&db, &[7u8]),
|
||||
Type::IntLiteral(0),
|
||||
Type::IntLiteral(1),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, "B")),
|
||||
Type::string_literal(&db, "B"),
|
||||
global_symbol(&db, mod_file, "foo").expect_type(),
|
||||
global_symbol(&db, mod_file, "bar").expect_type(),
|
||||
global_symbol(&db, mod_file, "B").expect_type(),
|
||||
|
|
|
@ -56,11 +56,10 @@ use crate::types::mro::MroErrorKind;
|
|||
use crate::types::unpacker::{UnpackResult, Unpacker};
|
||||
use crate::types::{
|
||||
bindings_ty, builtins_symbol, declarations_ty, global_symbol, symbol, typing_extensions_symbol,
|
||||
Boundness, BytesLiteralType, Class, ClassLiteralType, FunctionType, InstanceType,
|
||||
IntersectionBuilder, IntersectionType, IterationOutcome, KnownClass, KnownFunction,
|
||||
KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, StringLiteralType,
|
||||
Symbol, Truthiness, TupleType, Type, TypeArrayDisplay, TypeVarBoundOrConstraints,
|
||||
TypeVarInstance, UnionBuilder, UnionType,
|
||||
Boundness, Class, ClassLiteralType, FunctionType, InstanceType, IntersectionBuilder,
|
||||
IntersectionType, IterationOutcome, KnownClass, KnownFunction, KnownInstanceType,
|
||||
MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, Symbol, Truthiness, TupleType, Type,
|
||||
TypeArrayDisplay, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionType,
|
||||
};
|
||||
use crate::unpack::Unpack;
|
||||
use crate::util::subscript::{PyIndex, PySlice};
|
||||
|
@ -2202,7 +2201,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
fn infer_string_literal_expression(&mut self, literal: &ast::ExprStringLiteral) -> Type<'db> {
|
||||
if literal.value.len() <= Self::MAX_STRING_LITERAL_SIZE {
|
||||
Type::StringLiteral(StringLiteralType::new(self.db, literal.value.to_str()))
|
||||
Type::string_literal(self.db, literal.value.to_str())
|
||||
} else {
|
||||
Type::LiteralString
|
||||
}
|
||||
|
@ -2210,10 +2209,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
fn infer_bytes_literal_expression(&mut self, literal: &ast::ExprBytesLiteral) -> Type<'db> {
|
||||
// TODO: ignoring r/R prefixes for now, should normalize bytes values
|
||||
Type::BytesLiteral(BytesLiteralType::new(
|
||||
self.db,
|
||||
literal.value.bytes().collect::<Box<[u8]>>(),
|
||||
))
|
||||
let bytes: Vec<u8> = literal.value.bytes().collect();
|
||||
Type::bytes_literal(self.db, &bytes)
|
||||
}
|
||||
|
||||
fn infer_fstring_expression(&mut self, fstring: &ast::ExprFString) -> Type<'db> {
|
||||
|
@ -2280,12 +2277,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
parenthesized: _,
|
||||
} = tuple;
|
||||
|
||||
let element_types = elts
|
||||
.iter()
|
||||
.map(|elt| self.infer_expression(elt))
|
||||
.collect::<Vec<_>>();
|
||||
let element_types: Vec<Type<'db>> =
|
||||
elts.iter().map(|elt| self.infer_expression(elt)).collect();
|
||||
|
||||
Type::Tuple(TupleType::new(self.db, element_types.into_boxed_slice()))
|
||||
Type::tuple(self.db, &element_types)
|
||||
}
|
||||
|
||||
fn infer_list_expression(&mut self, list: &ast::ExprList) -> Type<'db> {
|
||||
|
@ -2987,21 +2982,15 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
}
|
||||
|
||||
(Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => {
|
||||
Some(Type::BytesLiteral(BytesLiteralType::new(
|
||||
self.db,
|
||||
[lhs.value(self.db).as_ref(), rhs.value(self.db).as_ref()]
|
||||
.concat()
|
||||
.into_boxed_slice(),
|
||||
)))
|
||||
let bytes = [&**lhs.value(self.db), &**rhs.value(self.db)].concat();
|
||||
Some(Type::bytes_literal(self.db, &bytes))
|
||||
}
|
||||
|
||||
(Type::StringLiteral(lhs), Type::StringLiteral(rhs), ast::Operator::Add) => {
|
||||
let lhs_value = lhs.value(self.db).to_string();
|
||||
let rhs_value = rhs.value(self.db).as_ref();
|
||||
let ty = if lhs_value.len() + rhs_value.len() <= Self::MAX_STRING_LITERAL_SIZE {
|
||||
Type::StringLiteral(StringLiteralType::new(self.db, {
|
||||
(lhs_value + rhs_value).into_boxed_str()
|
||||
}))
|
||||
Type::string_literal(self.db, &(lhs_value + rhs_value))
|
||||
} else {
|
||||
Type::LiteralString
|
||||
};
|
||||
|
@ -3017,16 +3006,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
(Type::StringLiteral(s), Type::IntLiteral(n), ast::Operator::Mult)
|
||||
| (Type::IntLiteral(n), Type::StringLiteral(s), ast::Operator::Mult) => {
|
||||
let ty = if n < 1 {
|
||||
Type::StringLiteral(StringLiteralType::new(self.db, ""))
|
||||
Type::string_literal(self.db, "")
|
||||
} else if let Ok(n) = usize::try_from(n) {
|
||||
if n.checked_mul(s.value(self.db).len())
|
||||
.is_some_and(|new_length| new_length <= Self::MAX_STRING_LITERAL_SIZE)
|
||||
{
|
||||
let new_literal = s.value(self.db).repeat(n);
|
||||
Type::StringLiteral(StringLiteralType::new(
|
||||
self.db,
|
||||
new_literal.into_boxed_str(),
|
||||
))
|
||||
Type::string_literal(self.db, &new_literal)
|
||||
} else {
|
||||
Type::LiteralString
|
||||
}
|
||||
|
@ -3039,7 +3025,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
(Type::LiteralString, Type::IntLiteral(n), ast::Operator::Mult)
|
||||
| (Type::IntLiteral(n), Type::LiteralString, ast::Operator::Mult) => {
|
||||
let ty = if n < 1 {
|
||||
Type::StringLiteral(StringLiteralType::new(self.db, ""))
|
||||
Type::string_literal(self.db, "")
|
||||
} else {
|
||||
Type::LiteralString
|
||||
};
|
||||
|
@ -3752,7 +3738,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
if let Ok(new_elements) = elements.py_slice(start, stop, step) {
|
||||
let new_elements: Vec<_> = new_elements.copied().collect();
|
||||
Type::Tuple(TupleType::new(self.db, new_elements.into_boxed_slice()))
|
||||
Type::tuple(self.db, &new_elements)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
Type::Unknown
|
||||
|
@ -3766,12 +3752,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
literal_value
|
||||
.chars()
|
||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||
.map(|ch| {
|
||||
Type::StringLiteral(StringLiteralType::new(
|
||||
self.db,
|
||||
ch.to_string().into_boxed_str(),
|
||||
))
|
||||
})
|
||||
.map(|ch| Type::string_literal(self.db, &ch.to_string()))
|
||||
.unwrap_or_else(|_| {
|
||||
self.diagnostics.add_index_out_of_bounds(
|
||||
"string",
|
||||
|
@ -3791,7 +3772,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
let chars: Vec<_> = literal_value.chars().collect();
|
||||
let result = if let Ok(new_chars) = chars.py_slice(start, stop, step) {
|
||||
let literal: String = new_chars.collect();
|
||||
Type::StringLiteral(StringLiteralType::new(self.db, literal.into_boxed_str()))
|
||||
Type::string_literal(self.db, &literal)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
Type::Unknown
|
||||
|
@ -3806,9 +3787,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
literal_value
|
||||
.iter()
|
||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||
.map(|byte| {
|
||||
Type::BytesLiteral(BytesLiteralType::new(self.db, [*byte].as_slice()))
|
||||
})
|
||||
.map(|byte| Type::bytes_literal(self.db, &[*byte]))
|
||||
.unwrap_or_else(|_| {
|
||||
self.diagnostics.add_index_out_of_bounds(
|
||||
"bytes literal",
|
||||
|
@ -3827,7 +3806,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
if let Ok(new_bytes) = literal_value.py_slice(start, stop, step) {
|
||||
let new_bytes: Vec<u8> = new_bytes.copied().collect();
|
||||
Type::BytesLiteral(BytesLiteralType::new(self.db, new_bytes.into_boxed_slice()))
|
||||
Type::bytes_literal(self.db, &new_bytes)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
Type::Unknown
|
||||
|
@ -4262,7 +4241,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
if return_todo {
|
||||
Type::Todo
|
||||
} else {
|
||||
Type::Tuple(TupleType::new(self.db, element_types.into_boxed_slice()))
|
||||
Type::tuple(self.db, &element_types)
|
||||
}
|
||||
}
|
||||
single_element => {
|
||||
|
@ -4270,7 +4249,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) {
|
||||
Type::Todo
|
||||
} else {
|
||||
Type::Tuple(TupleType::new(self.db, Box::from([single_element_ty])))
|
||||
Type::tuple(self.db, &[single_element_ty])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4541,7 +4520,7 @@ impl StringPartsCollector {
|
|||
if self.expression {
|
||||
KnownClass::Str.to_instance(db)
|
||||
} else if let Some(concatenated) = self.concatenated {
|
||||
Type::StringLiteral(StringLiteralType::new(db, concatenated.into_boxed_str()))
|
||||
Type::string_literal(db, &concatenated)
|
||||
} else {
|
||||
Type::LiteralString
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
|
|||
|
||||
use crate::semantic_index::ast_ids::{HasScopedAstId, ScopedExpressionId};
|
||||
use crate::semantic_index::symbol::ScopeId;
|
||||
use crate::types::{TupleType, Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
|
||||
use crate::types::{Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
|
||||
use crate::Db;
|
||||
|
||||
/// Unpacks the value expression type to their respective targets.
|
||||
|
@ -93,11 +93,10 @@ impl<'db> Unpacker<'db> {
|
|||
// further and deconstruct to an array of `StringLiteral` with each
|
||||
// individual character, instead of just an array of `LiteralString`, but
|
||||
// there would be a cost and it's not clear that it's worth it.
|
||||
let value_ty = Type::Tuple(TupleType::new(
|
||||
let value_ty = Type::tuple(
|
||||
self.db,
|
||||
vec![Type::LiteralString; string_literal_ty.len(self.db)]
|
||||
.into_boxed_slice(),
|
||||
));
|
||||
&vec![Type::LiteralString; string_literal_ty.len(self.db)],
|
||||
);
|
||||
self.unpack(target, value_ty, scope);
|
||||
}
|
||||
_ => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue