diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md index 380452f6c2..186139e1c3 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md @@ -341,6 +341,39 @@ reveal_type(C(1, True)) # revealed: C[Literal[1]] wrong_innards: C[int] = C("five", 1) ``` +### Some `__init__` overloads only apply to certain specializations + +```py +from typing import overload, Generic, TypeVar + +T = TypeVar("T") + +class C(Generic[T]): + @overload + def __init__(self: "C[str]", x: str) -> None: ... + @overload + def __init__(self: "C[bytes]", x: bytes) -> None: ... + @overload + def __init__(self, x: int) -> None: ... + def __init__(self, x: str | bytes | int) -> None: ... + +reveal_type(C("string")) # revealed: C[str] +reveal_type(C(b"bytes")) # revealed: C[bytes] +reveal_type(C(12)) # revealed: C[Unknown] + +C[str]("string") +C[str](b"bytes") # error: [no-matching-overload] +C[str](12) + +C[bytes]("string") # error: [no-matching-overload] +C[bytes](b"bytes") +C[bytes](12) + +C[None]("string") # error: [no-matching-overload] +C[None](b"bytes") # error: [no-matching-overload] +C[None](12) +``` + ## Generic subclass When a generic subclass fills its superclass's type parameter with one of its own, the actual types diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md index c5d4772d36..9d4c0a429d 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md @@ -279,6 +279,37 @@ reveal_type(C(1, True)) # revealed: C[Literal[1]] wrong_innards: C[int] = C("five", 1) ``` +### Some `__init__` overloads only apply to certain specializations + +```py +from typing import overload + +class C[T]: + @overload + def __init__(self: C[str], x: str) -> None: ... + @overload + def __init__(self: C[bytes], x: bytes) -> None: ... + @overload + def __init__(self, x: int) -> None: ... + def __init__(self, x: str | bytes | int) -> None: ... + +reveal_type(C("string")) # revealed: C[str] +reveal_type(C(b"bytes")) # revealed: C[bytes] +reveal_type(C(12)) # revealed: C[Unknown] + +C[str]("string") +C[str](b"bytes") # error: [no-matching-overload] +C[str](12) + +C[bytes]("string") # error: [no-matching-overload] +C[bytes](b"bytes") +C[bytes](12) + +C[None]("string") # error: [no-matching-overload] +C[None](b"bytes") # error: [no-matching-overload] +C[None](12) +``` + ## Generic subclass When a generic subclass fills its superclass's type parameter with one of its own, the actual types diff --git a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md index 4376f1db0d..90426ff6b1 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md @@ -102,7 +102,7 @@ class C[T]: return "a" reveal_type(getattr_static(C[int], "f")) # revealed: def f(self, x: int) -> str -reveal_type(getattr_static(C[int], "f").__get__) # revealed: +reveal_type(getattr_static(C[int], "f").__get__) # revealed: reveal_type(getattr_static(C[int], "f").__get__(None, C[int])) # revealed: def f(self, x: int) -> str # revealed: bound method C[int].f(x: int) -> str reveal_type(getattr_static(C[int], "f").__get__(C[int](), C[int])) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 15d675a869..ede7203475 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1170,6 +1170,21 @@ impl<'db> Type<'db> { target.is_equivalent_to(db, Type::object(db)) } + // These clauses handle type variants that include function literals. A function + // literal is the subtype of itself, and not of any other function literal. However, + // our representation of a function literal includes any specialization that should be + // applied to the signature. Different specializations of the same function literal are + // only subtypes of each other if they result in the same signature. + (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { + self_function.is_subtype_of(db, target_function) + } + (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => { + self_method.is_subtype_of(db, target_method) + } + (Type::MethodWrapper(self_method), Type::MethodWrapper(target_method)) => { + self_method.is_subtype_of(db, target_method) + } + // No literal type is a subtype of any other literal type, unless they are the same // type (which is handled above). This case is not necessary from a correctness // perspective (the fallback cases below will handle it correctly), but it is important @@ -1504,6 +1519,21 @@ impl<'db> Type<'db> { true } + // These clauses handle type variants that include function literals. A function + // literal is assignable to itself, and not to any other function literal. However, our + // representation of a function literal includes any specialization that should be + // applied to the signature. Different specializations of the same function literal are + // only assignable to each other if they result in the same signature. + (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { + self_function.is_assignable_to(db, target_function) + } + (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => { + self_method.is_assignable_to(db, target_method) + } + (Type::MethodWrapper(self_method), Type::MethodWrapper(target_method)) => { + self_method.is_assignable_to(db, target_method) + } + // `type[Any]` is assignable to any `type[...]` type, because `type[Any]` can // materialize to any `type[...]` type. (Type::SubclassOf(subclass_of_ty), Type::SubclassOf(_)) @@ -1627,6 +1657,15 @@ impl<'db> Type<'db> { left.is_equivalent_to(db, right) } (Type::Tuple(left), Type::Tuple(right)) => left.is_equivalent_to(db, right), + (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { + self_function.is_equivalent_to(db, target_function) + } + (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => { + self_method.is_equivalent_to(db, target_method) + } + (Type::MethodWrapper(self_method), Type::MethodWrapper(target_method)) => { + self_method.is_equivalent_to(db, target_method) + } (Type::Callable(left), Type::Callable(right)) => left.is_equivalent_to(db, right), (Type::NominalInstance(left), Type::NominalInstance(right)) => { left.is_equivalent_to(db, right) @@ -1682,6 +1721,15 @@ impl<'db> Type<'db> { first.is_gradual_equivalent_to(db, second) } + (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { + self_function.is_gradual_equivalent_to(db, target_function) + } + (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => { + self_method.is_gradual_equivalent_to(db, target_method) + } + (Type::MethodWrapper(self_method), Type::MethodWrapper(target_method)) => { + self_method.is_gradual_equivalent_to(db, target_method) + } (Type::Callable(first), Type::Callable(second)) => { first.is_gradual_equivalent_to(db, second) } @@ -6727,6 +6775,7 @@ impl<'db> FunctionType<'db> { /// would depend on the function's AST and rerun for every change in that file. #[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)] pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> { + let inherited_generic_context = self.inherited_generic_context(db); let specialization = self.specialization(db); if let Some(overloaded) = self.to_overloaded(db) { FunctionSignature { @@ -6734,13 +6783,13 @@ impl<'db> FunctionType<'db> { Type::FunctionLiteral(self), overloaded.overloads.iter().copied().map(|overload| { overload - .internal_signature(db) + .internal_signature(db, inherited_generic_context) .apply_optional_specialization(db, specialization) }), ), implementation: overloaded.implementation.map(|implementation| { implementation - .internal_signature(db) + .internal_signature(db, inherited_generic_context) .apply_optional_specialization(db, specialization) }), } @@ -6748,7 +6797,7 @@ impl<'db> FunctionType<'db> { FunctionSignature { overloads: CallableSignature::single( Type::FunctionLiteral(self), - self.internal_signature(db) + self.internal_signature(db, inherited_generic_context) .apply_optional_specialization(db, specialization), ), implementation: None, @@ -6766,7 +6815,11 @@ impl<'db> FunctionType<'db> { /// /// Don't call this when checking any other file; only when type-checking the function body /// scope. - fn internal_signature(self, db: &'db dyn Db) -> Signature<'db> { + fn internal_signature( + self, + db: &'db dyn Db, + inherited_generic_context: Option>, + ) -> Signature<'db> { let scope = self.body_scope(db); let function_stmt_node = scope.node(db).expect_function(); let definition = self.definition(db); @@ -6777,7 +6830,7 @@ impl<'db> FunctionType<'db> { Signature::from_function( db, generic_context, - self.inherited_generic_context(db), + inherited_generic_context, definition, function_stmt_node, ) @@ -6934,6 +6987,42 @@ impl<'db> FunctionType<'db> { } } + fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool { + // A function literal is the subtype of itself, and not of any other function literal. + // However, our representation of a function literal includes any specialization that + // should be applied to the signature. Different specializations of the same function + // literal are only subtypes of each other if they result in subtype signatures. + self.body_scope(db) == other.body_scope(db) + && self + .into_callable_type(db) + .is_subtype_of(db, other.into_callable_type(db)) + } + + fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool { + // A function literal is assignable to itself, and not to any other function literal. + // However, our representation of a function literal includes any specialization that + // should be applied to the signature. Different specializations of the same function + // literal are only assignable to each other if they result in assignable signatures. + self.body_scope(db) == other.body_scope(db) + && self + .into_callable_type(db) + .is_assignable_to(db, other.into_callable_type(db)) + } + + fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + self.body_scope(db) == other.body_scope(db) + && self + .into_callable_type(db) + .is_equivalent_to(db, other.into_callable_type(db)) + } + + fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + self.body_scope(db) == other.body_scope(db) + && self + .into_callable_type(db) + .is_gradual_equivalent_to(db, other.into_callable_type(db)) + } + /// Returns a tuple of two spans. The first is /// the span for the identifier of the function /// definition for `self`. The second is @@ -7215,6 +7304,43 @@ impl<'db> BoundMethodType<'db> { .map(signatures::Signature::bind_self), )) } + + fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool { + // A bound method is a typically a subtype of itself. However, we must explicitly verify + // the subtyping of the underlying function signatures (since they might be specialized + // differently), and of the bound self parameter (taking care that parameters, including a + // bound self parameter, are contravariant.) + self.function(db).is_subtype_of(db, other.function(db)) + && other + .self_instance(db) + .is_subtype_of(db, self.self_instance(db)) + } + + fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool { + // A bound method is a typically assignable to itself. However, we must explicitly verify + // the assignability of the underlying function signatures (since they might be specialized + // differently), and of the bound self parameter (taking care that parameters, including a + // bound self parameter, are contravariant.) + self.function(db).is_assignable_to(db, other.function(db)) + && other + .self_instance(db) + .is_assignable_to(db, self.self_instance(db)) + } + + fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + self.function(db).is_equivalent_to(db, other.function(db)) + && other + .self_instance(db) + .is_equivalent_to(db, self.self_instance(db)) + } + + fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + self.function(db) + .is_gradual_equivalent_to(db, other.function(db)) + && other + .self_instance(db) + .is_gradual_equivalent_to(db, self.self_instance(db)) + } } /// This type represents the set of all callable objects with a certain, possibly overloaded, @@ -7445,6 +7571,140 @@ pub enum MethodWrapperKind<'db> { StrStartswith(StringLiteralType<'db>), } +impl<'db> MethodWrapperKind<'db> { + fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool { + match (self, other) { + ( + MethodWrapperKind::FunctionTypeDunderGet(self_function), + MethodWrapperKind::FunctionTypeDunderGet(other_function), + ) => self_function.is_subtype_of(db, other_function), + + ( + MethodWrapperKind::FunctionTypeDunderCall(self_function), + MethodWrapperKind::FunctionTypeDunderCall(other_function), + ) => self_function.is_subtype_of(db, other_function), + + (MethodWrapperKind::PropertyDunderGet(_), MethodWrapperKind::PropertyDunderGet(_)) + | (MethodWrapperKind::PropertyDunderSet(_), MethodWrapperKind::PropertyDunderSet(_)) + | (MethodWrapperKind::StrStartswith(_), MethodWrapperKind::StrStartswith(_)) => { + self == other + } + + ( + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + ) => false, + } + } + + fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool { + match (self, other) { + ( + MethodWrapperKind::FunctionTypeDunderGet(self_function), + MethodWrapperKind::FunctionTypeDunderGet(other_function), + ) => self_function.is_assignable_to(db, other_function), + + ( + MethodWrapperKind::FunctionTypeDunderCall(self_function), + MethodWrapperKind::FunctionTypeDunderCall(other_function), + ) => self_function.is_assignable_to(db, other_function), + + (MethodWrapperKind::PropertyDunderGet(_), MethodWrapperKind::PropertyDunderGet(_)) + | (MethodWrapperKind::PropertyDunderSet(_), MethodWrapperKind::PropertyDunderSet(_)) + | (MethodWrapperKind::StrStartswith(_), MethodWrapperKind::StrStartswith(_)) => { + self == other + } + + ( + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + ) => false, + } + } + + fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + match (self, other) { + ( + MethodWrapperKind::FunctionTypeDunderGet(self_function), + MethodWrapperKind::FunctionTypeDunderGet(other_function), + ) => self_function.is_equivalent_to(db, other_function), + + ( + MethodWrapperKind::FunctionTypeDunderCall(self_function), + MethodWrapperKind::FunctionTypeDunderCall(other_function), + ) => self_function.is_equivalent_to(db, other_function), + + (MethodWrapperKind::PropertyDunderGet(_), MethodWrapperKind::PropertyDunderGet(_)) + | (MethodWrapperKind::PropertyDunderSet(_), MethodWrapperKind::PropertyDunderSet(_)) + | (MethodWrapperKind::StrStartswith(_), MethodWrapperKind::StrStartswith(_)) => { + self == other + } + + ( + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + ) => false, + } + } + + fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool { + match (self, other) { + ( + MethodWrapperKind::FunctionTypeDunderGet(self_function), + MethodWrapperKind::FunctionTypeDunderGet(other_function), + ) => self_function.is_gradual_equivalent_to(db, other_function), + + ( + MethodWrapperKind::FunctionTypeDunderCall(self_function), + MethodWrapperKind::FunctionTypeDunderCall(other_function), + ) => self_function.is_gradual_equivalent_to(db, other_function), + + (MethodWrapperKind::PropertyDunderGet(_), MethodWrapperKind::PropertyDunderGet(_)) + | (MethodWrapperKind::PropertyDunderSet(_), MethodWrapperKind::PropertyDunderSet(_)) + | (MethodWrapperKind::StrStartswith(_), MethodWrapperKind::StrStartswith(_)) => { + self == other + } + + ( + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + MethodWrapperKind::FunctionTypeDunderGet(_) + | MethodWrapperKind::FunctionTypeDunderCall(_) + | MethodWrapperKind::PropertyDunderGet(_) + | MethodWrapperKind::PropertyDunderSet(_) + | MethodWrapperKind::StrStartswith(_), + ) => false, + } + } +} + /// Represents a specific instance of `types.WrapperDescriptorType` #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update)] pub enum WrapperDescriptorKind { diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 990681c8da..7394436fcd 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -1412,7 +1412,7 @@ impl<'db> Binding<'db> { } num_synthetic_args = 0; - for (argument_index, (argument, argument_type)) in argument_types.iter().enumerate() { + for (argument_index, (argument, mut argument_type)) in argument_types.iter().enumerate() { if matches!(argument, Argument::Synthetic) { num_synthetic_args += 1; } @@ -1424,9 +1424,12 @@ impl<'db> Binding<'db> { let parameter = ¶meters[parameter_index]; if let Some(mut expected_ty) = parameter.annotated_type() { if let Some(specialization) = self.specialization { + argument_type = argument_type.apply_specialization(db, specialization); expected_ty = expected_ty.apply_specialization(db, specialization); } if let Some(inherited_specialization) = self.inherited_specialization { + argument_type = + argument_type.apply_specialization(db, inherited_specialization); expected_ty = expected_ty.apply_specialization(db, inherited_specialization); } if !argument_type.is_assignable_to(db, expected_ty) { diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 8faca50825..d0f3e9a61e 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -170,31 +170,15 @@ impl Display for DisplayRepresentation<'_> { Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => { write!( f, - "", + "", function = function.name(self.db), - specialization = if let Some(specialization) = function.specialization(self.db) - { - specialization - .display_short(self.db, TupleSpecialization::No) - .to_string() - } else { - String::new() - }, ) } Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderCall(function)) => { write!( f, - "", + "", function = function.name(self.db), - specialization = if let Some(specialization) = function.specialization(self.db) - { - specialization - .display_short(self.db, TupleSpecialization::No) - .to_string() - } else { - String::new() - }, ) } Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(_)) => { diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 2b9d12cba6..93c3b5bbd0 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -641,32 +641,34 @@ impl<'db> SpecializationBuilder<'db> { } match (formal, actual) { - (Type::TypeVar(typevar), _) => match typevar.bound_or_constraints(self.db) { - Some(TypeVarBoundOrConstraints::UpperBound(bound)) => { - if !actual.is_assignable_to(self.db, bound) { - return Err(SpecializationError::MismatchedBound { + (Type::TypeVar(typevar), ty) | (ty, Type::TypeVar(typevar)) => { + match typevar.bound_or_constraints(self.db) { + Some(TypeVarBoundOrConstraints::UpperBound(bound)) => { + if !ty.is_assignable_to(self.db, bound) { + return Err(SpecializationError::MismatchedBound { + typevar, + argument: ty, + }); + } + self.add_type_mapping(typevar, ty); + } + Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { + for constraint in constraints.iter(self.db) { + if ty.is_assignable_to(self.db, *constraint) { + self.add_type_mapping(typevar, *constraint); + return Ok(()); + } + } + return Err(SpecializationError::MismatchedConstraint { typevar, - argument: actual, + argument: ty, }); } - self.add_type_mapping(typevar, actual); - } - Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { - for constraint in constraints.iter(self.db) { - if actual.is_assignable_to(self.db, *constraint) { - self.add_type_mapping(typevar, *constraint); - return Ok(()); - } + _ => { + self.add_type_mapping(typevar, ty); } - return Err(SpecializationError::MismatchedConstraint { - typevar, - argument: actual, - }); } - _ => { - self.add_type_mapping(typevar, actual); - } - }, + } (Type::Tuple(formal_tuple), Type::Tuple(actual_tuple)) => { let formal_elements = formal_tuple.elements(self.db); diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 192db5d298..41552b85e6 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -1528,7 +1528,7 @@ mod tests { db.write_dedented("/src/a.py", "def f(): ...").unwrap(); let func = get_function_f(&db, "/src/a.py"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); assert!(sig.return_ty.is_none()); assert_params(&sig, &[]); @@ -1551,7 +1551,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.py"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); assert_eq!(sig.return_ty.unwrap().display(&db).to_string(), "bytes"); assert_params( @@ -1602,7 +1602,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.py"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); let [ Parameter { @@ -1638,7 +1638,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.pyi"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); let [ Parameter { @@ -1674,7 +1674,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.py"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); let [ Parameter { @@ -1720,7 +1720,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.pyi"); - let sig = func.internal_signature(&db); + let sig = func.internal_signature(&db, None); let [ Parameter { @@ -1756,7 +1756,7 @@ mod tests { .unwrap(); let func = get_function_f(&db, "/src/a.py"); - let expected_sig = func.internal_signature(&db); + let expected_sig = func.internal_signature(&db, None); // With no decorators, internal and external signature are the same assert_eq!(