mirror of
https://github.com/astral-sh/ruff
synced 2026-01-22 05:51:03 -05:00
[ty] Make signature return and parameter types non-optional (#22425)
## Summary Fixes https://github.com/astral-sh/ty/issues/2363 Fixes https://github.com/astral-sh/ty/issues/2013 And several other bugs with the same root cause. And makes any similar bugs impossible by construction. Previously we distinguished "no annotation" (Rust `None`) from "explicitly annotated with something of type `Unknown`" (which is not an error, and results in the annotation being of Rust type `Some(Type::DynamicType(Unknown))`), even though semantically these should be treated the same. This was a bit of a bug magnet, because it was easy to forget to make this `None` -> `Unknown` translation everywhere we needed to. And in fact we did fail to do it in the case of materializing a callable, leading to a top-materialized callable still having (rust) `None` return type, which should have instead materialized to `object`. This also fixes several other bugs related to not handling un-annotated return types correctly: 1. We previously considered the return type of an unannotated `async def` to be `Unknown`, where it should be `CoroutineType[Any, Any, Unknown]`. 2. We previously failed to infer a ParamSpec if the return type of the callable we are inferring against was not annotated. 3. We previously wrongly returned `Unknown` from `some_dict.get("key", None)` if the value type of `some_dict` included a callable type with un-annotated return type. We now make signature return types and annotated parameter types required, and we eagerly insert `Unknown` if there's no annotation. Most of the diff is just a bunch of mechanical code changes where we construct these types, and simplifications where we use them. One exception is type display: when a callable type has un-annotated parameters, we want to display them as un-annotated, but if it has a parameter explicitly annotated with something of `Unknown` type, we want to display that parameter as `x: Unknown` (it would be confusing if it looked like your annotation just disappeared entirely). Fortunately, we already have a mechanism in place for handling this: the `inferred_annotation` flag, which suppresses display of an annotation. Previously we used it only for `self` and `cls` parameters with an inferred annotated type -- but we now also set it for any un-annotated parameter, for which we infer `Unknown` type. We also need to normalize `inferred_annotation`, since it's display-only and shouldn't impact type equivalence. (This is technically a previously-existing bug, it just never came up when it only affected self types -- now it comes up because we have tests asserting that `def f(x)` and `def g(x: Unknown)` are equivalent.) ## Test Plan Added mdtests.
This commit is contained in:
@@ -420,7 +420,7 @@ impl<'db> Completion<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
fn argument(name: &str, ty: Option<Type<'db>>, documentation: Option<&str>) -> Self {
|
||||
fn argument(name: &str, ty: Type<'db>, documentation: Option<&str>) -> Self {
|
||||
let insert = Some(format!("{name}=").into_boxed_str());
|
||||
let documentation = documentation.map(|d| Docstring::new(d.to_owned()));
|
||||
|
||||
@@ -428,7 +428,7 @@ impl<'db> Completion<'db> {
|
||||
name: name.into(),
|
||||
qualified: None,
|
||||
insert,
|
||||
ty,
|
||||
ty: Some(ty),
|
||||
kind: Some(CompletionKind::Variable),
|
||||
module_name: None,
|
||||
import: None,
|
||||
@@ -1134,7 +1134,7 @@ fn add_class_arg_completions<'db>(
|
||||
};
|
||||
|
||||
if !is_set("metaclass") {
|
||||
let ty = Some(KnownClass::Type.to_subclass_of(model.db()));
|
||||
let ty = KnownClass::Type.to_subclass_of(model.db());
|
||||
completions.add(Completion::argument("metaclass", ty, None));
|
||||
}
|
||||
|
||||
@@ -1148,7 +1148,7 @@ fn add_class_arg_completions<'db>(
|
||||
//
|
||||
// See https://peps.python.org/pep-0728/
|
||||
if is_typed_dict && !is_set("total") {
|
||||
let ty = Some(KnownClass::Bool.to_instance(model.db()));
|
||||
let ty = KnownClass::Bool.to_instance(model.db());
|
||||
completions.add(Completion::argument("total", ty, None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ pub struct ParameterDetails<'db> {
|
||||
pub name: String,
|
||||
/// The parameter label in the signature (e.g., "param1: str")
|
||||
pub label: String,
|
||||
/// The annotated type of the parameter, if any
|
||||
pub ty: Option<Type<'db>>,
|
||||
/// The annotated type of the parameter. If no annotation was provided, this is `Unknown`.
|
||||
pub ty: Type<'db>,
|
||||
/// Documentation specific to the parameter, typically extracted from the
|
||||
/// function's docstring
|
||||
pub documentation: Option<String>,
|
||||
@@ -237,7 +237,7 @@ fn create_parameters_from_offsets<'db>(
|
||||
docstring: Option<&Docstring>,
|
||||
parameter_names: &[String],
|
||||
parameter_kinds: &[ParameterKind],
|
||||
parameter_types: &[Option<Type<'db>>],
|
||||
parameter_types: &[Type<'db>],
|
||||
) -> Vec<ParameterDetails<'db>> {
|
||||
// Extract parameter documentation from the function's docstring if available.
|
||||
let param_docs = if let Some(docstring) = docstring {
|
||||
@@ -264,12 +264,11 @@ fn create_parameters_from_offsets<'db>(
|
||||
parameter_kinds.get(i),
|
||||
Some(ParameterKind::PositionalOnly { .. })
|
||||
);
|
||||
let ty = parameter_types.get(i).copied().flatten();
|
||||
|
||||
ParameterDetails {
|
||||
name: param_name.to_string(),
|
||||
label,
|
||||
ty,
|
||||
ty: parameter_types[i],
|
||||
documentation: param_docs.get(param_name).cloned(),
|
||||
is_positional_only,
|
||||
}
|
||||
|
||||
@@ -117,3 +117,15 @@ def _():
|
||||
result = yield from retrieve().__await__()
|
||||
reveal_type(result) # revealed: int
|
||||
```
|
||||
|
||||
## Un-annotated async functions
|
||||
|
||||
An `async def` with no annotated return type is still known to return `CoroutineType` of `Unknown`,
|
||||
not just `Unknown`:
|
||||
|
||||
```py
|
||||
async def f():
|
||||
pass
|
||||
|
||||
reveal_type(f()) # revealed: CoroutineType[Any, Any, Unknown]
|
||||
```
|
||||
|
||||
@@ -783,3 +783,20 @@ class Container[**P]:
|
||||
# error: [invalid-argument-type] "Argument to bound method `method` is incorrect: Expected `(**P@Container) -> None`, found `(**Q@try_assign) -> None`"
|
||||
return self.method(f)
|
||||
```
|
||||
|
||||
## `ParamSpec` inference with un-annotated return type
|
||||
|
||||
Regression test for an issue where `ParamSpec` inference failed when the callable we were inferring
|
||||
from did not have an annotated return type.
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
|
||||
def infer_paramspec[**P](func: Callable[P, None]) -> Callable[P, None]:
|
||||
return func
|
||||
|
||||
def f(x: int, y: str):
|
||||
pass
|
||||
|
||||
reveal_type(infer_paramspec(f)) # revealed: (x: int, y: str) -> None
|
||||
```
|
||||
|
||||
@@ -736,6 +736,29 @@ static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal[""]]], No
|
||||
static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal["", "a"]]], Not[AlwaysFalsy]))
|
||||
```
|
||||
|
||||
## Callable types with Unknown/missing return type
|
||||
|
||||
See <https://github.com/astral-sh/ty/issues/2363>, a property test failure involving
|
||||
`~type & ~((...) -> Unknown)` not being assignable to `~type`. Since `~type & ~Callable` is a subset
|
||||
of `~type`, the intersection should be assignable to `~type`.
|
||||
|
||||
The root cause was that we failed to properly materialize a `Callable[..., Unknown]` type when the
|
||||
`Unknown` return type originated from a missing annotation.
|
||||
|
||||
```py
|
||||
from ty_extensions import static_assert, is_assignable_to, Intersection, Not, Unknown, CallableTypeOf
|
||||
from typing import Callable
|
||||
|
||||
# `Callable[..., Unknown]` has explicit Unknown return type
|
||||
static_assert(is_assignable_to(Intersection[Not[type], Not[Callable[..., Unknown]]], Not[type]))
|
||||
|
||||
# Function with no return annotation (has implicit Unknown return type internally)
|
||||
def no_return_annotation(*args, **kwargs): ...
|
||||
|
||||
# `CallableTypeOf[no_return_annotation]` has `returns: None` internally (no annotation)
|
||||
static_assert(is_assignable_to(Intersection[Not[type], Not[CallableTypeOf[no_return_annotation]]], Not[type]))
|
||||
```
|
||||
|
||||
## Intersections with non-fully-static negated elements
|
||||
|
||||
A type can be _assignable_ to an intersection containing negated elements only if the _bottom_
|
||||
|
||||
@@ -1687,10 +1687,10 @@ mod implicit_globals {
|
||||
[Parameter::positional_only(Some(Name::new_static("format")))
|
||||
.with_annotated_type(KnownClass::Int.to_instance(db))],
|
||||
),
|
||||
Some(KnownClass::Dict.to_specialized_instance(
|
||||
KnownClass::Dict.to_specialized_instance(
|
||||
db,
|
||||
[KnownClass::Str.to_instance(db), Type::any()],
|
||||
)),
|
||||
),
|
||||
);
|
||||
Place::Defined(
|
||||
DefinedPlace::new(Type::function_like_callable(db, signature))
|
||||
|
||||
@@ -899,9 +899,7 @@ impl ReachabilityConstraints {
|
||||
let (no_overloads_return_never, all_overloads_return_never) = overloads_iterator
|
||||
.fold((true, true), |(none, all), overload| {
|
||||
let overload_returns_never =
|
||||
overload.return_ty.is_some_and(|return_type| {
|
||||
return_type.is_equivalent_to(db, Type::Never)
|
||||
});
|
||||
overload.return_ty.is_equivalent_to(db, Type::Never);
|
||||
|
||||
(
|
||||
none && !overload_returns_never,
|
||||
|
||||
@@ -1925,7 +1925,7 @@ impl<'db> Type<'db> {
|
||||
SubclassOfInner::Dynamic(_) | SubclassOfInner::TypeVar(_) => {
|
||||
Some(CallableTypes::one(CallableType::single(
|
||||
db,
|
||||
Signature::new(Parameters::unknown(), Some(Type::from(subclass_of_ty))),
|
||||
Signature::new(Parameters::unknown(), Type::from(subclass_of_ty)),
|
||||
)))
|
||||
}
|
||||
},
|
||||
@@ -1968,7 +1968,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(None)
|
||||
.with_annotated_type(newtype.base(db).instance_type(db))],
|
||||
),
|
||||
Some(Type::NewTypeInstance(newtype)),
|
||||
Type::NewTypeInstance(newtype),
|
||||
),
|
||||
)))
|
||||
}
|
||||
@@ -3811,7 +3811,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("func")))
|
||||
.with_annotated_type(Type::object())],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
@@ -3836,7 +3836,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||
KnownClass::ConstraintSet.to_instance(db),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
@@ -3851,7 +3851,7 @@ impl<'db> Type<'db> {
|
||||
.type_form()
|
||||
.with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -3878,7 +3878,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(Type::TypeVar(val_ty)),
|
||||
Type::TypeVar(val_ty),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -3897,7 +3897,7 @@ impl<'db> Type<'db> {
|
||||
// errors instead of `type-assertion-failure` errors.
|
||||
.with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -3916,7 +3916,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(Type::any()),
|
||||
Type::any(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
@@ -3932,7 +3932,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("cls")))
|
||||
.with_annotated_type(Type::none(db))],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
// def dataclass(cls: type[_T], /) -> type[_T]: ...
|
||||
Signature::new(
|
||||
@@ -3941,7 +3941,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("cls")))
|
||||
.with_annotated_type(KnownClass::Type.to_instance(db))],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
// TODO: make this overload Python-version-dependent
|
||||
|
||||
@@ -3994,7 +3994,7 @@ impl<'db> Type<'db> {
|
||||
.with_default_type(Type::BooleanLiteral(false)),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -4029,7 +4029,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any())
|
||||
.with_default_type(Type::BooleanLiteral(false))],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -4053,7 +4053,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::object())
|
||||
.with_default_type(Type::string_literal(db, ""))],
|
||||
),
|
||||
Some(KnownClass::Str.to_instance(db)),
|
||||
KnownClass::Str.to_instance(db),
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::new(
|
||||
@@ -4083,7 +4083,7 @@ impl<'db> Type<'db> {
|
||||
.with_default_type(Type::string_literal(db, "strict")),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Str.to_instance(db)),
|
||||
KnownClass::Str.to_instance(db),
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -4110,7 +4110,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("o")))
|
||||
.with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(type_instance),
|
||||
type_instance,
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::new(
|
||||
@@ -4132,7 +4132,7 @@ impl<'db> Type<'db> {
|
||||
),
|
||||
],
|
||||
),
|
||||
Some(type_instance),
|
||||
type_instance,
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -4145,11 +4145,8 @@ impl<'db> Type<'db> {
|
||||
// def __init__(self) -> None: ...
|
||||
// def __new__(cls) -> Self: ...
|
||||
// ```
|
||||
Binding::single(
|
||||
self,
|
||||
Signature::new(Parameters::empty(), Some(Type::object())),
|
||||
)
|
||||
.into()
|
||||
Binding::single(self, Signature::new(Parameters::empty(), Type::object()))
|
||||
.into()
|
||||
}
|
||||
|
||||
Some(KnownClass::Enum) => {
|
||||
@@ -4179,7 +4176,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Super.to_instance(db)),
|
||||
KnownClass::Super.to_instance(db),
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::new(
|
||||
@@ -4187,12 +4184,9 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("t")))
|
||||
.with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(KnownClass::Super.to_instance(db)),
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::empty(),
|
||||
Some(KnownClass::Super.to_instance(db)),
|
||||
KnownClass::Super.to_instance(db),
|
||||
),
|
||||
Signature::new(Parameters::empty(), KnownClass::Super.to_instance(db)),
|
||||
],
|
||||
)
|
||||
.into()
|
||||
@@ -4234,7 +4228,7 @@ impl<'db> Type<'db> {
|
||||
.with_default_type(Type::IntLiteral(1)),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Deprecated.to_instance(db)),
|
||||
KnownClass::Deprecated.to_instance(db),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -4276,7 +4270,7 @@ impl<'db> Type<'db> {
|
||||
.with_default_type(Type::empty_tuple(db)),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -4288,7 +4282,7 @@ impl<'db> Type<'db> {
|
||||
db,
|
||||
[Parameter::positional_only(None).with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(Type::any()),
|
||||
Type::any(),
|
||||
);
|
||||
let setter_signature = Signature::new(
|
||||
Parameters::new(
|
||||
@@ -4298,14 +4292,14 @@ impl<'db> Type<'db> {
|
||||
Parameter::positional_only(None).with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
);
|
||||
let deleter_signature = Signature::new(
|
||||
Parameters::new(
|
||||
db,
|
||||
[Parameter::positional_only(None).with_annotated_type(Type::any())],
|
||||
),
|
||||
Some(Type::any()),
|
||||
Type::any(),
|
||||
);
|
||||
|
||||
Binding::single(
|
||||
@@ -4349,7 +4343,7 @@ impl<'db> Type<'db> {
|
||||
.with_default_type(Type::none(db)),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -4372,7 +4366,7 @@ impl<'db> Type<'db> {
|
||||
CallableBinding::from_overloads(
|
||||
self,
|
||||
[
|
||||
Signature::new(Parameters::empty(), Some(Type::empty_tuple(db))),
|
||||
Signature::new(Parameters::empty(), Type::empty_tuple(db)),
|
||||
Signature::new_generic(
|
||||
Some(GenericContext::from_typevar_instances(db, [element_ty])),
|
||||
Parameters::new(
|
||||
@@ -4387,7 +4381,7 @@ impl<'db> Type<'db> {
|
||||
),
|
||||
)],
|
||||
),
|
||||
Some(Type::homogeneous_tuple(db, Type::TypeVar(element_ty))),
|
||||
Type::homogeneous_tuple(db, Type::TypeVar(element_ty)),
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -4404,7 +4398,7 @@ impl<'db> Type<'db> {
|
||||
Signature::new_generic(
|
||||
class.generic_context(db),
|
||||
Parameters::gradual_form(),
|
||||
self.to_instance(db),
|
||||
self.to_instance(db).unwrap_or(Type::unknown()),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
@@ -4430,7 +4424,7 @@ impl<'db> Type<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
@@ -4445,7 +4439,10 @@ impl<'db> Type<'db> {
|
||||
// TODO check call vs signatures of `__new__` and/or `__init__`
|
||||
Binding::single(
|
||||
self,
|
||||
Signature::new(Parameters::gradual_form(), self.to_instance(db)),
|
||||
Signature::new(
|
||||
Parameters::gradual_form(),
|
||||
self.to_instance(db).unwrap_or(Type::unknown()),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
@@ -4463,7 +4460,10 @@ impl<'db> Type<'db> {
|
||||
// TODO check call vs signatures of `__new__` and/or `__init__`
|
||||
SubclassOfInner::TypeVar(_) => Binding::single(
|
||||
self,
|
||||
Signature::new(Parameters::gradual_form(), self.to_instance(db)),
|
||||
Signature::new(
|
||||
Parameters::gradual_form(),
|
||||
self.to_instance(db).unwrap_or(Type::unknown()),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
},
|
||||
@@ -4530,11 +4530,8 @@ impl<'db> Type<'db> {
|
||||
// Intersect with `Any` for the return type to reflect the fact that the `dataclass()`
|
||||
// decorator adds methods to the class
|
||||
let returns = IntersectionType::from_elements(db, [typevar_meta, Type::any()]);
|
||||
let signature = Signature::new_generic(
|
||||
Some(context),
|
||||
Parameters::new(db, parameters),
|
||||
Some(returns),
|
||||
);
|
||||
let signature =
|
||||
Signature::new_generic(Some(context), Parameters::new(db, parameters), returns);
|
||||
Binding::single(self, signature).into()
|
||||
}
|
||||
|
||||
@@ -4551,7 +4548,7 @@ impl<'db> Type<'db> {
|
||||
[Parameter::positional_only(None)
|
||||
.with_annotated_type(newtype.base(db).instance_type(db))],
|
||||
),
|
||||
Some(Type::NewTypeInstance(newtype)),
|
||||
Type::NewTypeInstance(newtype),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
@@ -10387,7 +10384,7 @@ impl<'db> CallableType<'db> {
|
||||
) -> CallableType<'db> {
|
||||
CallableType::new(
|
||||
db,
|
||||
CallableSignature::single(Signature::new(parameters, None)),
|
||||
CallableSignature::single(Signature::new(parameters, Type::unknown())),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
)
|
||||
}
|
||||
@@ -10990,7 +10987,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_annotated_type(KnownClass::Type.to_instance(db)),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::new(
|
||||
@@ -11006,7 +11003,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_default_type(Type::none(db)),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
]
|
||||
.into_iter(),
|
||||
@@ -11025,7 +11022,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_annotated_type(Type::object()),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
)))
|
||||
}
|
||||
KnownBoundMethodType::StrStartswith(_) => {
|
||||
@@ -11058,7 +11055,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_default_type(Type::none(db)),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11078,7 +11075,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||
KnownClass::ConstraintSet.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11086,7 +11083,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
| KnownBoundMethodType::ConstraintSetNever => {
|
||||
Either::Right(std::iter::once(Signature::new(
|
||||
Parameters::empty(),
|
||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||
KnownClass::ConstraintSet.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11103,7 +11100,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||
KnownClass::ConstraintSet.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11114,7 +11111,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
[Parameter::positional_only(Some(Name::new_static("other")))
|
||||
.with_annotated_type(KnownClass::ConstraintSet.to_instance(db))],
|
||||
),
|
||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||
KnownClass::ConstraintSet.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11130,7 +11127,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
))
|
||||
.with_default_type(Type::none(db))],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -11143,10 +11140,10 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
.with_annotated_type(KnownClass::ConstraintSet.to_instance(db)),
|
||||
],
|
||||
),
|
||||
Some(UnionType::from_elements(
|
||||
UnionType::from_elements(
|
||||
db,
|
||||
[KnownClass::Specialization.to_instance(db), Type::none(db)],
|
||||
)),
|
||||
),
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -11193,7 +11190,7 @@ impl WrapperDescriptorKind {
|
||||
.with_annotated_type(type_instance),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
Signature::new(
|
||||
Parameters::new(
|
||||
@@ -11211,7 +11208,7 @@ impl WrapperDescriptorKind {
|
||||
.with_default_type(none),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
),
|
||||
]
|
||||
}
|
||||
@@ -11237,7 +11234,7 @@ impl WrapperDescriptorKind {
|
||||
.with_annotated_type(object),
|
||||
],
|
||||
),
|
||||
None,
|
||||
Type::unknown(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1747,9 +1747,8 @@ impl<'db> CallableBinding<'db> {
|
||||
let mut is_argument_assignable_to_any_overload = false;
|
||||
'overload: for overload in &self.overloads {
|
||||
for parameter_index in &overload.argument_matches[argument_index].parameters {
|
||||
let parameter_type = overload.signature.parameters()[*parameter_index]
|
||||
.annotated_type()
|
||||
.unwrap_or(Type::unknown());
|
||||
let parameter_type =
|
||||
overload.signature.parameters()[*parameter_index].annotated_type();
|
||||
if argument_type
|
||||
.when_assignable_to(db, parameter_type, overload.inferable_typevars)
|
||||
.is_always_satisfied(db)
|
||||
@@ -1996,9 +1995,8 @@ impl<'db> CallableBinding<'db> {
|
||||
for ¶meter_index in &overload.argument_matches[argument_index].parameters {
|
||||
// TODO: For an unannotated `self` / `cls` parameter, the type should be
|
||||
// `typing.Self` / `type[typing.Self]`
|
||||
let current_parameter_type = overload.signature.parameters()[parameter_index]
|
||||
.annotated_type()
|
||||
.unwrap_or(Type::unknown());
|
||||
let current_parameter_type =
|
||||
overload.signature.parameters()[parameter_index].annotated_type();
|
||||
let first_parameter_type = &mut first_parameter_types[parameter_index];
|
||||
if let Some(first_parameter_type) = first_parameter_type {
|
||||
if !first_parameter_type
|
||||
@@ -2109,9 +2107,8 @@ impl<'db> CallableBinding<'db> {
|
||||
}
|
||||
// TODO: For an unannotated `self` / `cls` parameter, the type should be
|
||||
// `typing.Self` / `type[typing.Self]`
|
||||
let mut parameter_type = overload.signature.parameters()[*parameter_index]
|
||||
.annotated_type()
|
||||
.unwrap_or(Type::unknown());
|
||||
let mut parameter_type =
|
||||
overload.signature.parameters()[*parameter_index].annotated_type();
|
||||
if let Some(specialization) = overload.specialization {
|
||||
parameter_type =
|
||||
parameter_type.apply_specialization(db, specialization);
|
||||
@@ -3011,7 +3008,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
|
||||
let return_with_tcx = self
|
||||
.constructor_instance_type
|
||||
.or(self.signature.return_ty)
|
||||
.or(Some(self.signature.return_ty))
|
||||
.zip(self.call_expression_tcx.annotation);
|
||||
|
||||
self.inferable_typevars = generic_context.inferable_typevars(self.db);
|
||||
@@ -3051,13 +3048,8 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
for (parameter_index, variadic_argument_type) in
|
||||
self.argument_matches[argument_index].iter()
|
||||
{
|
||||
let parameter = ¶meters[parameter_index];
|
||||
let Some(expected_type) = parameter.annotated_type() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let specialization_result = builder.infer_map(
|
||||
expected_type,
|
||||
parameters[parameter_index].annotated_type(),
|
||||
variadic_argument_type.unwrap_or(argument_type),
|
||||
|(identity, variance, inferred_ty)| {
|
||||
// Avoid widening the inferred type if it is already assignable to the
|
||||
@@ -3092,10 +3084,9 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
|
||||
// Attempt to promote any literal types assigned to the specialization.
|
||||
let maybe_promote = |identity, typevar, ty: Type<'db>| {
|
||||
let Some(return_ty) = self.constructor_instance_type.or(self.signature.return_ty)
|
||||
else {
|
||||
return ty;
|
||||
};
|
||||
let return_ty = self
|
||||
.constructor_instance_type
|
||||
.unwrap_or(self.signature.return_ty);
|
||||
|
||||
let mut combined_tcx = TypeContext::default();
|
||||
let mut variance_in_return = TypeVarVariance::Bivariant;
|
||||
@@ -3188,30 +3179,29 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
) {
|
||||
let parameters = self.signature.parameters();
|
||||
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(self.db, specialization);
|
||||
expected_ty = expected_ty.apply_specialization(self.db, specialization);
|
||||
}
|
||||
// This is one of the few places where we want to check if there's _any_ specialization
|
||||
// where assignability holds; normally we want to check that assignability holds for
|
||||
// _all_ specializations.
|
||||
// TODO: Soon we will go further, and build the actual specializations from the
|
||||
// constraint set that we get from this assignability check, instead of inferring and
|
||||
// building them in an earlier separate step.
|
||||
if argument_type
|
||||
.when_assignable_to(self.db, expected_ty, self.inferable_typevars)
|
||||
.is_never_satisfied(self.db)
|
||||
{
|
||||
let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
|
||||
&& !parameter.is_variadic();
|
||||
self.errors.push(BindingError::InvalidArgumentType {
|
||||
parameter: ParameterContext::new(parameter, parameter_index, positional),
|
||||
argument_index: adjusted_argument_index,
|
||||
expected_ty,
|
||||
provided_ty: argument_type,
|
||||
});
|
||||
}
|
||||
let mut expected_ty = parameter.annotated_type();
|
||||
if let Some(specialization) = self.specialization {
|
||||
argument_type = argument_type.apply_specialization(self.db, specialization);
|
||||
expected_ty = expected_ty.apply_specialization(self.db, specialization);
|
||||
}
|
||||
// This is one of the few places where we want to check if there's _any_ specialization
|
||||
// where assignability holds; normally we want to check that assignability holds for
|
||||
// _all_ specializations.
|
||||
// TODO: Soon we will go further, and build the actual specializations from the
|
||||
// constraint set that we get from this assignability check, instead of inferring and
|
||||
// building them in an earlier separate step.
|
||||
if argument_type
|
||||
.when_assignable_to(self.db, expected_ty, self.inferable_typevars)
|
||||
.is_never_satisfied(self.db)
|
||||
{
|
||||
let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
|
||||
&& !parameter.is_variadic();
|
||||
self.errors.push(BindingError::InvalidArgumentType {
|
||||
parameter: ParameterContext::new(parameter, parameter_index, positional),
|
||||
argument_index: adjusted_argument_index,
|
||||
expected_ty,
|
||||
provided_ty: argument_type,
|
||||
});
|
||||
}
|
||||
// We still update the actual type of the parameter in this binding to match the
|
||||
// argument, even if the argument type is not assignable to the expected parameter
|
||||
@@ -3328,10 +3318,11 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !self.signature.parameters()[*parameter_index]
|
||||
.annotated_type()
|
||||
.is_some_and(|ty| matches!(ty, Type::TypeVar(typevar) if typevar.is_paramspec(self.db)))
|
||||
{
|
||||
let Type::TypeVar(typevar) = self.signature.parameters()[*parameter_index].annotated_type()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if !typevar.is_paramspec(self.db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3692,7 +3683,7 @@ impl<'db> Binding<'db> {
|
||||
for (keywords_index, keywords_type) in keywords_arguments {
|
||||
matcher.match_keyword_variadic(db, keywords_index, keywords_type);
|
||||
}
|
||||
self.return_ty = self.signature.return_ty.unwrap_or(Type::unknown());
|
||||
self.return_ty = self.signature.return_ty;
|
||||
self.parameter_tys = vec![None; parameters.len()].into_boxed_slice();
|
||||
self.variadic_argument_matched_to_variadic_parameter =
|
||||
matcher.variadic_argument_matched_to_variadic_parameter;
|
||||
@@ -4723,9 +4714,9 @@ fn asynccontextmanager_return_type<'db>(db: &'db dyn Db, func_ty: Type<'db>) ->
|
||||
.exactly_one()
|
||||
.ok()?;
|
||||
let signature = &binding.signature;
|
||||
let return_ty = signature.return_ty?;
|
||||
|
||||
let yield_ty = return_ty
|
||||
let yield_ty = signature
|
||||
.return_ty
|
||||
.try_iterate_with_mode(db, EvaluationMode::Async)
|
||||
.ok()?
|
||||
.homogeneous_element_type(db);
|
||||
@@ -4741,7 +4732,7 @@ fn asynccontextmanager_return_type<'db>(db: &'db dyn Db, func_ty: Type<'db>) ->
|
||||
});
|
||||
|
||||
let new_return_ty = Type::from(context_manager).to_instance(db)?;
|
||||
let new_signature = Signature::new(signature.parameters().clone(), Some(new_return_ty));
|
||||
let new_signature = Signature::new(signature.parameters().clone(), new_return_ty);
|
||||
|
||||
Some(Type::Callable(CallableType::new(
|
||||
db,
|
||||
|
||||
@@ -858,7 +858,7 @@ impl<'db> ClassType<'db> {
|
||||
let index_parameter = Parameter::positional_only(Some(Name::new_static("index")))
|
||||
.with_annotated_type(index_annotation);
|
||||
let parameters = Parameters::new(db, [self_parameter, index_parameter]);
|
||||
Signature::new(parameters, Some(return_annotation))
|
||||
Signature::new(parameters, return_annotation)
|
||||
}
|
||||
|
||||
let (class_literal, specialization) = self.class_literal(db);
|
||||
@@ -895,7 +895,7 @@ impl<'db> ClassType<'db> {
|
||||
);
|
||||
|
||||
let synthesized_dunder_method =
|
||||
Type::function_like_callable(db, Signature::new(parameters, Some(return_type)));
|
||||
Type::function_like_callable(db, Signature::new(parameters, return_type));
|
||||
|
||||
Member::definitely_declared(synthesized_dunder_method)
|
||||
}
|
||||
@@ -1133,7 +1133,7 @@ impl<'db> ClassType<'db> {
|
||||
|
||||
let synthesized_dunder = Type::function_like_callable(
|
||||
db,
|
||||
Signature::new_generic(inherited_generic_context, parameters, None),
|
||||
Signature::new_generic(inherited_generic_context, parameters, Type::unknown()),
|
||||
);
|
||||
|
||||
Member::definitely_declared(synthesized_dunder)
|
||||
@@ -1214,14 +1214,12 @@ impl<'db> ClassType<'db> {
|
||||
// Step 3: If the return type of the `__new__` evaluates to a type that is not a subclass of this class,
|
||||
// then we should ignore the `__init__` and just return the `__new__` method.
|
||||
let returns_non_subclass = dunder_new_signature.overloads.iter().any(|signature| {
|
||||
signature.return_ty.is_some_and(|return_ty| {
|
||||
!return_ty.is_assignable_to(
|
||||
db,
|
||||
self_ty
|
||||
.to_instance(db)
|
||||
.expect("ClassType should be instantiable"),
|
||||
)
|
||||
})
|
||||
!signature.return_ty.is_assignable_to(
|
||||
db,
|
||||
self_ty
|
||||
.to_instance(db)
|
||||
.expect("ClassType should be instantiable"),
|
||||
)
|
||||
});
|
||||
|
||||
let instance_ty = Type::instance(db, self);
|
||||
@@ -1270,7 +1268,7 @@ impl<'db> ClassType<'db> {
|
||||
.parameters()
|
||||
.get_positional(0)
|
||||
.filter(|parameter| !parameter.inferred_annotation)
|
||||
.and_then(Parameter::annotated_type)
|
||||
.map(Parameter::annotated_type)
|
||||
.filter(|ty| {
|
||||
ty.as_typevar()
|
||||
.is_none_or(|bound_typevar| !bound_typevar.typevar(db).is_self(db))
|
||||
@@ -1285,7 +1283,7 @@ impl<'db> ClassType<'db> {
|
||||
Signature::new_generic(
|
||||
generic_context,
|
||||
signature.parameters().clone(),
|
||||
Some(return_type),
|
||||
return_type,
|
||||
)
|
||||
.with_definition(signature.definition())
|
||||
.bind_self(db, Some(instance_ty))
|
||||
@@ -1349,7 +1347,7 @@ impl<'db> ClassType<'db> {
|
||||
Signature::new_generic(
|
||||
class_generic_context,
|
||||
Parameters::empty(),
|
||||
Some(correct_return_type),
|
||||
correct_return_type,
|
||||
),
|
||||
))
|
||||
}
|
||||
@@ -2335,7 +2333,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
db,
|
||||
[Parameter::positional_only(Some(Name::new_static("self")))],
|
||||
),
|
||||
Some(field.declared_ty),
|
||||
field.declared_ty,
|
||||
);
|
||||
let property_getter = Type::single_callable(db, property_getter_signature);
|
||||
let property = PropertyInstanceType::new(db, Some(property_getter), None);
|
||||
@@ -2432,7 +2430,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(instance_ty),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
);
|
||||
|
||||
return Some(Type::function_like_callable(db, signature));
|
||||
@@ -2491,7 +2489,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
let instance_ty =
|
||||
Type::instance(db, self.apply_optional_specialization(db, specialization));
|
||||
|
||||
let signature_from_fields = |mut parameters: Vec<_>, return_ty: Option<Type<'db>>| {
|
||||
let signature_from_fields = |mut parameters: Vec<_>, return_ty: Type<'db>| {
|
||||
for (field_name, field) in self.fields(db, specialization, field_policy) {
|
||||
let (init, mut default_ty, kw_only, alias) = match &field.kind {
|
||||
FieldKind::NamedTuple { default_ty } => (true, *default_ty, None, None),
|
||||
@@ -2544,9 +2542,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
if let Some(value_param) =
|
||||
overload.signature.parameters().get_positional(2)
|
||||
{
|
||||
value_types = value_types.add(
|
||||
value_param.annotated_type().unwrap_or_else(Type::unknown),
|
||||
);
|
||||
value_types = value_types.add(value_param.annotated_type());
|
||||
} else if overload.signature.parameters().is_gradual() {
|
||||
value_types = value_types.add(Type::unknown());
|
||||
}
|
||||
@@ -2618,12 +2614,12 @@ impl<'db> ClassLiteral<'db> {
|
||||
let self_parameter = Parameter::positional_or_keyword(Name::new_static("self"))
|
||||
// TODO: could be `Self`.
|
||||
.with_annotated_type(instance_ty);
|
||||
signature_from_fields(vec![self_parameter], Some(Type::none(db)))
|
||||
signature_from_fields(vec![self_parameter], Type::none(db))
|
||||
}
|
||||
(CodeGeneratorKind::NamedTuple, "__new__") => {
|
||||
let cls_parameter = Parameter::positional_or_keyword(Name::new_static("cls"))
|
||||
.with_annotated_type(KnownClass::Type.to_instance(db));
|
||||
signature_from_fields(vec![cls_parameter], Some(Type::none(db)))
|
||||
signature_from_fields(vec![cls_parameter], Type::none(db))
|
||||
}
|
||||
(CodeGeneratorKind::NamedTuple, "_replace" | "__replace__") => {
|
||||
if name == "__replace__"
|
||||
@@ -2642,7 +2638,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
));
|
||||
let self_parameter = Parameter::positional_or_keyword(Name::new_static("self"))
|
||||
.with_annotated_type(self_ty);
|
||||
signature_from_fields(vec![self_parameter], Some(self_ty))
|
||||
signature_from_fields(vec![self_parameter], self_ty)
|
||||
}
|
||||
(CodeGeneratorKind::NamedTuple, "_fields") => {
|
||||
// Synthesize a precise tuple type for _fields using literal string types.
|
||||
@@ -2669,7 +2665,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(instance_ty),
|
||||
],
|
||||
),
|
||||
Some(KnownClass::Bool.to_instance(db)),
|
||||
KnownClass::Bool.to_instance(db),
|
||||
);
|
||||
|
||||
Some(Type::function_like_callable(db, signature))
|
||||
@@ -2686,7 +2682,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
[Parameter::positional_or_keyword(Name::new_static("self"))
|
||||
.with_annotated_type(instance_ty)],
|
||||
),
|
||||
Some(KnownClass::Int.to_instance(db)),
|
||||
KnownClass::Int.to_instance(db),
|
||||
);
|
||||
|
||||
Some(Type::function_like_callable(db, signature))
|
||||
@@ -2762,7 +2758,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
let self_parameter = Parameter::positional_or_keyword(Name::new_static("self"))
|
||||
.with_annotated_type(instance_ty);
|
||||
|
||||
signature_from_fields(vec![self_parameter], Some(instance_ty))
|
||||
signature_from_fields(vec![self_parameter], instance_ty)
|
||||
}
|
||||
(CodeGeneratorKind::DataclassLike(_), "__setattr__") => {
|
||||
if has_dataclass_param(DataclassFlags::FROZEN) {
|
||||
@@ -2776,7 +2772,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
Parameter::positional_or_keyword(Name::new_static("value")),
|
||||
],
|
||||
),
|
||||
Some(Type::Never),
|
||||
Type::Never,
|
||||
);
|
||||
|
||||
return Some(Type::function_like_callable(db, signature));
|
||||
@@ -2821,7 +2817,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(Type::any()),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
)),
|
||||
CallableTypeKind::FunctionLike,
|
||||
)));
|
||||
@@ -2842,7 +2838,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(field.declared_ty),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -2869,7 +2865,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(key_type),
|
||||
],
|
||||
),
|
||||
Some(field.declared_ty),
|
||||
field.declared_ty,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -2905,7 +2901,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(Type::Never),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
)),
|
||||
CallableTypeKind::FunctionLike,
|
||||
)));
|
||||
@@ -2925,7 +2921,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(key_type),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -2959,11 +2955,11 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(key_type),
|
||||
],
|
||||
),
|
||||
Some(if field.is_required() {
|
||||
if field.is_required() {
|
||||
field.declared_ty
|
||||
} else {
|
||||
UnionType::from_elements(db, [field.declared_ty, Type::none(db)])
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
let t_default = BoundTypeVarInstance::synthetic(
|
||||
@@ -2985,14 +2981,14 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(Type::TypeVar(t_default)),
|
||||
],
|
||||
),
|
||||
Some(if field.is_required() {
|
||||
if field.is_required() {
|
||||
field.declared_ty
|
||||
} else {
|
||||
UnionType::from_elements(
|
||||
db,
|
||||
[field.declared_ty, Type::TypeVar(t_default)],
|
||||
)
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
[get_sig, get_with_default_sig]
|
||||
@@ -3009,10 +3005,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(KnownClass::Str.to_instance(db)),
|
||||
],
|
||||
),
|
||||
Some(UnionType::from_elements(
|
||||
db,
|
||||
[Type::unknown(), Type::none(db)],
|
||||
)),
|
||||
UnionType::from_elements(db, [Type::unknown(), Type::none(db)]),
|
||||
)
|
||||
}))
|
||||
.chain(std::iter::once({
|
||||
@@ -3035,10 +3028,10 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(Type::TypeVar(t_default)),
|
||||
],
|
||||
),
|
||||
Some(UnionType::from_elements(
|
||||
UnionType::from_elements(
|
||||
db,
|
||||
[Type::unknown(), Type::TypeVar(t_default)],
|
||||
)),
|
||||
),
|
||||
)
|
||||
}));
|
||||
|
||||
@@ -3072,7 +3065,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(key_type),
|
||||
],
|
||||
),
|
||||
Some(field.declared_ty),
|
||||
field.declared_ty,
|
||||
);
|
||||
|
||||
// `.pop()` with a default value
|
||||
@@ -3095,10 +3088,10 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(Type::TypeVar(t_default)),
|
||||
],
|
||||
),
|
||||
Some(UnionType::from_elements(
|
||||
UnionType::from_elements(
|
||||
db,
|
||||
[field.declared_ty, Type::TypeVar(t_default)],
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
[pop_sig, pop_with_default_sig]
|
||||
@@ -3128,7 +3121,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
.with_annotated_type(field.declared_ty),
|
||||
],
|
||||
),
|
||||
Some(field.declared_ty),
|
||||
field.declared_ty,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -3150,7 +3143,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
Parameter::keyword_variadic(Name::new_static("kwargs")),
|
||||
],
|
||||
),
|
||||
Some(Type::none(db)),
|
||||
Type::none(db),
|
||||
);
|
||||
|
||||
Some(Type::function_like_callable(db, signature))
|
||||
|
||||
@@ -1680,7 +1680,7 @@ impl<'db> Signature<'db> {
|
||||
pub(crate) struct DisplaySignature<'a, 'db> {
|
||||
definition: Option<Definition<'db>>,
|
||||
parameters: &'a Parameters<'db>,
|
||||
return_ty: Option<Type<'db>>,
|
||||
return_ty: Type<'db>,
|
||||
db: &'db dyn Db,
|
||||
settings: DisplaySettings<'db>,
|
||||
}
|
||||
@@ -1727,9 +1727,8 @@ impl<'db> FmtDetailed<'db> for DisplaySignature<'_, 'db> {
|
||||
.fmt_detailed(&mut f)?;
|
||||
|
||||
// Return type
|
||||
let return_ty = self.return_ty.unwrap_or_else(Type::unknown);
|
||||
f.write_str(" -> ")?;
|
||||
return_ty
|
||||
self.return_ty
|
||||
.display_with(self.db, self.settings.singleline())
|
||||
.fmt_detailed(&mut f)?;
|
||||
|
||||
@@ -1897,17 +1896,16 @@ impl<'db> FmtDetailed<'db> for DisplayParameter<'_, 'db> {
|
||||
fn fmt_detailed(&self, f: &mut TypeWriter<'_, '_, 'db>) -> fmt::Result {
|
||||
if let Some(name) = self.param.display_name() {
|
||||
f.write_str(&name)?;
|
||||
if let Some(annotated_type) = self.param.annotated_type() {
|
||||
if self.param.should_annotation_be_displayed() {
|
||||
f.write_str(": ")?;
|
||||
annotated_type
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f)?;
|
||||
}
|
||||
if self.param.should_annotation_be_displayed() {
|
||||
let annotated_type = self.param.annotated_type();
|
||||
f.write_str(": ")?;
|
||||
annotated_type
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f)?;
|
||||
}
|
||||
// Default value can only be specified if `name` is given.
|
||||
if let Some(default_type) = self.param.default_type() {
|
||||
if self.param.annotated_type().is_some() {
|
||||
if self.param.should_annotation_be_displayed() {
|
||||
f.write_str(" = ")?;
|
||||
} else {
|
||||
f.write_str("=")?;
|
||||
@@ -1940,10 +1938,13 @@ impl<'db> FmtDetailed<'db> for DisplayParameter<'_, 'db> {
|
||||
_ => f.write_str("...")?,
|
||||
}
|
||||
}
|
||||
} else if let Some(ty) = self.param.annotated_type() {
|
||||
} else {
|
||||
// This case is specifically for the `Callable` signature where name and default value
|
||||
// cannot be provided.
|
||||
ty.display_with(self.db, self.settings.clone())
|
||||
// cannot be provided. For unnamed parameters we always display the type, to ensure we
|
||||
// have something visible in the parameter slot.
|
||||
self.param
|
||||
.annotated_type()
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -2646,9 +2647,12 @@ mod tests {
|
||||
parameters: impl IntoIterator<Item = Parameter<'db>>,
|
||||
return_ty: Option<Type<'db>>,
|
||||
) -> String {
|
||||
Signature::new(Parameters::new(db, parameters), return_ty)
|
||||
.display(db)
|
||||
.to_string()
|
||||
Signature::new(
|
||||
Parameters::new(db, parameters),
|
||||
return_ty.unwrap_or(Type::unknown()),
|
||||
)
|
||||
.display(db)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn display_signature_multiline<'db>(
|
||||
@@ -2656,9 +2660,12 @@ mod tests {
|
||||
parameters: impl IntoIterator<Item = Parameter<'db>>,
|
||||
return_ty: Option<Type<'db>>,
|
||||
) -> String {
|
||||
Signature::new(Parameters::new(db, parameters), return_ty)
|
||||
.display_with(db, super::DisplaySettings::default().multiline())
|
||||
.to_string()
|
||||
Signature::new(
|
||||
Parameters::new(db, parameters),
|
||||
return_ty.unwrap_or(Type::unknown()),
|
||||
)
|
||||
.display_with(db, super::DisplaySettings::default().multiline())
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -416,21 +416,19 @@ impl<'db> GenericContext<'db> {
|
||||
db: &'db dyn Db,
|
||||
definition: Definition<'db>,
|
||||
parameters: &Parameters<'db>,
|
||||
return_type: Option<Type<'db>>,
|
||||
return_type: Type<'db>,
|
||||
) -> Option<Self> {
|
||||
// Find all of the legacy typevars mentioned in the function signature.
|
||||
let mut variables = FxOrderSet::default();
|
||||
for param in parameters {
|
||||
if let Some(ty) = param.annotated_type() {
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
param
|
||||
.annotated_type()
|
||||
.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
if let Some(ty) = param.default_type() {
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
}
|
||||
if let Some(ty) = return_type {
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
return_type.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
|
||||
if variables.is_empty() {
|
||||
return None;
|
||||
|
||||
@@ -456,8 +456,8 @@ pub struct CallSignatureDetails<'db> {
|
||||
/// Parameter kinds, useful to determine correct autocomplete suggestions.
|
||||
pub parameter_kinds: Vec<ParameterKind<'db>>,
|
||||
|
||||
/// Parameter kinds, useful to determine correct autocomplete suggestions.
|
||||
pub parameter_types: Vec<Option<Type<'db>>>,
|
||||
/// Annotated types of parameters. If no annotation was provided, this is `Unknown`.
|
||||
pub parameter_types: Vec<Type<'db>>,
|
||||
|
||||
/// The definition where this callable was originally defined (useful for
|
||||
/// extracting docstrings).
|
||||
@@ -521,12 +521,11 @@ pub fn call_signature_details<'db>(
|
||||
let display_details = signature.display(model.db()).to_string_parts();
|
||||
let parameter_label_offsets = display_details.parameter_ranges;
|
||||
let parameter_names = display_details.parameter_names;
|
||||
let (parameter_kinds, parameter_types): (Vec<ParameterKind>, Vec<Option<Type>>) =
|
||||
signature
|
||||
.parameters()
|
||||
.iter()
|
||||
.map(|param| (param.kind().clone(), param.annotated_type()))
|
||||
.unzip();
|
||||
let (parameter_kinds, parameter_types): (Vec<ParameterKind>, Vec<Type>) = signature
|
||||
.parameters()
|
||||
.iter()
|
||||
.map(|param| (param.kind().clone(), param.annotated_type()))
|
||||
.unzip();
|
||||
|
||||
CallSignatureDetails {
|
||||
definition: signature.definition(),
|
||||
|
||||
@@ -7068,7 +7068,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// the expected type passed should be the "raw" type,
|
||||
// i.e. type variables in the return type are non-inferable,
|
||||
// and the return types of async functions are not wrapped in `CoroutineType[...]`.
|
||||
TypeContext::new(func.last_definition_raw_signature(self.db()).return_ty)
|
||||
TypeContext::new(Some(
|
||||
func.last_definition_raw_signature(self.db()).return_ty,
|
||||
))
|
||||
})
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
@@ -7483,18 +7485,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
};
|
||||
|
||||
let mut parameter_type =
|
||||
overload.signature.parameters()[*parameter_index].annotated_type()?;
|
||||
overload.signature.parameters()[*parameter_index].annotated_type();
|
||||
|
||||
// If this is a generic call, attempt to specialize the parameter type using the
|
||||
// declared type context, if provided.
|
||||
if let Some(generic_context) = overload.signature.generic_context
|
||||
&& let Some(return_ty) = overload.signature.return_ty
|
||||
&& let Some(declared_return_ty) = call_expression_tcx.annotation
|
||||
{
|
||||
let mut builder =
|
||||
SpecializationBuilder::new(db, generic_context.inferable_typevars(db));
|
||||
|
||||
let _ = builder.infer(return_ty, declared_return_ty);
|
||||
let _ = builder.infer(overload.signature.return_ty, declared_return_ty);
|
||||
let specialization = builder.build(generic_context);
|
||||
|
||||
parameter_type = parameter_type.apply_specialization(db, specialization);
|
||||
@@ -8771,7 +8772,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// TODO: Useful inference of a lambda's return type will require a different approach,
|
||||
// which does the inference of the body expression based on arguments at each call site,
|
||||
// rather than eagerly computing a return type without knowing the argument types.
|
||||
Type::function_like_callable(self.db(), Signature::new(parameters, Some(Type::unknown())))
|
||||
Type::function_like_callable(self.db(), Signature::new(parameters, Type::unknown()))
|
||||
}
|
||||
|
||||
fn infer_call_expression(
|
||||
|
||||
@@ -1176,7 +1176,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
let callable_type = if let (Some(parameters), Some(return_type), true) =
|
||||
(parameters, return_type, correct_argument_number)
|
||||
{
|
||||
Type::single_callable(db, Signature::new(parameters, Some(return_type)))
|
||||
Type::single_callable(db, Signature::new(parameters, return_type))
|
||||
} else {
|
||||
Type::Callable(CallableType::unknown(db))
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ pub(crate) enum Ty {
|
||||
},
|
||||
Callable {
|
||||
params: CallableParams,
|
||||
returns: Option<Box<Ty>>,
|
||||
returns: Box<Ty>,
|
||||
},
|
||||
/// `unittest.mock.Mock` is interesting because it is a nominal instance type
|
||||
/// where the class has `Any` in its MRO
|
||||
@@ -91,9 +91,7 @@ impl CallableParams {
|
||||
Parameter::keyword_variadic(param.name.unwrap())
|
||||
}
|
||||
};
|
||||
if let Some(annotated_ty) = param.annotated_ty {
|
||||
parameter = parameter.with_annotated_type(annotated_ty.into_type(db));
|
||||
}
|
||||
parameter = parameter.with_annotated_type(param.annotated_ty.into_type(db));
|
||||
if let Some(default_ty) = param.default_ty {
|
||||
parameter = parameter.with_default_type(default_ty.into_type(db));
|
||||
}
|
||||
@@ -108,7 +106,7 @@ impl CallableParams {
|
||||
pub(crate) struct Param {
|
||||
kind: ParamKind,
|
||||
name: Option<Name>,
|
||||
annotated_ty: Option<Ty>,
|
||||
annotated_ty: Ty,
|
||||
default_ty: Option<Ty>,
|
||||
}
|
||||
|
||||
@@ -237,10 +235,7 @@ impl Ty {
|
||||
}
|
||||
Ty::Callable { params, returns } => Type::single_callable(
|
||||
db,
|
||||
Signature::new(
|
||||
params.into_parameters(db),
|
||||
returns.map(|ty| ty.into_type(db)),
|
||||
),
|
||||
Signature::new(params.into_parameters(db), returns.into_type(db)),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -374,7 +369,7 @@ fn arbitrary_type(g: &mut Gen, size: u32, fully_static: bool) -> Ty {
|
||||
0 if !fully_static => CallableParams::GradualForm,
|
||||
_ => CallableParams::List(arbitrary_parameter_list(g, size, fully_static)),
|
||||
},
|
||||
returns: arbitrary_annotation(g, size - 1, fully_static).map(Box::new),
|
||||
returns: Box::new(arbitrary_type(g, size - 1, fully_static)),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -433,7 +428,7 @@ fn arbitrary_parameter_list(g: &mut Gen, size: u32, fully_static: bool) -> Vec<P
|
||||
params.push(Param {
|
||||
kind: next_kind,
|
||||
name,
|
||||
annotated_ty: arbitrary_annotation(g, size, fully_static),
|
||||
annotated_ty: arbitrary_type(g, size, fully_static),
|
||||
default_ty: if matches!(next_kind, ParamKind::Variadic | ParamKind::KeywordVariadic) {
|
||||
None
|
||||
} else {
|
||||
@@ -445,15 +440,6 @@ fn arbitrary_parameter_list(g: &mut Gen, size: u32, fully_static: bool) -> Vec<P
|
||||
params
|
||||
}
|
||||
|
||||
/// An arbitrary optional type, always `Some` if fully static.
|
||||
fn arbitrary_annotation(g: &mut Gen, size: u32, fully_static: bool) -> Option<Ty> {
|
||||
if fully_static {
|
||||
Some(arbitrary_type(g, size, true))
|
||||
} else {
|
||||
arbitrary_optional_type(g, size, false)
|
||||
}
|
||||
}
|
||||
|
||||
fn arbitrary_optional_type(g: &mut Gen, size: u32, fully_static: bool) -> Option<Ty> {
|
||||
match u32::arbitrary(g) % 2 {
|
||||
0 => None,
|
||||
|
||||
@@ -217,7 +217,7 @@ impl<'db> ProtocolInterface<'db> {
|
||||
db,
|
||||
[Parameter::positional_only(Some(Name::new_static("self")))],
|
||||
),
|
||||
Some(ty.normalized(db)),
|
||||
ty.normalized(db),
|
||||
);
|
||||
let property_getter = Type::single_callable(db, property_getter_signature);
|
||||
let property = PropertyInstanceType::new(db, Some(property_getter), None);
|
||||
|
||||
@@ -18,9 +18,7 @@ use smallvec::{SmallVec, smallvec_inline};
|
||||
|
||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type, semantic_index};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::constraints::{
|
||||
ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension,
|
||||
};
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_context};
|
||||
use crate::types::infer::{infer_deferred_types, infer_scope_types};
|
||||
use crate::types::relation::{
|
||||
@@ -177,9 +175,12 @@ impl<'db> CallableSignature<'db> {
|
||||
)),
|
||||
]),
|
||||
),
|
||||
return_ty: self_signature
|
||||
.return_ty
|
||||
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
|
||||
return_ty: self_signature.return_ty.apply_type_mapping_impl(
|
||||
db,
|
||||
type_mapping,
|
||||
tcx,
|
||||
visitor,
|
||||
),
|
||||
}))
|
||||
}
|
||||
Type::Callable(callable)
|
||||
@@ -209,9 +210,12 @@ impl<'db> CallableSignature<'db> {
|
||||
.chain(signature.parameters().iter().cloned()),
|
||||
)
|
||||
},
|
||||
return_ty: self_signature.return_ty.map(|ty| {
|
||||
ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
}),
|
||||
return_ty: self_signature.return_ty.apply_type_mapping_impl(
|
||||
db,
|
||||
type_mapping,
|
||||
tcx,
|
||||
visitor,
|
||||
),
|
||||
}),
|
||||
))
|
||||
}
|
||||
@@ -327,9 +331,7 @@ impl<'db> CallableSignature<'db> {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn is_single_paramspec(
|
||||
&self,
|
||||
) -> Option<(BoundTypeVarInstance<'db>, Option<Type<'db>>)> {
|
||||
pub(crate) fn is_single_paramspec(&self) -> Option<(BoundTypeVarInstance<'db>, Type<'db>)> {
|
||||
Self::signatures_is_single_paramspec(&self.overloads)
|
||||
}
|
||||
|
||||
@@ -338,7 +340,7 @@ impl<'db> CallableSignature<'db> {
|
||||
/// along with the return type of the signature.
|
||||
fn signatures_is_single_paramspec(
|
||||
signatures: &[Signature<'db>],
|
||||
) -> Option<(BoundTypeVarInstance<'db>, Option<Type<'db>>)> {
|
||||
) -> Option<(BoundTypeVarInstance<'db>, Type<'db>)> {
|
||||
// TODO: This might need updating once we support `Concatenate`
|
||||
let [signature] = signatures else {
|
||||
return None;
|
||||
@@ -396,8 +398,37 @@ impl<'db> CallableSignature<'db> {
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
);
|
||||
let return_types_match = self_return_type.zip(other_return_type).when_some_and(
|
||||
|(self_return_type, other_return_type)| {
|
||||
let return_types_match = self_return_type.has_relation_to_impl(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
);
|
||||
return param_spec_matches.and(db, || return_types_match);
|
||||
}
|
||||
|
||||
(Some((self_bound_typevar, self_return_type)), None) => {
|
||||
let upper = Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(other_signatures.iter().map(
|
||||
|signature| {
|
||||
Signature::new(signature.parameters().clone(), Type::unknown())
|
||||
},
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::Never,
|
||||
upper,
|
||||
);
|
||||
let return_types_match = other_signatures
|
||||
.iter()
|
||||
.map(|signature| signature.return_ty)
|
||||
.when_any(db, |other_return_type| {
|
||||
self_return_type.has_relation_to_impl(
|
||||
db,
|
||||
other_return_type,
|
||||
@@ -406,74 +437,39 @@ impl<'db> CallableSignature<'db> {
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
},
|
||||
);
|
||||
return param_spec_matches.and(db, || return_types_match);
|
||||
}
|
||||
|
||||
(Some((self_bound_typevar, self_return_type)), None) => {
|
||||
let upper =
|
||||
Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(other_signatures.iter().map(
|
||||
|signature| Signature::new(signature.parameters().clone(), None),
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::Never,
|
||||
upper,
|
||||
);
|
||||
let return_types_match = self_return_type.when_some_and(|self_return_type| {
|
||||
other_signatures
|
||||
.iter()
|
||||
.filter_map(|signature| signature.return_ty)
|
||||
.when_any(db, |other_return_type| {
|
||||
self_return_type.has_relation_to_impl(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
});
|
||||
});
|
||||
return param_spec_matches.and(db, || return_types_match);
|
||||
}
|
||||
|
||||
(None, Some((other_bound_typevar, other_return_type))) => {
|
||||
let lower =
|
||||
Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(self_signatures.iter().map(
|
||||
|signature| Signature::new(signature.parameters().clone(), None),
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let lower = Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(self_signatures.iter().map(
|
||||
|signature| {
|
||||
Signature::new(signature.parameters().clone(), Type::unknown())
|
||||
},
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
other_bound_typevar,
|
||||
lower,
|
||||
Type::object(),
|
||||
);
|
||||
let return_types_match = other_return_type.when_some_and(|other_return_type| {
|
||||
self_signatures
|
||||
.iter()
|
||||
.filter_map(|signature| signature.return_ty)
|
||||
.when_any(db, |self_return_type| {
|
||||
self_return_type.has_relation_to_impl(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
});
|
||||
let return_types_match = self_signatures
|
||||
.iter()
|
||||
.map(|signature| signature.return_ty)
|
||||
.when_any(db, |self_return_type| {
|
||||
self_return_type.has_relation_to_impl(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
});
|
||||
return param_spec_matches.and(db, || return_types_match);
|
||||
}
|
||||
|
||||
@@ -601,8 +597,8 @@ pub struct Signature<'db> {
|
||||
/// We may get invalid signatures, though, and need to handle them without panicking.
|
||||
parameters: Parameters<'db>,
|
||||
|
||||
/// Annotated return type, if any.
|
||||
pub(crate) return_ty: Option<Type<'db>>,
|
||||
/// Return type. If no annotation was provided, this is `Unknown`.
|
||||
pub(crate) return_ty: Type<'db>,
|
||||
}
|
||||
|
||||
pub(super) fn walk_signature<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
||||
@@ -616,17 +612,13 @@ pub(super) fn walk_signature<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
||||
// By default we usually don't visit the type of the default value,
|
||||
// as it isn't relevant to most things
|
||||
for parameter in &signature.parameters {
|
||||
if let Some(ty) = parameter.annotated_type() {
|
||||
visitor.visit_type(db, ty);
|
||||
}
|
||||
}
|
||||
if let Some(return_ty) = &signature.return_ty {
|
||||
visitor.visit_type(db, *return_ty);
|
||||
visitor.visit_type(db, parameter.annotated_type());
|
||||
}
|
||||
visitor.visit_type(db, signature.return_ty);
|
||||
}
|
||||
|
||||
impl<'db> Signature<'db> {
|
||||
pub(crate) fn new(parameters: Parameters<'db>, return_ty: Option<Type<'db>>) -> Self {
|
||||
pub(crate) fn new(parameters: Parameters<'db>, return_ty: Type<'db>) -> Self {
|
||||
Self {
|
||||
generic_context: None,
|
||||
definition: None,
|
||||
@@ -638,7 +630,7 @@ impl<'db> Signature<'db> {
|
||||
pub(crate) fn new_generic(
|
||||
generic_context: Option<GenericContext<'db>>,
|
||||
parameters: Parameters<'db>,
|
||||
return_ty: Option<Type<'db>>,
|
||||
return_ty: Type<'db>,
|
||||
) -> Self {
|
||||
Self {
|
||||
generic_context,
|
||||
@@ -654,7 +646,7 @@ impl<'db> Signature<'db> {
|
||||
generic_context: None,
|
||||
definition: None,
|
||||
parameters: Parameters::gradual_form(),
|
||||
return_ty: Some(signature_type),
|
||||
return_ty: signature_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,7 +658,7 @@ impl<'db> Signature<'db> {
|
||||
generic_context: None,
|
||||
definition: None,
|
||||
parameters: Parameters::todo(),
|
||||
return_ty: Some(signature_type),
|
||||
return_ty: signature_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,7 +679,8 @@ impl<'db> Signature<'db> {
|
||||
let return_ty = function_node
|
||||
.returns
|
||||
.as_ref()
|
||||
.map(|returns| function_signature_expression_type(db, definition, returns.as_ref()));
|
||||
.map(|returns| function_signature_expression_type(db, definition, returns.as_ref()))
|
||||
.unwrap_or_else(Type::unknown);
|
||||
let legacy_generic_context =
|
||||
GenericContext::from_function_params(db, definition, ¶meters, return_ty);
|
||||
let full_generic_context = GenericContext::merge_pep695_and_legacy(
|
||||
@@ -705,21 +698,19 @@ impl<'db> Signature<'db> {
|
||||
}
|
||||
|
||||
pub(super) fn wrap_coroutine_return_type(self, db: &'db dyn Db) -> Self {
|
||||
let return_ty = self.return_ty.map(|return_ty| {
|
||||
KnownClass::CoroutineType
|
||||
.to_specialized_instance(db, [Type::any(), Type::any(), return_ty])
|
||||
});
|
||||
let return_ty = KnownClass::CoroutineType
|
||||
.to_specialized_instance(db, [Type::any(), Type::any(), self.return_ty]);
|
||||
Self { return_ty, ..self }
|
||||
}
|
||||
|
||||
/// Returns the signature which accepts any parameters and returns an `Unknown` type.
|
||||
pub(crate) fn unknown() -> Self {
|
||||
Self::new(Parameters::unknown(), Some(Type::unknown()))
|
||||
Self::new(Parameters::unknown(), Type::unknown())
|
||||
}
|
||||
|
||||
/// Return the "bottom" signature, subtype of all other fully-static signatures.
|
||||
pub(crate) fn bottom() -> Self {
|
||||
Self::new(Parameters::bottom(), Some(Type::Never))
|
||||
Self::new(Parameters::bottom(), Type::Never)
|
||||
}
|
||||
|
||||
pub(crate) fn with_inherited_generic_context(
|
||||
@@ -756,9 +747,7 @@ impl<'db> Signature<'db> {
|
||||
.iter()
|
||||
.map(|param| param.normalized_impl(db, visitor)),
|
||||
),
|
||||
return_ty: self
|
||||
.return_ty
|
||||
.map(|return_ty| return_ty.normalized_impl(db, visitor)),
|
||||
return_ty: self.return_ty.normalized_impl(db, visitor),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,16 +757,13 @@ impl<'db> Signature<'db> {
|
||||
div: Type<'db>,
|
||||
nested: bool,
|
||||
) -> Option<Self> {
|
||||
let return_ty = match self.return_ty {
|
||||
Some(return_ty) if nested => {
|
||||
Some(return_ty.recursive_type_normalized_impl(db, div, true)?)
|
||||
}
|
||||
Some(return_ty) => Some(
|
||||
return_ty
|
||||
.recursive_type_normalized_impl(db, div, true)
|
||||
.unwrap_or(div),
|
||||
),
|
||||
None => None,
|
||||
let return_ty = if nested {
|
||||
self.return_ty
|
||||
.recursive_type_normalized_impl(db, div, true)?
|
||||
} else {
|
||||
self.return_ty
|
||||
.recursive_type_normalized_impl(db, div, true)
|
||||
.unwrap_or(div)
|
||||
};
|
||||
let parameters = {
|
||||
let mut parameters = Vec::with_capacity(self.parameters.len());
|
||||
@@ -811,7 +797,7 @@ impl<'db> Signature<'db> {
|
||||
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
return_ty: self
|
||||
.return_ty
|
||||
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
|
||||
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,16 +809,18 @@ impl<'db> Signature<'db> {
|
||||
visitor: &FindLegacyTypeVarsVisitor<'db>,
|
||||
) {
|
||||
for param in &self.parameters {
|
||||
if let Some(ty) = param.annotated_type() {
|
||||
ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
param.annotated_type().find_legacy_typevars_impl(
|
||||
db,
|
||||
binding_context,
|
||||
typevars,
|
||||
visitor,
|
||||
);
|
||||
if let Some(ty) = param.default_type() {
|
||||
ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
}
|
||||
if let Some(ty) = self.return_ty {
|
||||
ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
self.return_ty
|
||||
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
|
||||
/// Return the parameters in this signature.
|
||||
@@ -852,11 +840,11 @@ impl<'db> Signature<'db> {
|
||||
) {
|
||||
if let Some(first_parameter) = self.parameters.value.first_mut()
|
||||
&& first_parameter.is_positional()
|
||||
&& first_parameter.annotated_type.is_none()
|
||||
&& first_parameter.annotated_type.is_unknown()
|
||||
&& first_parameter.inferred_annotation
|
||||
&& let Some(self_type) = self_type()
|
||||
{
|
||||
first_parameter.annotated_type = Some(self_type);
|
||||
first_parameter.inferred_annotation = true;
|
||||
first_parameter.annotated_type = self_type;
|
||||
|
||||
// If we've added an implicit `self` annotation, we might need to update the
|
||||
// signature's generic context, too. (The generic context should include any synthetic
|
||||
@@ -919,8 +907,7 @@ impl<'db> Signature<'db> {
|
||||
TypeContext::default(),
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
);
|
||||
return_ty = return_ty
|
||||
.map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default()));
|
||||
return_ty = return_ty.apply_type_mapping(db, &self_mapping, TypeContext::default());
|
||||
}
|
||||
Self {
|
||||
generic_context: self
|
||||
@@ -943,9 +930,9 @@ impl<'db> Signature<'db> {
|
||||
TypeContext::default(),
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
);
|
||||
let return_ty = self
|
||||
.return_ty
|
||||
.map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default()));
|
||||
let return_ty =
|
||||
self.return_ty
|
||||
.apply_type_mapping(db, &self_mapping, TypeContext::default());
|
||||
Self {
|
||||
generic_context: self.generic_context,
|
||||
definition: self.definition,
|
||||
@@ -1006,16 +993,6 @@ impl<'db> Signature<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut check_types = |self_type: Option<Type<'db>>, other_type: Option<Type<'db>>| {
|
||||
let self_type = self_type.unwrap_or(Type::unknown());
|
||||
let other_type = other_type.unwrap_or(Type::unknown());
|
||||
!result
|
||||
.intersect(
|
||||
db,
|
||||
self_type.is_equivalent_to_impl(db, other_type, inferable, visitor),
|
||||
)
|
||||
.is_never_satisfied(db)
|
||||
};
|
||||
|
||||
if self.parameters.is_gradual() != other.parameters.is_gradual() {
|
||||
return ConstraintSet::from(false);
|
||||
@@ -1025,6 +1002,15 @@ impl<'db> Signature<'db> {
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
|
||||
let mut check_types = |self_type: Type<'db>, other_type: Type<'db>| {
|
||||
!result
|
||||
.intersect(
|
||||
db,
|
||||
self_type.is_equivalent_to_impl(db, other_type, inferable, visitor),
|
||||
)
|
||||
.is_never_satisfied(db)
|
||||
};
|
||||
|
||||
if !check_types(self.return_ty, other.return_ty) {
|
||||
return result;
|
||||
}
|
||||
@@ -1096,29 +1082,24 @@ impl<'db> Signature<'db> {
|
||||
{
|
||||
let upper = Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(
|
||||
other
|
||||
.overloads
|
||||
.iter()
|
||||
.map(|signature| Signature::new(signature.parameters().clone(), None)),
|
||||
),
|
||||
CallableSignature::from_overloads(other.overloads.iter().map(|signature| {
|
||||
Signature::new(signature.parameters().clone(), Type::unknown())
|
||||
})),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches =
|
||||
ConstraintSet::constrain_typevar(db, self_bound_typevar, Type::Never, upper);
|
||||
let return_types_match = self.return_ty.when_some_and(|self_return_type| {
|
||||
other
|
||||
.overloads
|
||||
.iter()
|
||||
.filter_map(|signature| signature.return_ty)
|
||||
.when_any(db, |other_return_type| {
|
||||
self_return_type.when_constraint_set_assignable_to(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
)
|
||||
})
|
||||
});
|
||||
let return_types_match = other
|
||||
.overloads
|
||||
.iter()
|
||||
.map(|signature| signature.return_ty)
|
||||
.when_any(db, |other_return_type| {
|
||||
self.return_ty.when_constraint_set_assignable_to(
|
||||
db,
|
||||
other_return_type,
|
||||
inferable,
|
||||
)
|
||||
});
|
||||
return param_spec_matches.and(db, || return_types_match);
|
||||
}
|
||||
|
||||
@@ -1258,10 +1239,8 @@ impl<'db> Signature<'db> {
|
||||
}
|
||||
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut check_types = |type1: Option<Type<'db>>, type2: Option<Type<'db>>| {
|
||||
let type1 = type1.unwrap_or(Type::unknown());
|
||||
let type2 = type2.unwrap_or(Type::unknown());
|
||||
|
||||
let mut check_types = |type1: Type<'db>, type2: Type<'db>| {
|
||||
match (type1, type2) {
|
||||
// This is a special case where the _same_ components of two different `ParamSpec`
|
||||
// type variables are assignable to each other when they're both in an inferable
|
||||
@@ -1310,11 +1289,11 @@ impl<'db> Signature<'db> {
|
||||
&& self
|
||||
.parameters
|
||||
.variadic()
|
||||
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object()))
|
||||
.is_some_and(|(_, param)| param.annotated_type().is_object())
|
||||
&& self
|
||||
.parameters
|
||||
.keyword_variadic()
|
||||
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object()))
|
||||
.is_some_and(|(_, param)| param.annotated_type().is_object())
|
||||
{
|
||||
return ConstraintSet::from(true);
|
||||
}
|
||||
@@ -1360,7 +1339,10 @@ impl<'db> Signature<'db> {
|
||||
(Some(self_bound_typevar), None) => {
|
||||
let upper = Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::single(Signature::new(other.parameters.clone(), None)),
|
||||
CallableSignature::single(Signature::new(
|
||||
other.parameters.clone(),
|
||||
Type::unknown(),
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
@@ -1376,7 +1358,10 @@ impl<'db> Signature<'db> {
|
||||
(None, Some(other_bound_typevar)) => {
|
||||
let lower = Type::Callable(CallableType::new(
|
||||
db,
|
||||
CallableSignature::single(Signature::new(self.parameters.clone(), None)),
|
||||
CallableSignature::single(Signature::new(
|
||||
self.parameters.clone(),
|
||||
Type::unknown(),
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
@@ -1585,10 +1570,9 @@ impl<'db> Signature<'db> {
|
||||
|
||||
// Type of the variadic keyword parameter in `self`.
|
||||
//
|
||||
// This is a nested option where the outer option represents the presence of a keyword
|
||||
// variadic parameter in `self` and the inner option represents the annotated type of the
|
||||
// keyword variadic parameter.
|
||||
let mut self_keyword_variadic: Option<Option<Type<'db>>> = None;
|
||||
// This is an option representing the presence (and annotated type) of a keyword variadic
|
||||
// parameter in `self`.
|
||||
let mut self_keyword_variadic: Option<Type<'db>> = None;
|
||||
|
||||
for self_parameter in self_parameters {
|
||||
match self_parameter.kind() {
|
||||
@@ -1700,12 +1684,14 @@ impl<'db> VarianceInferable<'db> for &Signature<'db> {
|
||||
.iter()
|
||||
.filter_map(|parameter| match parameter.form {
|
||||
ParameterForm::Type => None,
|
||||
ParameterForm::Value => parameter.annotated_type().map(|ty| {
|
||||
ty.with_polarity(TypeVarVariance::Contravariant)
|
||||
.variance_of(db, typevar)
|
||||
}),
|
||||
ParameterForm::Value => Some(
|
||||
parameter
|
||||
.annotated_type()
|
||||
.with_polarity(TypeVarVariance::Contravariant)
|
||||
.variance_of(db, typevar),
|
||||
),
|
||||
}),
|
||||
self.return_ty.map(|ty| ty.variance_of(db, typevar)),
|
||||
Some(self.return_ty.variance_of(db, typevar)),
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
@@ -1773,10 +1759,10 @@ impl<'db> Parameters<'db> {
|
||||
&& p2.is_keyword_variadic()
|
||||
{
|
||||
match (p1.annotated_type(), p2.annotated_type()) {
|
||||
(None | Some(Type::Dynamic(_)), None | Some(Type::Dynamic(_))) => {
|
||||
(Type::Dynamic(_), Type::Dynamic(_)) => {
|
||||
kind = ParametersKind::Gradual;
|
||||
}
|
||||
(Some(Type::TypeVar(args_typevar)), Some(Type::TypeVar(kwargs_typevar))) => {
|
||||
(Type::TypeVar(args_typevar), Type::TypeVar(kwargs_typevar)) => {
|
||||
if let (Some(ParamSpecAttrKind::Args), Some(ParamSpecAttrKind::Kwargs)) = (
|
||||
args_typevar.paramspec_attr(db),
|
||||
kwargs_typevar.paramspec_attr(db),
|
||||
@@ -1938,7 +1924,7 @@ impl<'db> Parameters<'db> {
|
||||
}
|
||||
|
||||
let (Type::TypeVar(args_typevar), Type::TypeVar(kwargs_typevar)) =
|
||||
(maybe_args.annotated_type()?, maybe_kwargs.annotated_type()?)
|
||||
(maybe_args.annotated_type(), maybe_kwargs.annotated_type())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
@@ -2195,11 +2181,14 @@ impl<'db> std::ops::Index<usize> for Parameters<'db> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
pub(crate) struct Parameter<'db> {
|
||||
/// Annotated type of the parameter.
|
||||
annotated_type: Option<Type<'db>>,
|
||||
/// Annotated type of the parameter. If no annotation was provided, this is `Unknown`.
|
||||
annotated_type: Type<'db>,
|
||||
|
||||
/// Does the type of this parameter come from an explicit annotation, or was it inferred from
|
||||
/// the context, like `Self` for the `self` parameter of instance methods.
|
||||
/// the context, like `Unknown` for any normal un-annotated parameter, `Self` for the `self`
|
||||
/// parameter of instance method, or `type[Self]` for `cls` parameter of classmethods. This
|
||||
/// field is only used to decide whether to display the annotated type; it has no effect on the
|
||||
/// type semantics of the parameter.
|
||||
pub(crate) inferred_annotation: bool,
|
||||
|
||||
kind: ParameterKind<'db>,
|
||||
@@ -2209,8 +2198,8 @@ pub(crate) struct Parameter<'db> {
|
||||
impl<'db> Parameter<'db> {
|
||||
pub(crate) fn positional_only(name: Option<Name>) -> Self {
|
||||
Self {
|
||||
annotated_type: None,
|
||||
inferred_annotation: false,
|
||||
annotated_type: Type::unknown(),
|
||||
inferred_annotation: true,
|
||||
kind: ParameterKind::PositionalOnly {
|
||||
name,
|
||||
default_type: None,
|
||||
@@ -2221,8 +2210,8 @@ impl<'db> Parameter<'db> {
|
||||
|
||||
pub(crate) fn positional_or_keyword(name: Name) -> Self {
|
||||
Self {
|
||||
annotated_type: None,
|
||||
inferred_annotation: false,
|
||||
annotated_type: Type::unknown(),
|
||||
inferred_annotation: true,
|
||||
kind: ParameterKind::PositionalOrKeyword {
|
||||
name,
|
||||
default_type: None,
|
||||
@@ -2233,8 +2222,8 @@ impl<'db> Parameter<'db> {
|
||||
|
||||
pub(crate) fn variadic(name: Name) -> Self {
|
||||
Self {
|
||||
annotated_type: None,
|
||||
inferred_annotation: false,
|
||||
annotated_type: Type::unknown(),
|
||||
inferred_annotation: true,
|
||||
kind: ParameterKind::Variadic { name },
|
||||
form: ParameterForm::Value,
|
||||
}
|
||||
@@ -2242,8 +2231,8 @@ impl<'db> Parameter<'db> {
|
||||
|
||||
pub(crate) fn keyword_only(name: Name) -> Self {
|
||||
Self {
|
||||
annotated_type: None,
|
||||
inferred_annotation: false,
|
||||
annotated_type: Type::unknown(),
|
||||
inferred_annotation: true,
|
||||
kind: ParameterKind::KeywordOnly {
|
||||
name,
|
||||
default_type: None,
|
||||
@@ -2254,15 +2243,18 @@ impl<'db> Parameter<'db> {
|
||||
|
||||
pub(crate) fn keyword_variadic(name: Name) -> Self {
|
||||
Self {
|
||||
annotated_type: None,
|
||||
inferred_annotation: false,
|
||||
annotated_type: Type::unknown(),
|
||||
inferred_annotation: true,
|
||||
kind: ParameterKind::KeywordVariadic { name },
|
||||
form: ParameterForm::Value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the annotated type for this parameter. This also marks the annotation as explicit
|
||||
/// (not inferred), so it will be displayed.
|
||||
pub(crate) fn with_annotated_type(mut self, annotated_type: Type<'db>) -> Self {
|
||||
self.annotated_type = Some(annotated_type);
|
||||
self.annotated_type = annotated_type;
|
||||
self.inferred_annotation = false;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2291,9 +2283,12 @@ impl<'db> Parameter<'db> {
|
||||
visitor: &ApplyTypeMappingVisitor<'db>,
|
||||
) -> Self {
|
||||
Self {
|
||||
annotated_type: self
|
||||
.annotated_type
|
||||
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
|
||||
annotated_type: self.annotated_type.apply_type_mapping_impl(
|
||||
db,
|
||||
type_mapping,
|
||||
tcx,
|
||||
visitor,
|
||||
),
|
||||
kind: self
|
||||
.kind
|
||||
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
@@ -2303,7 +2298,7 @@ impl<'db> Parameter<'db> {
|
||||
}
|
||||
|
||||
/// Strip information from the parameter so that two equivalent parameters compare equal.
|
||||
/// Normalize nested unions and intersections in the annotated type, if any.
|
||||
/// Normalize nested unions and intersections in the annotated type.
|
||||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
pub(crate) fn normalized_impl(
|
||||
@@ -2313,18 +2308,14 @@ impl<'db> Parameter<'db> {
|
||||
) -> Self {
|
||||
let Parameter {
|
||||
annotated_type,
|
||||
inferred_annotation,
|
||||
kind,
|
||||
form,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// Ensure unions and intersections are ordered in the annotated type (if there is one).
|
||||
// Ensure that a parameter without an annotation is treated equivalently to a parameter
|
||||
// with a dynamic type as its annotation. (We must use `Any` here as all dynamic types
|
||||
// normalize to `Any`.)
|
||||
let annotated_type = annotated_type
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.unwrap_or_else(Type::any);
|
||||
// Ensure unions and intersections are ordered in the annotated type.
|
||||
// Unknown normalizes to Any.
|
||||
let annotated_type = annotated_type.normalized_impl(db, visitor);
|
||||
|
||||
// Ensure that parameter names are stripped from positional-only, variadic and keyword-variadic parameters.
|
||||
// Ensure that we only record whether a parameter *has* a default
|
||||
@@ -2356,8 +2347,10 @@ impl<'db> Parameter<'db> {
|
||||
};
|
||||
|
||||
Self {
|
||||
annotated_type: Some(annotated_type),
|
||||
inferred_annotation: *inferred_annotation,
|
||||
annotated_type,
|
||||
// Normalize `inferred_annotation` to `false` since it's a display-only field
|
||||
// that doesn't affect type semantics.
|
||||
inferred_annotation: false,
|
||||
kind,
|
||||
form: *form,
|
||||
}
|
||||
@@ -2376,13 +2369,12 @@ impl<'db> Parameter<'db> {
|
||||
form,
|
||||
} = self;
|
||||
|
||||
let annotated_type = match annotated_type {
|
||||
Some(ty) if nested => Some(ty.recursive_type_normalized_impl(db, div, true)?),
|
||||
Some(ty) => Some(
|
||||
ty.recursive_type_normalized_impl(db, div, true)
|
||||
.unwrap_or(div),
|
||||
),
|
||||
None => None,
|
||||
let annotated_type = if nested {
|
||||
annotated_type.recursive_type_normalized_impl(db, div, true)?
|
||||
} else {
|
||||
annotated_type
|
||||
.recursive_type_normalized_impl(db, div, true)
|
||||
.unwrap_or(div)
|
||||
};
|
||||
|
||||
let kind = match kind {
|
||||
@@ -2443,13 +2435,20 @@ impl<'db> Parameter<'db> {
|
||||
parameter: &ast::Parameter,
|
||||
kind: ParameterKind<'db>,
|
||||
) -> Self {
|
||||
let (annotated_type, inferred_annotation) = if let Some(annotation) = parameter.annotation()
|
||||
{
|
||||
(
|
||||
function_signature_expression_type(db, definition, annotation),
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
(Type::unknown(), true)
|
||||
};
|
||||
Self {
|
||||
annotated_type: parameter
|
||||
.annotation()
|
||||
.map(|annotation| function_signature_expression_type(db, definition, annotation)),
|
||||
annotated_type,
|
||||
kind,
|
||||
form: ParameterForm::Value,
|
||||
inferred_annotation: false,
|
||||
inferred_annotation,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2494,8 +2493,8 @@ impl<'db> Parameter<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Annotated type of the parameter, if annotated.
|
||||
pub(crate) fn annotated_type(&self) -> Option<Type<'db>> {
|
||||
/// Annotated type of the parameter. If no annotation was provided, this is `Unknown`.
|
||||
pub(crate) fn annotated_type(&self) -> Type<'db> {
|
||||
self.annotated_type
|
||||
}
|
||||
|
||||
@@ -2654,7 +2653,7 @@ mod tests {
|
||||
|
||||
let sig = func.signature(&db);
|
||||
|
||||
assert!(sig.return_ty.is_none());
|
||||
assert!(sig.return_ty.is_unknown());
|
||||
assert_params(&sig, &[]);
|
||||
}
|
||||
|
||||
@@ -2679,7 +2678,7 @@ mod tests {
|
||||
|
||||
let sig = func.signature(&db);
|
||||
|
||||
assert_eq!(sig.return_ty.unwrap().display(&db).to_string(), "bytes");
|
||||
assert_eq!(sig.return_ty.display(&db).to_string(), "bytes");
|
||||
assert_params(
|
||||
&sig,
|
||||
&[
|
||||
@@ -2743,7 +2742,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(name, "a");
|
||||
// Parameter resolution not deferred; we should see A not B
|
||||
assert_eq!(annotated_type.unwrap().display(&db).to_string(), "A");
|
||||
assert_eq!(annotated_type.display(&db).to_string(), "A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2781,7 +2780,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(name, "a");
|
||||
// Parameter resolution deferred:
|
||||
assert_eq!(annotated_type.unwrap().display(&db).to_string(), "A | B");
|
||||
assert_eq!(annotated_type.display(&db).to_string(), "A | B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2824,8 +2823,8 @@ mod tests {
|
||||
};
|
||||
assert_eq!(a_name, "a");
|
||||
assert_eq!(b_name, "b");
|
||||
assert_eq!(a_annotated_ty.unwrap().display(&db).to_string(), "A");
|
||||
assert_eq!(b_annotated_ty.unwrap().display(&db).to_string(), "T@f");
|
||||
assert_eq!(a_annotated_ty.display(&db).to_string(), "A");
|
||||
assert_eq!(b_annotated_ty.display(&db).to_string(), "T@f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2869,8 +2868,8 @@ mod tests {
|
||||
assert_eq!(a_name, "a");
|
||||
assert_eq!(b_name, "b");
|
||||
// Parameter resolution deferred:
|
||||
assert_eq!(a_annotated_ty.unwrap().display(&db).to_string(), "A | B");
|
||||
assert_eq!(b_annotated_ty.unwrap().display(&db).to_string(), "T@f");
|
||||
assert_eq!(a_annotated_ty.display(&db).to_string(), "A | B");
|
||||
assert_eq!(b_annotated_ty.display(&db).to_string(), "T@f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user