mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00
[ty] Simplify signature types, use them in CallableType
(#18344)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
There were many fields in `Signature` and friends that really had more to do with how a signature was being _used_ — how it was looked up, details about an individual call site, etc. Those fields more properly belong in `Bindings` and friends. This is a pure refactoring, and should not affect any tests or ecosystem projects. I started on this journey in support of https://github.com/astral-sh/ty/issues/462. It seemed worth pulling out as a separate PR. One major concrete benefit of this refactoring is that we can now use `CallableSignature` directly in `CallableType`. (We can't use `CallableSignature` directly in that `Type` variant because signatures are not currently interned.)
This commit is contained in:
parent
a5ebb3f3a2
commit
452f992fbc
8 changed files with 730 additions and 689 deletions
|
@ -29,7 +29,7 @@ pub(crate) use self::infer::{
|
||||||
infer_scope_types,
|
infer_scope_types,
|
||||||
};
|
};
|
||||||
pub(crate) use self::narrow::ClassInfoConstraintFunction;
|
pub(crate) use self::narrow::ClassInfoConstraintFunction;
|
||||||
pub(crate) use self::signatures::{CallableSignature, Signature, Signatures};
|
pub(crate) use self::signatures::{CallableSignature, Signature};
|
||||||
pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType};
|
pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType};
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::module_resolver::{KnownModule, file_to_module, resolve_module};
|
use crate::module_resolver::{KnownModule, file_to_module, resolve_module};
|
||||||
|
@ -41,7 +41,7 @@ use crate::suppression::check_suppressions;
|
||||||
use crate::symbol::{
|
use crate::symbol::{
|
||||||
Boundness, Symbol, SymbolAndQualifiers, imported_symbol, symbol_from_bindings,
|
Boundness, Symbol, SymbolAndQualifiers, imported_symbol, symbol_from_bindings,
|
||||||
};
|
};
|
||||||
use crate::types::call::{Bindings, CallArgumentTypes, CallableBinding};
|
use crate::types::call::{Binding, Bindings, CallArgumentTypes, CallableBinding};
|
||||||
pub(crate) use crate::types::class_base::ClassBase;
|
pub(crate) use crate::types::class_base::ClassBase;
|
||||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||||
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||||
|
@ -2768,7 +2768,7 @@ impl<'db> Type<'db> {
|
||||||
Some((self, AttributeKind::NormalOrNonDataDescriptor))
|
Some((self, AttributeKind::NormalOrNonDataDescriptor))
|
||||||
} else {
|
} else {
|
||||||
Some((
|
Some((
|
||||||
Type::Callable(callable.bind_self(db)),
|
callable.bind_self(db),
|
||||||
AttributeKind::NormalOrNonDataDescriptor,
|
AttributeKind::NormalOrNonDataDescriptor,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
@ -3546,28 +3546,28 @@ impl<'db> Type<'db> {
|
||||||
non_negative_int_literal(db, return_ty)
|
non_negative_int_literal(db, return_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the call signatures of a type.
|
/// Returns a [`Bindings`] that can be used to analyze a call to this type. You must call
|
||||||
|
/// [`match_parameters`][Bindings::match_parameters] and [`check_types`][Bindings::check_types]
|
||||||
|
/// to fully analyze a particular call site.
|
||||||
///
|
///
|
||||||
/// Note that all types have a valid [`Signatures`], even if the type is not callable.
|
/// Note that we return a [`Bindings`] for all types, even if the type is not callable.
|
||||||
/// Moreover, "callable" can be subtle for a union type, since some union elements might be
|
/// "Callable" can be subtle for a union type, since some union elements might be callable and
|
||||||
/// callable and some not. A union is callable if every element type is callable — and even
|
/// some not. A union is callable if every element type is callable — but even then, the
|
||||||
/// then, the elements might be inconsistent, such that there's no argument list that's valid
|
/// elements might be inconsistent, such that there's no argument list that's valid for all
|
||||||
/// for all elements. It's usually best to only worry about "callability" relative to a
|
/// elements. It's usually best to only worry about "callability" relative to a particular
|
||||||
/// particular argument list, via [`try_call`][Self::try_call] and
|
/// argument list, via [`try_call`][Self::try_call] and [`CallErrorKind::NotCallable`].
|
||||||
/// [`CallErrorKind::NotCallable`].
|
fn bindings(self, db: &'db dyn Db) -> Bindings<'db> {
|
||||||
fn signatures(self, db: &'db dyn Db) -> Signatures<'db> {
|
|
||||||
match self {
|
match self {
|
||||||
Type::Callable(callable) => Signatures::single(match callable.signatures(db) {
|
Type::Callable(callable) => {
|
||||||
[signature] => CallableSignature::single(self, signature.clone()),
|
CallableBinding::from_overloads(self, callable.signatures(db).iter().cloned())
|
||||||
signatures => CallableSignature::from_overloads(self, signatures.iter().cloned()),
|
.into()
|
||||||
}),
|
}
|
||||||
|
|
||||||
Type::BoundMethod(bound_method) => {
|
Type::BoundMethod(bound_method) => {
|
||||||
let signature = bound_method.function(db).signature(db);
|
let signature = bound_method.function(db).signature(db);
|
||||||
Signatures::single(
|
CallableBinding::from_overloads(self, signature.overloads.iter().cloned())
|
||||||
CallableSignature::from_overloads(self, signature.overloads.iter().cloned())
|
.with_bound_type(bound_method.self_instance(db))
|
||||||
.with_bound_type(bound_method.self_instance(db)),
|
.into()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::MethodWrapper(
|
Type::MethodWrapper(
|
||||||
|
@ -3591,7 +3591,7 @@ impl<'db> Type<'db> {
|
||||||
// specified yet, they will be dynamically added in `Bindings::evaluate_known_cases`.
|
// specified yet, they will be dynamically added in `Bindings::evaluate_known_cases`.
|
||||||
|
|
||||||
let not_none = Type::none(db).negate(db);
|
let not_none = Type::none(db).negate(db);
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
Signature::new(
|
Signature::new(
|
||||||
|
@ -3617,8 +3617,8 @@ impl<'db> Type<'db> {
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::WrapperDescriptor(
|
Type::WrapperDescriptor(
|
||||||
|
@ -3644,7 +3644,7 @@ impl<'db> Type<'db> {
|
||||||
unreachable!("Not part of outer match pattern")
|
unreachable!("Not part of outer match pattern")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
Signature::new(
|
Signature::new(
|
||||||
|
@ -3674,12 +3674,11 @@ impl<'db> Type<'db> {
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(_)) => {
|
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(_)) => Binding::single(
|
||||||
Signatures::single(CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3690,10 +3689,10 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
}
|
.into(),
|
||||||
Type::WrapperDescriptor(WrapperDescriptorKind::PropertyDunderSet) => {
|
|
||||||
Signatures::single(CallableSignature::single(
|
Type::WrapperDescriptor(WrapperDescriptorKind::PropertyDunderSet) => Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3706,11 +3705,10 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
}
|
.into(),
|
||||||
|
|
||||||
Type::MethodWrapper(MethodWrapperKind::StrStartswith(_)) => {
|
Type::MethodWrapper(MethodWrapperKind::StrStartswith(_)) => Binding::single(
|
||||||
Signatures::single(CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3740,20 +3738,21 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
Some(KnownClass::Bool.to_instance(db)),
|
Some(KnownClass::Bool.to_instance(db)),
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
}
|
.into(),
|
||||||
|
|
||||||
// TODO: We should probably also check the original return type of the function
|
// TODO: We should probably also check the original return type of the function
|
||||||
// that was decorated with `@dataclass_transform`, to see if it is consistent with
|
// that was decorated with `@dataclass_transform`, to see if it is consistent with
|
||||||
// with what we configure here.
|
// with what we configure here.
|
||||||
Type::DataclassTransformer(_) => Signatures::single(CallableSignature::single(
|
Type::DataclassTransformer(_) => Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([Parameter::positional_only(Some(Name::new_static("func")))
|
Parameters::new([Parameter::positional_only(Some(Name::new_static("func")))
|
||||||
.with_annotated_type(Type::object(db))]),
|
.with_annotated_type(Type::object(db))]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
)),
|
)
|
||||||
|
.into(),
|
||||||
|
|
||||||
Type::FunctionLiteral(function_type) => match function_type.known(db) {
|
Type::FunctionLiteral(function_type) => match function_type.known(db) {
|
||||||
Some(
|
Some(
|
||||||
|
@ -3762,8 +3761,7 @@ impl<'db> Type<'db> {
|
||||||
| KnownFunction::IsAssignableTo
|
| KnownFunction::IsAssignableTo
|
||||||
| KnownFunction::IsDisjointFrom
|
| KnownFunction::IsDisjointFrom
|
||||||
| KnownFunction::IsGradualEquivalentTo,
|
| KnownFunction::IsGradualEquivalentTo,
|
||||||
) => {
|
) => Binding::single(
|
||||||
let signature = CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3776,31 +3774,25 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
Some(KnownClass::Bool.to_instance(db)),
|
Some(KnownClass::Bool.to_instance(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into(),
|
||||||
}
|
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
KnownFunction::IsFullyStatic
|
KnownFunction::IsFullyStatic
|
||||||
| KnownFunction::IsSingleton
|
| KnownFunction::IsSingleton
|
||||||
| KnownFunction::IsSingleValued,
|
| KnownFunction::IsSingleValued,
|
||||||
) => {
|
) => Binding::single(
|
||||||
let signature = CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([Parameter::positional_only(Some(Name::new_static(
|
Parameters::new([Parameter::positional_only(Some(Name::new_static("a")))
|
||||||
"a",
|
|
||||||
)))
|
|
||||||
.type_form()
|
.type_form()
|
||||||
.with_annotated_type(Type::any())]),
|
.with_annotated_type(Type::any())]),
|
||||||
Some(KnownClass::Bool.to_instance(db)),
|
Some(KnownClass::Bool.to_instance(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into(),
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownFunction::AssertType) => {
|
Some(KnownFunction::AssertType) => Binding::single(
|
||||||
let signature = CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3812,12 +3804,11 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
Some(Type::none(db)),
|
Some(Type::none(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into(),
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownFunction::AssertNever) => {
|
Some(KnownFunction::AssertNever) => {
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([Parameter::positional_only(Some(Name::new_static(
|
Parameters::new([Parameter::positional_only(Some(Name::new_static(
|
||||||
|
@ -3830,12 +3821,11 @@ impl<'db> Type<'db> {
|
||||||
.with_annotated_type(Type::any())]),
|
.with_annotated_type(Type::any())]),
|
||||||
Some(Type::none(db)),
|
Some(Type::none(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownFunction::Cast) => {
|
Some(KnownFunction::Cast) => Binding::single(
|
||||||
let signature = CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -3847,12 +3837,11 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
Some(Type::any()),
|
Some(Type::any()),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into(),
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownFunction::Dataclass) => {
|
Some(KnownFunction::Dataclass) => {
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
// def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ...
|
// def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ...
|
||||||
|
@ -3922,12 +3911,15 @@ impl<'db> Type<'db> {
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
|
.into()
|
||||||
Signatures::single(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Signatures::single(function_type.signature(db).overloads.clone()),
|
_ => CallableBinding::from_overloads(
|
||||||
|
self,
|
||||||
|
function_type.signature(db).overloads.iter().cloned(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::ClassLiteral(class) => match class.known(db) {
|
Type::ClassLiteral(class) => match class.known(db) {
|
||||||
|
@ -3942,7 +3934,7 @@ impl<'db> Type<'db> {
|
||||||
// class bool(int):
|
// class bool(int):
|
||||||
// def __new__(cls, o: object = ..., /) -> Self: ...
|
// def __new__(cls, o: object = ..., /) -> Self: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([Parameter::positional_only(Some(Name::new_static(
|
Parameters::new([Parameter::positional_only(Some(Name::new_static(
|
||||||
|
@ -3952,8 +3944,8 @@ impl<'db> Type<'db> {
|
||||||
.with_default_type(Type::BooleanLiteral(false))]),
|
.with_default_type(Type::BooleanLiteral(false))]),
|
||||||
Some(KnownClass::Bool.to_instance(db)),
|
Some(KnownClass::Bool.to_instance(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Str) => {
|
Some(KnownClass::Str) => {
|
||||||
|
@ -3964,7 +3956,7 @@ impl<'db> Type<'db> {
|
||||||
// @overload
|
// @overload
|
||||||
// def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
|
// def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
Signature::new(
|
Signature::new(
|
||||||
|
@ -3997,8 +3989,8 @@ impl<'db> Type<'db> {
|
||||||
Some(KnownClass::Str.to_instance(db)),
|
Some(KnownClass::Str.to_instance(db)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Type) => {
|
Some(KnownClass::Type) => {
|
||||||
|
@ -4012,7 +4004,7 @@ impl<'db> Type<'db> {
|
||||||
// @overload
|
// @overload
|
||||||
// def __init__(self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None: ...
|
// def __init__(self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
Signature::new(
|
Signature::new(
|
||||||
|
@ -4042,29 +4034,32 @@ impl<'db> Type<'db> {
|
||||||
Some(type_instance),
|
Some(type_instance),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::NamedTuple) => {
|
Some(KnownClass::NamedTuple) => {
|
||||||
Signatures::single(CallableSignature::todo("functional `NamedTuple` syntax"))
|
Binding::single(self, Signature::todo("functional `NamedTuple` syntax")).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Object) => {
|
Some(KnownClass::Object) => {
|
||||||
// ```py
|
// ```py
|
||||||
// class object:
|
// class object:
|
||||||
// def __init__(self) -> None: ...
|
// def __init__(self) -> None: ...
|
||||||
// def __new__(cls) -> Self: ...
|
// def __new__(cls) -> Self: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::from_overloads(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
[Signature::new(
|
Signature::new(
|
||||||
Parameters::empty(),
|
Parameters::empty(),
|
||||||
Some(KnownClass::Object.to_instance(db)),
|
Some(KnownClass::Object.to_instance(db)),
|
||||||
)],
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Enum) => {
|
Some(KnownClass::Enum) => {
|
||||||
Signatures::single(CallableSignature::todo("functional `Enum` syntax"))
|
Binding::single(self, Signature::todo("functional `Enum` syntax")).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Super) => {
|
Some(KnownClass::Super) => {
|
||||||
|
@ -4077,7 +4072,7 @@ impl<'db> Type<'db> {
|
||||||
// @overload
|
// @overload
|
||||||
// def __init__(self) -> None: ...
|
// def __init__(self) -> None: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::from_overloads(
|
CallableBinding::from_overloads(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
Signature::new(
|
Signature::new(
|
||||||
|
@ -4101,8 +4096,8 @@ impl<'db> Type<'db> {
|
||||||
Some(KnownClass::Super.to_instance(db)),
|
Some(KnownClass::Super.to_instance(db)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::TypeVar) => {
|
Some(KnownClass::TypeVar) => {
|
||||||
|
@ -4119,7 +4114,7 @@ impl<'db> Type<'db> {
|
||||||
// default: Any = ...,
|
// default: Any = ...,
|
||||||
// ) -> Self: ...
|
// ) -> Self: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -4151,8 +4146,8 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
Some(KnownClass::TypeVar.to_instance(db)),
|
Some(KnownClass::TypeVar.to_instance(db)),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::TypeAliasType) => {
|
Some(KnownClass::TypeAliasType) => {
|
||||||
|
@ -4165,7 +4160,7 @@ impl<'db> Type<'db> {
|
||||||
// type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
|
// type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
|
||||||
// ) -> Self: ...
|
// ) -> Self: ...
|
||||||
// ```
|
// ```
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -4190,8 +4185,8 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::Property) => {
|
Some(KnownClass::Property) => {
|
||||||
|
@ -4215,7 +4210,7 @@ impl<'db> Type<'db> {
|
||||||
Some(Type::any()),
|
Some(Type::any()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -4223,10 +4218,7 @@ impl<'db> Type<'db> {
|
||||||
.with_annotated_type(UnionType::from_elements(
|
.with_annotated_type(UnionType::from_elements(
|
||||||
db,
|
db,
|
||||||
[
|
[
|
||||||
Type::Callable(CallableType::single(
|
CallableType::single(db, getter_signature),
|
||||||
db,
|
|
||||||
getter_signature,
|
|
||||||
)),
|
|
||||||
Type::none(db),
|
Type::none(db),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
|
@ -4235,10 +4227,7 @@ impl<'db> Type<'db> {
|
||||||
.with_annotated_type(UnionType::from_elements(
|
.with_annotated_type(UnionType::from_elements(
|
||||||
db,
|
db,
|
||||||
[
|
[
|
||||||
Type::Callable(CallableType::single(
|
CallableType::single(db, setter_signature),
|
||||||
db,
|
|
||||||
setter_signature,
|
|
||||||
)),
|
|
||||||
Type::none(db),
|
Type::none(db),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
|
@ -4247,10 +4236,7 @@ impl<'db> Type<'db> {
|
||||||
.with_annotated_type(UnionType::from_elements(
|
.with_annotated_type(UnionType::from_elements(
|
||||||
db,
|
db,
|
||||||
[
|
[
|
||||||
Type::Callable(CallableType::single(
|
CallableType::single(db, deleter_signature),
|
||||||
db,
|
|
||||||
deleter_signature,
|
|
||||||
)),
|
|
||||||
Type::none(db),
|
Type::none(db),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
|
@ -4264,8 +4250,8 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most class literal constructor calls are handled by `try_call_constructor` and
|
// Most class literal constructor calls are handled by `try_call_constructor` and
|
||||||
|
@ -4273,21 +4259,19 @@ impl<'db> Type<'db> {
|
||||||
// cases (e.g. evaluating callable subtyping). TODO improve this definition
|
// cases (e.g. evaluating callable subtyping). TODO improve this definition
|
||||||
// (intersection of `__new__` and `__init__` signatures? and respect metaclass
|
// (intersection of `__new__` and `__init__` signatures? and respect metaclass
|
||||||
// `__call__`).
|
// `__call__`).
|
||||||
_ => {
|
_ => Binding::single(
|
||||||
let signature = CallableSignature::single(
|
|
||||||
self,
|
self,
|
||||||
Signature::new_generic(
|
Signature::new_generic(
|
||||||
class.generic_context(db),
|
class.generic_context(db),
|
||||||
Parameters::gradual_form(),
|
Parameters::gradual_form(),
|
||||||
self.to_instance(db),
|
self.to_instance(db),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::KnownInstance(KnownInstanceType::TypedDict) => {
|
Type::KnownInstance(KnownInstanceType::TypedDict) => {
|
||||||
Signatures::single(CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
|
@ -4305,28 +4289,28 @@ impl<'db> Type<'db> {
|
||||||
]),
|
]),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GenericAlias(_) => {
|
Type::GenericAlias(_) => {
|
||||||
// TODO annotated return type on `__new__` or metaclass `__call__`
|
// TODO annotated return type on `__new__` or metaclass `__call__`
|
||||||
// TODO check call vs signatures of `__new__` and/or `__init__`
|
// TODO check call vs signatures of `__new__` and/or `__init__`
|
||||||
let signature = CallableSignature::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
Signature::new(Parameters::gradual_form(), self.to_instance(db)),
|
Signature::new(Parameters::gradual_form(), self.to_instance(db)),
|
||||||
);
|
)
|
||||||
Signatures::single(signature)
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() {
|
Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() {
|
||||||
SubclassOfInner::Dynamic(dynamic_type) => {
|
SubclassOfInner::Dynamic(dynamic_type) => Type::Dynamic(dynamic_type).bindings(db),
|
||||||
Type::Dynamic(dynamic_type).signatures(db)
|
|
||||||
}
|
|
||||||
// Most type[] constructor calls are handled by `try_call_constructor` and not via
|
// Most type[] constructor calls are handled by `try_call_constructor` and not via
|
||||||
// getting the signature here. This signature can still be used in some cases (e.g.
|
// getting the signature here. This signature can still be used in some cases (e.g.
|
||||||
// evaluating callable subtyping). TODO improve this definition (intersection of
|
// evaluating callable subtyping). TODO improve this definition (intersection of
|
||||||
// `__new__` and `__init__` signatures? and respect metaclass `__call__`).
|
// `__new__` and `__init__` signatures? and respect metaclass `__call__`).
|
||||||
SubclassOfInner::Class(class) => Type::from(class).signatures(db),
|
SubclassOfInner::Class(class) => Type::from(class).bindings(db),
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::NominalInstance(_) | Type::ProtocolInstance(_) => {
|
Type::NominalInstance(_) | Type::ProtocolInstance(_) => {
|
||||||
|
@ -4344,39 +4328,43 @@ impl<'db> Type<'db> {
|
||||||
.symbol
|
.symbol
|
||||||
{
|
{
|
||||||
Symbol::Type(dunder_callable, boundness) => {
|
Symbol::Type(dunder_callable, boundness) => {
|
||||||
let mut signatures = dunder_callable.signatures(db).clone();
|
let mut bindings = dunder_callable.bindings(db);
|
||||||
signatures.replace_callable_type(dunder_callable, self);
|
bindings.replace_callable_type(dunder_callable, self);
|
||||||
if boundness == Boundness::PossiblyUnbound {
|
if boundness == Boundness::PossiblyUnbound {
|
||||||
signatures.set_dunder_call_is_possibly_unbound();
|
bindings.set_dunder_call_is_possibly_unbound();
|
||||||
}
|
}
|
||||||
signatures
|
bindings
|
||||||
}
|
}
|
||||||
Symbol::Unbound => Signatures::not_callable(self),
|
Symbol::Unbound => CallableBinding::not_callable(self).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic types are callable, and the return type is the same dynamic type. Similarly,
|
// Dynamic types are callable, and the return type is the same dynamic type. Similarly,
|
||||||
// `Never` is always callable and returns `Never`.
|
// `Never` is always callable and returns `Never`.
|
||||||
Type::Dynamic(_) | Type::Never => Signatures::single(CallableSignature::dynamic(self)),
|
Type::Dynamic(_) | Type::Never => {
|
||||||
|
Binding::single(self, Signature::dynamic(self)).into()
|
||||||
|
}
|
||||||
|
|
||||||
// Note that this correctly returns `None` if none of the union elements are callable.
|
// Note that this correctly returns `None` if none of the union elements are callable.
|
||||||
Type::Union(union) => Signatures::from_union(
|
Type::Union(union) => Bindings::from_union(
|
||||||
self,
|
self,
|
||||||
union
|
union
|
||||||
.elements(db)
|
.elements(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|element| element.signatures(db)),
|
.map(|element| element.bindings(db)),
|
||||||
),
|
),
|
||||||
|
|
||||||
Type::Intersection(_) => {
|
Type::Intersection(_) => {
|
||||||
Signatures::single(CallableSignature::todo("Type::Intersection.call()"))
|
Binding::single(self, Signature::todo("Type::Intersection.call()")).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: these are actually callable
|
// TODO: these are actually callable
|
||||||
Type::MethodWrapper(_) | Type::DataclassDecorator(_) => Signatures::not_callable(self),
|
Type::MethodWrapper(_) | Type::DataclassDecorator(_) => {
|
||||||
|
CallableBinding::not_callable(self).into()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: some `KnownInstance`s are callable (e.g. TypedDicts)
|
// TODO: some `KnownInstance`s are callable (e.g. TypedDicts)
|
||||||
Type::KnownInstance(_) => Signatures::not_callable(self),
|
Type::KnownInstance(_) => CallableBinding::not_callable(self).into(),
|
||||||
|
|
||||||
Type::PropertyInstance(_)
|
Type::PropertyInstance(_)
|
||||||
| Type::AlwaysFalsy
|
| Type::AlwaysFalsy
|
||||||
|
@ -4389,7 +4377,7 @@ impl<'db> Type<'db> {
|
||||||
| Type::Tuple(_)
|
| Type::Tuple(_)
|
||||||
| Type::BoundSuper(_)
|
| Type::BoundSuper(_)
|
||||||
| Type::TypeVar(_)
|
| Type::TypeVar(_)
|
||||||
| Type::ModuleLiteral(_) => Signatures::not_callable(self),
|
| Type::ModuleLiteral(_) => CallableBinding::not_callable(self).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4404,8 +4392,9 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
argument_types: &CallArgumentTypes<'_, 'db>,
|
||||||
) -> Result<Bindings<'db>, CallError<'db>> {
|
) -> Result<Bindings<'db>, CallError<'db>> {
|
||||||
let signatures = self.signatures(db);
|
self.bindings(db)
|
||||||
Bindings::match_parameters(signatures, argument_types).check_types(db, argument_types)
|
.match_parameters(argument_types)
|
||||||
|
.check_types(db, argument_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up a dunder method on the meta-type of `self` and call it.
|
/// Look up a dunder method on the meta-type of `self` and call it.
|
||||||
|
@ -4451,8 +4440,9 @@ impl<'db> Type<'db> {
|
||||||
.symbol
|
.symbol
|
||||||
{
|
{
|
||||||
Symbol::Type(dunder_callable, boundness) => {
|
Symbol::Type(dunder_callable, boundness) => {
|
||||||
let signatures = dunder_callable.signatures(db);
|
let bindings = dunder_callable
|
||||||
let bindings = Bindings::match_parameters(signatures, argument_types)
|
.bindings(db)
|
||||||
|
.match_parameters(argument_types)
|
||||||
.check_types(db, argument_types)?;
|
.check_types(db, argument_types)?;
|
||||||
if boundness == Boundness::PossiblyUnbound {
|
if boundness == Boundness::PossiblyUnbound {
|
||||||
return Err(CallDunderError::PossiblyUnbound(Box::new(bindings)));
|
return Err(CallDunderError::PossiblyUnbound(Box::new(bindings)));
|
||||||
|
@ -4970,7 +4960,7 @@ impl<'db> Type<'db> {
|
||||||
KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)),
|
KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)),
|
||||||
|
|
||||||
// TODO: Use an opt-in rule for a bare `Callable`
|
// TODO: Use an opt-in rule for a bare `Callable`
|
||||||
KnownInstanceType::Callable => Ok(Type::Callable(CallableType::unknown(db))),
|
KnownInstanceType::Callable => Ok(CallableType::unknown(db)),
|
||||||
|
|
||||||
KnownInstanceType::TypingSelf => {
|
KnownInstanceType::TypingSelf => {
|
||||||
let index = semantic_index(db, scope_id.file(db));
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
|
@ -6896,7 +6886,7 @@ impl<'db> FunctionSignature<'db> {
|
||||||
/// Returns the "bottom" signature (subtype of all fully-static signatures.)
|
/// Returns the "bottom" signature (subtype of all fully-static signatures.)
|
||||||
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
||||||
FunctionSignature {
|
FunctionSignature {
|
||||||
overloads: CallableSignature::single(Type::any(), Signature::bottom(db)),
|
overloads: CallableSignature::single(Signature::bottom(db)),
|
||||||
implementation: None,
|
implementation: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6969,9 +6959,9 @@ impl<'db> FunctionType<'db> {
|
||||||
|
|
||||||
/// Convert the `FunctionType` into a [`Type::Callable`].
|
/// Convert the `FunctionType` into a [`Type::Callable`].
|
||||||
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
Type::Callable(CallableType::from_overloads(
|
Type::Callable(CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signature(db).overloads.iter().cloned(),
|
self.signature(db).overloads.clone(),
|
||||||
false,
|
false,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -7045,7 +7035,6 @@ impl<'db> FunctionType<'db> {
|
||||||
if let Some(overloaded) = self.to_overloaded(db) {
|
if let Some(overloaded) = self.to_overloaded(db) {
|
||||||
FunctionSignature {
|
FunctionSignature {
|
||||||
overloads: CallableSignature::from_overloads(
|
overloads: CallableSignature::from_overloads(
|
||||||
Type::FunctionLiteral(self),
|
|
||||||
overloaded.overloads.iter().copied().map(|overload| {
|
overloaded.overloads.iter().copied().map(|overload| {
|
||||||
type_mappings.iter().fold(
|
type_mappings.iter().fold(
|
||||||
overload.internal_signature(db, inherited_generic_context),
|
overload.internal_signature(db, inherited_generic_context),
|
||||||
|
@ -7062,13 +7051,10 @@ impl<'db> FunctionType<'db> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FunctionSignature {
|
FunctionSignature {
|
||||||
overloads: CallableSignature::single(
|
overloads: CallableSignature::single(type_mappings.iter().fold(
|
||||||
Type::FunctionLiteral(self),
|
|
||||||
type_mappings.iter().fold(
|
|
||||||
self.internal_signature(db, inherited_generic_context),
|
self.internal_signature(db, inherited_generic_context),
|
||||||
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
||||||
),
|
)),
|
||||||
),
|
|
||||||
implementation: None,
|
implementation: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7592,13 +7578,15 @@ pub struct BoundMethodType<'db> {
|
||||||
|
|
||||||
impl<'db> BoundMethodType<'db> {
|
impl<'db> BoundMethodType<'db> {
|
||||||
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
Type::Callable(CallableType::from_overloads(
|
Type::Callable(CallableType::new(
|
||||||
db,
|
db,
|
||||||
|
CallableSignature::from_overloads(
|
||||||
self.function(db)
|
self.function(db)
|
||||||
.signature(db)
|
.signature(db)
|
||||||
.overloads
|
.overloads
|
||||||
.iter()
|
.iter()
|
||||||
.map(signatures::Signature::bind_self),
|
.map(signatures::Signature::bind_self),
|
||||||
|
),
|
||||||
false,
|
false,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -7662,8 +7650,9 @@ impl<'db> BoundMethodType<'db> {
|
||||||
#[salsa::interned(debug)]
|
#[salsa::interned(debug)]
|
||||||
#[derive(PartialOrd, Ord)]
|
#[derive(PartialOrd, Ord)]
|
||||||
pub struct CallableType<'db> {
|
pub struct CallableType<'db> {
|
||||||
#[returns(deref)]
|
#[returns(ref)]
|
||||||
signatures: Box<[Signature<'db>]>,
|
signatures: CallableSignature<'db>,
|
||||||
|
|
||||||
/// We use `CallableType` to represent function-like objects, like the synthesized methods
|
/// We use `CallableType` to represent function-like objects, like the synthesized methods
|
||||||
/// of dataclasses or NamedTuples. These callables act like real functions when accessed
|
/// of dataclasses or NamedTuples. These callables act like real functions when accessed
|
||||||
/// as attributes on instances, i.e. they bind `self`.
|
/// as attributes on instances, i.e. they bind `self`.
|
||||||
|
@ -7671,49 +7660,37 @@ pub struct CallableType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> CallableType<'db> {
|
impl<'db> CallableType<'db> {
|
||||||
/// Create a non-overloaded callable type with a single signature.
|
/// Create a callable type with a single non-overloaded signature.
|
||||||
pub(crate) fn single(db: &'db dyn Db, signature: Signature<'db>) -> Self {
|
pub(crate) fn single(db: &'db dyn Db, signature: Signature<'db>) -> Type<'db> {
|
||||||
CallableType::new(db, vec![signature].into_boxed_slice(), false)
|
Type::Callable(CallableType::new(
|
||||||
|
db,
|
||||||
|
CallableSignature::single(signature),
|
||||||
|
false,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a non-overloaded, function-like callable type with a single signature.
|
/// Create a non-overloaded, function-like callable type with a single signature.
|
||||||
///
|
///
|
||||||
/// A function-like callable will bind `self` when accessed as an attribute on an instance.
|
/// A function-like callable will bind `self` when accessed as an attribute on an instance.
|
||||||
pub(crate) fn function_like(db: &'db dyn Db, signature: Signature<'db>) -> Self {
|
pub(crate) fn function_like(db: &'db dyn Db, signature: Signature<'db>) -> Type<'db> {
|
||||||
CallableType::new(db, vec![signature].into_boxed_slice(), true)
|
Type::Callable(CallableType::new(
|
||||||
}
|
db,
|
||||||
|
CallableSignature::single(signature),
|
||||||
/// Create an overloaded callable type with multiple signatures.
|
true,
|
||||||
///
|
))
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `overloads` is empty.
|
|
||||||
pub(crate) fn from_overloads<I>(db: &'db dyn Db, overloads: I, is_function_like: bool) -> Self
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = Signature<'db>>,
|
|
||||||
{
|
|
||||||
let overloads = overloads.into_iter().collect::<Vec<_>>().into_boxed_slice();
|
|
||||||
assert!(
|
|
||||||
!overloads.is_empty(),
|
|
||||||
"CallableType must have at least one signature"
|
|
||||||
);
|
|
||||||
CallableType::new(db, overloads, is_function_like)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a callable type which accepts any parameters and returns an `Unknown` type.
|
/// Create a callable type which accepts any parameters and returns an `Unknown` type.
|
||||||
pub(crate) fn unknown(db: &'db dyn Db) -> Self {
|
pub(crate) fn unknown(db: &'db dyn Db) -> Type<'db> {
|
||||||
CallableType::single(
|
Self::single(db, Signature::unknown())
|
||||||
db,
|
|
||||||
Signature::new(Parameters::unknown(), Some(Type::unknown())),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn bind_self(self, db: &'db dyn Db) -> Self {
|
pub(crate) fn bind_self(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
CallableType::from_overloads(
|
Type::Callable(CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signatures(db).iter().map(Signature::bind_self),
|
self.signatures(db).bind_self(),
|
||||||
false,
|
false,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a callable type which represents a fully-static "bottom" callable.
|
/// Create a callable type which represents a fully-static "bottom" callable.
|
||||||
|
@ -7722,28 +7699,24 @@ impl<'db> CallableType<'db> {
|
||||||
/// `(*args: object, **kwargs: object) -> Never`.
|
/// `(*args: object, **kwargs: object) -> Never`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn bottom(db: &'db dyn Db) -> Type<'db> {
|
pub(crate) fn bottom(db: &'db dyn Db) -> Type<'db> {
|
||||||
Type::Callable(CallableType::single(db, Signature::bottom(db)))
|
Self::single(db, Signature::bottom(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a "normalized" version of this `Callable` type.
|
/// Return a "normalized" version of this `Callable` type.
|
||||||
///
|
///
|
||||||
/// See [`Type::normalized`] for more details.
|
/// See [`Type::normalized`] for more details.
|
||||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||||
CallableType::from_overloads(
|
CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signatures(db)
|
self.signatures(db).normalized(db),
|
||||||
.iter()
|
|
||||||
.map(|signature| signature.normalized(db)),
|
|
||||||
self.is_function_like(db),
|
self.is_function_like(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
CallableType::from_overloads(
|
CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signatures(db)
|
self.signatures(db).apply_type_mapping(db, type_mapping),
|
||||||
.iter()
|
|
||||||
.map(|signature| signature.apply_type_mapping(db, type_mapping)),
|
|
||||||
self.is_function_like(db),
|
self.is_function_like(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7753,148 +7726,63 @@ impl<'db> CallableType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||||
) {
|
) {
|
||||||
for signature in self.signatures(db) {
|
self.signatures(db).find_legacy_typevars(db, typevars);
|
||||||
signature.find_legacy_typevars(db, typevars);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is fully static.
|
/// Check whether this callable type is fully static.
|
||||||
///
|
///
|
||||||
/// See [`Type::is_fully_static`] for more details.
|
/// See [`Type::is_fully_static`] for more details.
|
||||||
fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||||
self.signatures(db)
|
self.signatures(db).is_fully_static(db)
|
||||||
.iter()
|
|
||||||
.all(|signature| signature.is_fully_static(db))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is a subtype of another callable type.
|
/// Check whether this callable type is a subtype of another callable type.
|
||||||
///
|
///
|
||||||
/// See [`Type::is_subtype_of`] for more details.
|
/// See [`Type::is_subtype_of`] for more details.
|
||||||
fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
self.is_assignable_to_impl(db, other, &|self_signature, other_signature| {
|
let self_is_function_like = self.is_function_like(db);
|
||||||
self_signature.is_subtype_of(db, other_signature)
|
let other_is_function_like = other.is_function_like(db);
|
||||||
})
|
(self_is_function_like || !other_is_function_like)
|
||||||
|
&& self.signatures(db).is_subtype_of(db, other.signatures(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is assignable to another callable type.
|
/// Check whether this callable type is assignable to another callable type.
|
||||||
///
|
///
|
||||||
/// See [`Type::is_assignable_to`] for more details.
|
/// See [`Type::is_assignable_to`] for more details.
|
||||||
fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool {
|
fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
self.is_assignable_to_impl(db, other, &|self_signature, other_signature| {
|
|
||||||
self_signature.is_assignable_to(db, other_signature)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation for the various relation checks between two, possible overloaded, callable
|
|
||||||
/// types.
|
|
||||||
///
|
|
||||||
/// The `check_signature` closure is used to check the relation between two [`Signature`]s.
|
|
||||||
fn is_assignable_to_impl<F>(self, db: &'db dyn Db, other: Self, check_signature: &F) -> bool
|
|
||||||
where
|
|
||||||
F: Fn(&Signature<'db>, &Signature<'db>) -> bool,
|
|
||||||
{
|
|
||||||
let self_is_function_like = self.is_function_like(db);
|
let self_is_function_like = self.is_function_like(db);
|
||||||
let other_is_function_like = other.is_function_like(db);
|
let other_is_function_like = other.is_function_like(db);
|
||||||
|
(self_is_function_like || !other_is_function_like)
|
||||||
if !self_is_function_like && other_is_function_like {
|
&& self
|
||||||
return false;
|
.signatures(db)
|
||||||
}
|
.is_assignable_to(db, other.signatures(db))
|
||||||
|
|
||||||
match (self.signatures(db), other.signatures(db)) {
|
|
||||||
([self_signature], [other_signature]) => {
|
|
||||||
// Base case: both callable types contain a single signature.
|
|
||||||
check_signature(self_signature, other_signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `self` is possibly overloaded while `other` is definitely not overloaded.
|
|
||||||
(self_signatures, [other_signature]) => {
|
|
||||||
let other_callable = CallableType::single(db, other_signature.clone());
|
|
||||||
self_signatures
|
|
||||||
.iter()
|
|
||||||
.map(|self_signature| CallableType::single(db, self_signature.clone()))
|
|
||||||
.any(|self_callable| {
|
|
||||||
self_callable.is_assignable_to_impl(db, other_callable, check_signature)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// `self` is definitely not overloaded while `other` is possibly overloaded.
|
|
||||||
([self_signature], other_signatures) => {
|
|
||||||
let self_callable = CallableType::single(db, self_signature.clone());
|
|
||||||
other_signatures
|
|
||||||
.iter()
|
|
||||||
.map(|other_signature| CallableType::single(db, other_signature.clone()))
|
|
||||||
.all(|other_callable| {
|
|
||||||
self_callable.is_assignable_to_impl(db, other_callable, check_signature)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// `self` is definitely overloaded while `other` is possibly overloaded.
|
|
||||||
(_, other_signatures) => other_signatures
|
|
||||||
.iter()
|
|
||||||
.map(|other_signature| CallableType::single(db, other_signature.clone()))
|
|
||||||
.all(|other_callable| {
|
|
||||||
self.is_assignable_to_impl(db, other_callable, check_signature)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is equivalent to another callable type.
|
/// Check whether this callable type is equivalent to another callable type.
|
||||||
///
|
///
|
||||||
/// See [`Type::is_equivalent_to`] for more details.
|
/// See [`Type::is_equivalent_to`] for more details.
|
||||||
fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
if self.is_function_like(db) != other.is_function_like(db) {
|
self.is_function_like(db) == other.is_function_like(db)
|
||||||
return false;
|
&& self
|
||||||
}
|
.signatures(db)
|
||||||
|
.is_equivalent_to(db, other.signatures(db))
|
||||||
match (self.signatures(db), other.signatures(db)) {
|
|
||||||
([self_signature], [other_signature]) => {
|
|
||||||
// Common case: both callable types contain a single signature, use the custom
|
|
||||||
// equivalence check instead of delegating it to the subtype check.
|
|
||||||
self_signature.is_equivalent_to(db, other_signature)
|
|
||||||
}
|
|
||||||
(self_signatures, other_signatures) => {
|
|
||||||
if !self_signatures
|
|
||||||
.iter()
|
|
||||||
.chain(other_signatures.iter())
|
|
||||||
.all(|signature| signature.is_fully_static(db))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if self == other {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
self.is_subtype_of(db, other) && other.is_subtype_of(db, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is gradual equivalent to another callable type.
|
/// Check whether this callable type is gradual equivalent to another callable type.
|
||||||
///
|
///
|
||||||
/// See [`Type::is_gradual_equivalent_to`] for more details.
|
/// See [`Type::is_gradual_equivalent_to`] for more details.
|
||||||
fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
if self.is_function_like(db) != other.is_function_like(db) {
|
self.is_function_like(db) == other.is_function_like(db)
|
||||||
return false;
|
&& self
|
||||||
}
|
.signatures(db)
|
||||||
|
.is_gradual_equivalent_to(db, other.signatures(db))
|
||||||
match (self.signatures(db), other.signatures(db)) {
|
|
||||||
([self_signature], [other_signature]) => {
|
|
||||||
self_signature.is_gradual_equivalent_to(db, other_signature)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// TODO: overloads
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Type::replace_self_reference`].
|
/// See [`Type::replace_self_reference`].
|
||||||
fn replace_self_reference(self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
fn replace_self_reference(self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
||||||
CallableType::from_overloads(
|
CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signatures(db)
|
self.signatures(db).replace_self_reference(db, class),
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|signature| signature.replace_self_reference(db, class)),
|
|
||||||
self.is_function_like(db),
|
self.is_function_like(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use super::context::InferContext;
|
use super::context::InferContext;
|
||||||
use super::{CallableSignature, Signature, Signatures, Type};
|
use super::{Signature, Type};
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
|
|
||||||
mod arguments;
|
mod arguments;
|
||||||
mod bind;
|
mod bind;
|
||||||
pub(super) use arguments::{Argument, CallArgumentTypes, CallArguments};
|
pub(super) use arguments::{Argument, CallArgumentTypes, CallArguments};
|
||||||
pub(super) use bind::{Bindings, CallableBinding};
|
pub(super) use bind::{Binding, Bindings, CallableBinding};
|
||||||
|
|
||||||
/// Wraps a [`Bindings`] for an unsuccessful call with information about why the call was
|
/// Wraps a [`Bindings`] for an unsuccessful call with information about why the call was
|
||||||
/// unsuccessful.
|
/// unsuccessful.
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
//! [signatures][crate::types::signatures], we have to handle the fact that the callable might be a
|
//! [signatures][crate::types::signatures], we have to handle the fact that the callable might be a
|
||||||
//! union of types, each of which might contain multiple overloads.
|
//! union of types, each of which might contain multiple overloads.
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Argument, CallArgumentTypes, CallArguments, CallError, CallErrorKind, CallableSignature,
|
Argument, CallArgumentTypes, CallArguments, CallError, CallErrorKind, InferContext, Signature,
|
||||||
InferContext, Signature, Signatures, Type,
|
Type,
|
||||||
};
|
};
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::dunder_all::dunder_all_names;
|
use crate::dunder_all::dunder_all_names;
|
||||||
|
@ -33,7 +33,9 @@ use ruff_python_ast as ast;
|
||||||
/// It's guaranteed that the wrapped bindings have no errors.
|
/// It's guaranteed that the wrapped bindings have no errors.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Bindings<'db> {
|
pub(crate) struct Bindings<'db> {
|
||||||
signatures: Signatures<'db>,
|
/// The type that is (hopefully) callable.
|
||||||
|
callable_type: Type<'db>,
|
||||||
|
|
||||||
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a non-union
|
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a non-union
|
||||||
/// type.
|
/// type.
|
||||||
elements: SmallVec<[CallableBinding<'db>; 1]>,
|
elements: SmallVec<[CallableBinding<'db>; 1]>,
|
||||||
|
@ -45,6 +47,40 @@ pub(crate) struct Bindings<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Bindings<'db> {
|
impl<'db> Bindings<'db> {
|
||||||
|
/// Creates a new `Bindings` from an iterator of [`Bindings`]s. Panics if the iterator is
|
||||||
|
/// empty.
|
||||||
|
pub(crate) fn from_union<I>(callable_type: Type<'db>, elements: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Bindings<'db>>,
|
||||||
|
{
|
||||||
|
let elements: SmallVec<_> = elements
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|s| s.elements.into_iter())
|
||||||
|
.collect();
|
||||||
|
assert!(!elements.is_empty());
|
||||||
|
Self {
|
||||||
|
callable_type,
|
||||||
|
elements,
|
||||||
|
argument_forms: Box::from([]),
|
||||||
|
conflicting_forms: Box::from([]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn replace_callable_type(&mut self, before: Type<'db>, after: Type<'db>) {
|
||||||
|
if self.callable_type == before {
|
||||||
|
self.callable_type = after;
|
||||||
|
}
|
||||||
|
for binding in &mut self.elements {
|
||||||
|
binding.replace_callable_type(before, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_dunder_call_is_possibly_unbound(&mut self) {
|
||||||
|
for binding in &mut self.elements {
|
||||||
|
binding.dunder_call_is_possibly_unbound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Match the arguments of a call site against the parameters of a collection of possibly
|
/// Match the arguments of a call site against the parameters of a collection of possibly
|
||||||
/// unioned, possibly overloaded signatures.
|
/// unioned, possibly overloaded signatures.
|
||||||
///
|
///
|
||||||
|
@ -55,30 +91,15 @@ impl<'db> Bindings<'db> {
|
||||||
///
|
///
|
||||||
/// Once you have argument types available, you can call [`check_types`][Self::check_types] to
|
/// Once you have argument types available, you can call [`check_types`][Self::check_types] to
|
||||||
/// verify that each argument type is assignable to the corresponding parameter type.
|
/// verify that each argument type is assignable to the corresponding parameter type.
|
||||||
pub(crate) fn match_parameters(
|
pub(crate) fn match_parameters(mut self, arguments: &CallArguments<'_>) -> Self {
|
||||||
signatures: Signatures<'db>,
|
|
||||||
arguments: &CallArguments<'_>,
|
|
||||||
) -> Self {
|
|
||||||
let mut argument_forms = vec![None; arguments.len()];
|
let mut argument_forms = vec![None; arguments.len()];
|
||||||
let mut conflicting_forms = vec![false; arguments.len()];
|
let mut conflicting_forms = vec![false; arguments.len()];
|
||||||
let elements: SmallVec<[CallableBinding<'db>; 1]> = signatures
|
for binding in &mut self.elements {
|
||||||
.iter()
|
binding.match_parameters(arguments, &mut argument_forms, &mut conflicting_forms);
|
||||||
.map(|signature| {
|
|
||||||
CallableBinding::match_parameters(
|
|
||||||
signature,
|
|
||||||
arguments,
|
|
||||||
&mut argument_forms,
|
|
||||||
&mut conflicting_forms,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Bindings {
|
|
||||||
signatures,
|
|
||||||
elements,
|
|
||||||
argument_forms: argument_forms.into(),
|
|
||||||
conflicting_forms: conflicting_forms.into(),
|
|
||||||
}
|
}
|
||||||
|
self.argument_forms = argument_forms.into();
|
||||||
|
self.conflicting_forms = conflicting_forms.into();
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that the type of each argument is assignable to type of the parameter that it was
|
/// Verify that the type of each argument is assignable to type of the parameter that it was
|
||||||
|
@ -95,8 +116,8 @@ impl<'db> Bindings<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
argument_types: &CallArgumentTypes<'_, 'db>,
|
||||||
) -> Result<Self, CallError<'db>> {
|
) -> Result<Self, CallError<'db>> {
|
||||||
for (signature, element) in self.signatures.iter().zip(&mut self.elements) {
|
for element in &mut self.elements {
|
||||||
element.check_types(db, signature, argument_types);
|
element.check_types(db, argument_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.evaluate_known_cases(db);
|
self.evaluate_known_cases(db);
|
||||||
|
@ -157,7 +178,7 @@ impl<'db> Bindings<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn callable_type(&self) -> Type<'db> {
|
pub(crate) fn callable_type(&self) -> Type<'db> {
|
||||||
self.signatures.callable_type
|
self.callable_type
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the return type of the call. For successful calls, this is the actual return type.
|
/// Returns the return type of the call. For successful calls, this is the actual return type.
|
||||||
|
@ -228,7 +249,7 @@ impl<'db> Bindings<'db> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Each special case listed here should have a corresponding clause in `Type::signatures`.
|
// Each special case listed here should have a corresponding clause in `Type::signatures`.
|
||||||
for (binding, callable_signature) in self.elements.iter_mut().zip(self.signatures.iter()) {
|
for binding in &mut self.elements {
|
||||||
let binding_type = binding.callable_type;
|
let binding_type = binding.callable_type;
|
||||||
for (overload_index, overload) in binding.matching_overloads_mut() {
|
for (overload_index, overload) in binding.matching_overloads_mut() {
|
||||||
match binding_type {
|
match binding_type {
|
||||||
|
@ -848,15 +869,12 @@ impl<'db> Bindings<'db> {
|
||||||
|
|
||||||
let mut dataclass_params = DataclassParams::from(params);
|
let mut dataclass_params = DataclassParams::from(params);
|
||||||
|
|
||||||
if let Some(Some(Type::BooleanLiteral(order))) =
|
if let Some(Some(Type::BooleanLiteral(order))) = overload
|
||||||
callable_signature.iter().nth(overload_index).and_then(
|
.signature
|
||||||
|signature| {
|
|
||||||
let (idx, _) = signature
|
|
||||||
.parameters()
|
.parameters()
|
||||||
.keyword_by_name("order")?;
|
.keyword_by_name("order")
|
||||||
overload.parameter_types().get(idx)
|
.map(|(idx, _)| idx)
|
||||||
},
|
.and_then(|idx| overload.parameter_types().get(idx))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
dataclass_params.set(DataclassParams::ORDER, *order);
|
dataclass_params.set(DataclassParams::ORDER, *order);
|
||||||
}
|
}
|
||||||
|
@ -945,6 +963,37 @@ impl<'a, 'db> IntoIterator for &'a mut Bindings<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'db> From<CallableBinding<'db>> for Bindings<'db> {
|
||||||
|
fn from(from: CallableBinding<'db>) -> Bindings<'db> {
|
||||||
|
Bindings {
|
||||||
|
callable_type: from.callable_type,
|
||||||
|
elements: smallvec![from],
|
||||||
|
argument_forms: Box::from([]),
|
||||||
|
conflicting_forms: Box::from([]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> From<Binding<'db>> for Bindings<'db> {
|
||||||
|
fn from(from: Binding<'db>) -> Bindings<'db> {
|
||||||
|
let callable_type = from.callable_type;
|
||||||
|
let signature_type = from.signature_type;
|
||||||
|
let callable_binding = CallableBinding {
|
||||||
|
callable_type,
|
||||||
|
signature_type,
|
||||||
|
dunder_call_is_possibly_unbound: false,
|
||||||
|
bound_type: None,
|
||||||
|
overloads: smallvec![from],
|
||||||
|
};
|
||||||
|
Bindings {
|
||||||
|
callable_type,
|
||||||
|
elements: smallvec![callable_binding],
|
||||||
|
argument_forms: Box::from([]),
|
||||||
|
conflicting_forms: Box::from([]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Binding information for a single callable. If the callable is overloaded, there is a separate
|
/// Binding information for a single callable. If the callable is overloaded, there is a separate
|
||||||
/// [`Binding`] for each overload.
|
/// [`Binding`] for each overload.
|
||||||
///
|
///
|
||||||
|
@ -962,10 +1011,21 @@ impl<'a, 'db> IntoIterator for &'a mut Bindings<'db> {
|
||||||
/// [overloads]: https://github.com/python/typing/pull/1839
|
/// [overloads]: https://github.com/python/typing/pull/1839
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CallableBinding<'db> {
|
pub(crate) struct CallableBinding<'db> {
|
||||||
|
/// The type that is (hopefully) callable.
|
||||||
pub(crate) callable_type: Type<'db>,
|
pub(crate) callable_type: Type<'db>,
|
||||||
|
|
||||||
|
/// The type we'll use for error messages referring to details of the called signature. For
|
||||||
|
/// calls to functions this will be the same as `callable_type`; for other callable instances
|
||||||
|
/// it may be a `__call__` method.
|
||||||
pub(crate) signature_type: Type<'db>,
|
pub(crate) signature_type: Type<'db>,
|
||||||
|
|
||||||
|
/// If this is a callable object (i.e. called via a `__call__` method), the boundness of
|
||||||
|
/// that call method.
|
||||||
pub(crate) dunder_call_is_possibly_unbound: bool,
|
pub(crate) dunder_call_is_possibly_unbound: bool,
|
||||||
|
|
||||||
|
/// The type of the bound `self` or `cls` parameter if this signature is for a bound method.
|
||||||
|
pub(crate) bound_type: Option<Type<'db>>,
|
||||||
|
|
||||||
/// The bindings of each overload of this callable. Will be empty if the type is not callable.
|
/// The bindings of each overload of this callable. Will be empty if the type is not callable.
|
||||||
///
|
///
|
||||||
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a
|
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a
|
||||||
|
@ -974,15 +1034,56 @@ pub(crate) struct CallableBinding<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> CallableBinding<'db> {
|
impl<'db> CallableBinding<'db> {
|
||||||
|
pub(crate) fn from_overloads(
|
||||||
|
signature_type: Type<'db>,
|
||||||
|
overloads: impl IntoIterator<Item = Signature<'db>>,
|
||||||
|
) -> Self {
|
||||||
|
let overloads = overloads
|
||||||
|
.into_iter()
|
||||||
|
.map(|signature| Binding::single(signature_type, signature))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
callable_type: signature_type,
|
||||||
|
signature_type,
|
||||||
|
dunder_call_is_possibly_unbound: false,
|
||||||
|
bound_type: None,
|
||||||
|
overloads,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn not_callable(signature_type: Type<'db>) -> Self {
|
||||||
|
Self {
|
||||||
|
callable_type: signature_type,
|
||||||
|
signature_type,
|
||||||
|
dunder_call_is_possibly_unbound: false,
|
||||||
|
bound_type: None,
|
||||||
|
overloads: smallvec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_bound_type(mut self, bound_type: Type<'db>) -> Self {
|
||||||
|
self.bound_type = Some(bound_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_callable_type(&mut self, before: Type<'db>, after: Type<'db>) {
|
||||||
|
if self.callable_type == before {
|
||||||
|
self.callable_type = after;
|
||||||
|
}
|
||||||
|
for binding in &mut self.overloads {
|
||||||
|
binding.replace_callable_type(before, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn match_parameters(
|
fn match_parameters(
|
||||||
signature: &CallableSignature<'db>,
|
&mut self,
|
||||||
arguments: &CallArguments<'_>,
|
arguments: &CallArguments<'_>,
|
||||||
argument_forms: &mut [Option<ParameterForm>],
|
argument_forms: &mut [Option<ParameterForm>],
|
||||||
conflicting_forms: &mut [bool],
|
conflicting_forms: &mut [bool],
|
||||||
) -> Self {
|
) {
|
||||||
// If this callable is a bound method, prepend the self instance onto the arguments list
|
// If this callable is a bound method, prepend the self instance onto the arguments list
|
||||||
// before checking.
|
// before checking.
|
||||||
let arguments = arguments.with_self(signature.bound_type);
|
let arguments = arguments.with_self(self.bound_type);
|
||||||
|
|
||||||
// TODO: This checks every overload. In the proposed more detailed call checking spec [1],
|
// TODO: This checks every overload. In the proposed more detailed call checking spec [1],
|
||||||
// arguments are checked for arity first, and are only checked for type assignability against
|
// arguments are checked for arity first, and are only checked for type assignability against
|
||||||
|
@ -990,37 +1091,17 @@ impl<'db> CallableBinding<'db> {
|
||||||
// two phases.
|
// two phases.
|
||||||
//
|
//
|
||||||
// [1] https://typing.python.org/en/latest/spec/overload.html#overload-call-evaluation
|
// [1] https://typing.python.org/en/latest/spec/overload.html#overload-call-evaluation
|
||||||
let overloads = signature
|
for overload in &mut self.overloads {
|
||||||
.into_iter()
|
overload.match_parameters(arguments.as_ref(), argument_forms, conflicting_forms);
|
||||||
.map(|signature| {
|
|
||||||
Binding::match_parameters(
|
|
||||||
signature,
|
|
||||||
arguments.as_ref(),
|
|
||||||
argument_forms,
|
|
||||||
conflicting_forms,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
CallableBinding {
|
|
||||||
callable_type: signature.callable_type,
|
|
||||||
signature_type: signature.signature_type,
|
|
||||||
dunder_call_is_possibly_unbound: signature.dunder_call_is_possibly_unbound,
|
|
||||||
overloads,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_types(
|
fn check_types(&mut self, db: &'db dyn Db, argument_types: &CallArgumentTypes<'_, 'db>) {
|
||||||
&mut self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
signature: &CallableSignature<'db>,
|
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
|
||||||
) {
|
|
||||||
// If this callable is a bound method, prepend the self instance onto the arguments list
|
// If this callable is a bound method, prepend the self instance onto the arguments list
|
||||||
// before checking.
|
// before checking.
|
||||||
let argument_types = argument_types.with_self(signature.bound_type);
|
let argument_types = argument_types.with_self(self.bound_type);
|
||||||
for (signature, overload) in signature.iter().zip(&mut self.overloads) {
|
for overload in &mut self.overloads {
|
||||||
overload.check_types(db, signature, argument_types.as_ref());
|
overload.check_types(db, argument_types.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1215,9 +1296,28 @@ impl<'db> CallableBinding<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'db> IntoIterator for &'a CallableBinding<'db> {
|
||||||
|
type Item = &'a Binding<'db>;
|
||||||
|
type IntoIter = std::slice::Iter<'a, Binding<'db>>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.overloads.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Binding information for one of the overloads of a callable.
|
/// Binding information for one of the overloads of a callable.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Binding<'db> {
|
pub(crate) struct Binding<'db> {
|
||||||
|
pub(crate) signature: Signature<'db>,
|
||||||
|
|
||||||
|
/// The type that is (hopefully) callable.
|
||||||
|
pub(crate) callable_type: Type<'db>,
|
||||||
|
|
||||||
|
/// The type we'll use for error messages referring to details of the called signature. For
|
||||||
|
/// calls to functions this will be the same as `callable_type`; for other callable instances
|
||||||
|
/// it may be a `__call__` method.
|
||||||
|
pub(crate) signature_type: Type<'db>,
|
||||||
|
|
||||||
/// Return type of the call.
|
/// Return type of the call.
|
||||||
return_ty: Type<'db>,
|
return_ty: Type<'db>,
|
||||||
|
|
||||||
|
@ -1241,18 +1341,37 @@ pub(crate) struct Binding<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Binding<'db> {
|
impl<'db> Binding<'db> {
|
||||||
|
pub(crate) fn single(signature_type: Type<'db>, signature: Signature<'db>) -> Binding<'db> {
|
||||||
|
Binding {
|
||||||
|
signature,
|
||||||
|
callable_type: signature_type,
|
||||||
|
signature_type,
|
||||||
|
return_ty: Type::unknown(),
|
||||||
|
specialization: None,
|
||||||
|
inherited_specialization: None,
|
||||||
|
argument_parameters: Box::from([]),
|
||||||
|
parameter_tys: Box::from([]),
|
||||||
|
errors: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_callable_type(&mut self, before: Type<'db>, after: Type<'db>) {
|
||||||
|
if self.callable_type == before {
|
||||||
|
self.callable_type = after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn match_parameters(
|
fn match_parameters(
|
||||||
signature: &Signature<'db>,
|
&mut self,
|
||||||
arguments: &CallArguments<'_>,
|
arguments: &CallArguments<'_>,
|
||||||
argument_forms: &mut [Option<ParameterForm>],
|
argument_forms: &mut [Option<ParameterForm>],
|
||||||
conflicting_forms: &mut [bool],
|
conflicting_forms: &mut [bool],
|
||||||
) -> Self {
|
) {
|
||||||
let parameters = signature.parameters();
|
let parameters = self.signature.parameters();
|
||||||
// The parameter that each argument is matched with.
|
// The parameter that each argument is matched with.
|
||||||
let mut argument_parameters = vec![None; arguments.len()];
|
let mut argument_parameters = vec![None; arguments.len()];
|
||||||
// Whether each parameter has been matched with an argument.
|
// Whether each parameter has been matched with an argument.
|
||||||
let mut parameter_matched = vec![false; parameters.len()];
|
let mut parameter_matched = vec![false; parameters.len()];
|
||||||
let mut errors = vec![];
|
|
||||||
let mut next_positional = 0;
|
let mut next_positional = 0;
|
||||||
let mut first_excess_positional = None;
|
let mut first_excess_positional = None;
|
||||||
let mut num_synthetic_args = 0;
|
let mut num_synthetic_args = 0;
|
||||||
|
@ -1290,7 +1409,7 @@ impl<'db> Binding<'db> {
|
||||||
.keyword_by_name(name)
|
.keyword_by_name(name)
|
||||||
.or_else(|| parameters.keyword_variadic())
|
.or_else(|| parameters.keyword_variadic())
|
||||||
else {
|
else {
|
||||||
errors.push(BindingError::UnknownArgument {
|
self.errors.push(BindingError::UnknownArgument {
|
||||||
argument_name: ast::name::Name::new(name),
|
argument_name: ast::name::Name::new(name),
|
||||||
argument_index: get_argument_index(argument_index, num_synthetic_args),
|
argument_index: get_argument_index(argument_index, num_synthetic_args),
|
||||||
});
|
});
|
||||||
|
@ -1315,7 +1434,7 @@ impl<'db> Binding<'db> {
|
||||||
}
|
}
|
||||||
if parameter_matched[index] {
|
if parameter_matched[index] {
|
||||||
if !parameter.is_variadic() && !parameter.is_keyword_variadic() {
|
if !parameter.is_variadic() && !parameter.is_keyword_variadic() {
|
||||||
errors.push(BindingError::ParameterAlreadyAssigned {
|
self.errors.push(BindingError::ParameterAlreadyAssigned {
|
||||||
argument_index: get_argument_index(argument_index, num_synthetic_args),
|
argument_index: get_argument_index(argument_index, num_synthetic_args),
|
||||||
parameter: ParameterContext::new(parameter, index, positional),
|
parameter: ParameterContext::new(parameter, index, positional),
|
||||||
});
|
});
|
||||||
|
@ -1325,7 +1444,7 @@ impl<'db> Binding<'db> {
|
||||||
parameter_matched[index] = true;
|
parameter_matched[index] = true;
|
||||||
}
|
}
|
||||||
if let Some(first_excess_argument_index) = first_excess_positional {
|
if let Some(first_excess_argument_index) = first_excess_positional {
|
||||||
errors.push(BindingError::TooManyPositionalArguments {
|
self.errors.push(BindingError::TooManyPositionalArguments {
|
||||||
first_excess_argument_index: get_argument_index(
|
first_excess_argument_index: get_argument_index(
|
||||||
first_excess_argument_index,
|
first_excess_argument_index,
|
||||||
num_synthetic_args,
|
num_synthetic_args,
|
||||||
|
@ -1350,27 +1469,17 @@ impl<'db> Binding<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
errors.push(BindingError::MissingArguments {
|
self.errors.push(BindingError::MissingArguments {
|
||||||
parameters: ParameterContexts(missing),
|
parameters: ParameterContexts(missing),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
self.return_ty = self.signature.return_ty.unwrap_or(Type::unknown());
|
||||||
return_ty: signature.return_ty.unwrap_or(Type::unknown()),
|
self.argument_parameters = argument_parameters.into_boxed_slice();
|
||||||
specialization: None,
|
self.parameter_tys = vec![None; parameters.len()].into_boxed_slice();
|
||||||
inherited_specialization: None,
|
|
||||||
argument_parameters: argument_parameters.into_boxed_slice(),
|
|
||||||
parameter_tys: vec![None; parameters.len()].into_boxed_slice(),
|
|
||||||
errors,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_types(
|
fn check_types(&mut self, db: &'db dyn Db, argument_types: &CallArgumentTypes<'_, 'db>) {
|
||||||
&mut self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
signature: &Signature<'db>,
|
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
|
||||||
) {
|
|
||||||
let mut num_synthetic_args = 0;
|
let mut num_synthetic_args = 0;
|
||||||
let get_argument_index = |argument_index: usize, num_synthetic_args: usize| {
|
let get_argument_index = |argument_index: usize, num_synthetic_args: usize| {
|
||||||
if argument_index >= num_synthetic_args {
|
if argument_index >= num_synthetic_args {
|
||||||
|
@ -1386,6 +1495,7 @@ impl<'db> Binding<'db> {
|
||||||
|
|
||||||
// If this overload is generic, first see if we can infer a specialization of the function
|
// If this overload is generic, first see if we can infer a specialization of the function
|
||||||
// from the arguments that were passed in.
|
// from the arguments that were passed in.
|
||||||
|
let signature = &self.signature;
|
||||||
let parameters = signature.parameters();
|
let parameters = signature.parameters();
|
||||||
if signature.generic_context.is_some() || signature.inherited_generic_context.is_some() {
|
if signature.generic_context.is_some() || signature.inherited_generic_context.is_some() {
|
||||||
let mut builder = SpecializationBuilder::new(db);
|
let mut builder = SpecializationBuilder::new(db);
|
||||||
|
|
|
@ -9,10 +9,10 @@ use super::{
|
||||||
use crate::semantic_index::DeclarationWithConstraint;
|
use crate::semantic_index::DeclarationWithConstraint;
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::types::generics::{GenericContext, Specialization};
|
use crate::types::generics::{GenericContext, Specialization};
|
||||||
use crate::types::signatures::{Parameter, Parameters};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
CallableType, DataclassParams, DataclassTransformerParams, KnownInstanceType, Signature,
|
CallableType, DataclassParams, DataclassTransformerParams, KnownInstanceType, TypeMapping,
|
||||||
TypeMapping, TypeVarInstance,
|
TypeVarInstance,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet, KnownModule, Program,
|
Db, FxOrderSet, KnownModule, Program,
|
||||||
|
@ -1229,13 +1229,15 @@ impl<'db> ClassLiteral<'db> {
|
||||||
// the `__set__` method can be called. We build a union of all possible options
|
// the `__set__` method can be called. We build a union of all possible options
|
||||||
// to account for possible overloads.
|
// to account for possible overloads.
|
||||||
let mut value_types = UnionBuilder::new(db);
|
let mut value_types = UnionBuilder::new(db);
|
||||||
for signature in &dunder_set.signatures(db) {
|
for binding in &dunder_set.bindings(db) {
|
||||||
for overload in signature {
|
for overload in binding {
|
||||||
if let Some(value_param) = overload.parameters().get_positional(2) {
|
if let Some(value_param) =
|
||||||
|
overload.signature.parameters().get_positional(2)
|
||||||
|
{
|
||||||
value_types = value_types.add(
|
value_types = value_types.add(
|
||||||
value_param.annotated_type().unwrap_or_else(Type::unknown),
|
value_param.annotated_type().unwrap_or_else(Type::unknown),
|
||||||
);
|
);
|
||||||
} else if overload.parameters().is_gradual() {
|
} else if overload.signature.parameters().is_gradual() {
|
||||||
value_types = value_types.add(Type::unknown());
|
value_types = value_types.add(Type::unknown());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1267,7 +1269,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature = Signature::new(Parameters::new(parameters), Some(Type::none(db)));
|
let signature = Signature::new(Parameters::new(parameters), Some(Type::none(db)));
|
||||||
Some(Type::Callable(CallableType::function_like(db, signature)))
|
Some(CallableType::function_like(db, signature))
|
||||||
};
|
};
|
||||||
|
|
||||||
match (field_policy, name) {
|
match (field_policy, name) {
|
||||||
|
@ -1317,7 +1319,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
Some(KnownClass::Bool.to_instance(db)),
|
Some(KnownClass::Bool.to_instance(db)),
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(Type::Callable(CallableType::function_like(db, signature)))
|
Some(CallableType::function_like(db, signature))
|
||||||
}
|
}
|
||||||
(CodeGeneratorKind::NamedTuple, name) if name != "__init__" => {
|
(CodeGeneratorKind::NamedTuple, name) if name != "__init__" => {
|
||||||
KnownClass::NamedTupleFallback
|
KnownClass::NamedTupleFallback
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ruff_python_literal::escape::AsciiEscape;
|
||||||
|
|
||||||
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
|
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
|
||||||
use crate::types::generics::{GenericContext, Specialization};
|
use crate::types::generics::{GenericContext, Specialization};
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
CallableType, IntersectionType, KnownClass, MethodWrapperKind, Protocol, StringLiteralType,
|
CallableType, IntersectionType, KnownClass, MethodWrapperKind, Protocol, StringLiteralType,
|
||||||
SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance, UnionType,
|
SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance, UnionType,
|
||||||
|
@ -413,13 +413,13 @@ impl<'db> CallableType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct DisplayCallableType<'db> {
|
pub(crate) struct DisplayCallableType<'db> {
|
||||||
signatures: &'db [Signature<'db>],
|
signatures: &'db CallableSignature<'db>,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for DisplayCallableType<'_> {
|
impl Display for DisplayCallableType<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self.signatures {
|
match self.signatures.overloads.as_slice() {
|
||||||
[signature] => signature.display(self.db).fmt(f),
|
[signature] => signature.display(self.db).fmt(f),
|
||||||
signatures => {
|
signatures => {
|
||||||
// TODO: How to display overloads?
|
// TODO: How to display overloads?
|
||||||
|
|
|
@ -64,7 +64,9 @@ use crate::symbol::{
|
||||||
global_symbol, module_type_implicit_global_declaration, module_type_implicit_global_symbol,
|
global_symbol, module_type_implicit_global_declaration, module_type_implicit_global_symbol,
|
||||||
symbol, symbol_from_bindings, symbol_from_declarations, typing_extensions_symbol,
|
symbol, symbol_from_bindings, symbol_from_declarations, typing_extensions_symbol,
|
||||||
};
|
};
|
||||||
use crate::types::call::{Argument, Bindings, CallArgumentTypes, CallArguments, CallError};
|
use crate::types::call::{
|
||||||
|
Argument, Binding, Bindings, CallArgumentTypes, CallArguments, CallError,
|
||||||
|
};
|
||||||
use crate::types::class::{MetaclassErrorKind, SliceLiteral};
|
use crate::types::class::{MetaclassErrorKind, SliceLiteral};
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
||||||
|
@ -81,16 +83,17 @@ use crate::types::diagnostic::{
|
||||||
};
|
};
|
||||||
use crate::types::generics::GenericContext;
|
use crate::types::generics::GenericContext;
|
||||||
use crate::types::mro::MroErrorKind;
|
use crate::types::mro::MroErrorKind;
|
||||||
|
use crate::types::signatures::{CallableSignature, Signature};
|
||||||
use crate::types::unpacker::{UnpackResult, Unpacker};
|
use crate::types::unpacker::{UnpackResult, Unpacker};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BareTypeAliasType, CallDunderError, CallableSignature, CallableType, ClassLiteral, ClassType,
|
BareTypeAliasType, CallDunderError, CallableType, ClassLiteral, ClassType, DataclassParams,
|
||||||
DataclassParams, DynamicType, FunctionDecorators, FunctionType, GenericAlias,
|
DynamicType, FunctionDecorators, FunctionType, GenericAlias, IntersectionBuilder,
|
||||||
IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType,
|
IntersectionType, KnownClass, KnownFunction, KnownInstanceType, MemberLookupPolicy,
|
||||||
MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm,
|
MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, Parameters,
|
||||||
Parameters, Signature, Signatures, StringLiteralType, SubclassOfType, Symbol,
|
StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness, TupleType, Type,
|
||||||
SymbolAndQualifiers, Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers,
|
TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints,
|
||||||
TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, UnionType, binding_type,
|
||||||
TypeVarVariance, UnionBuilder, UnionType, binding_type, todo_type,
|
todo_type,
|
||||||
};
|
};
|
||||||
use crate::unpack::{Unpack, UnpackPosition};
|
use crate::unpack::{Unpack, UnpackPosition};
|
||||||
use crate::util::subscript::{PyIndex, PySlice};
|
use crate::util::subscript::{PyIndex, PySlice};
|
||||||
|
@ -4847,10 +4850,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
// TODO: Useful inference of a lambda's return type will require a different approach,
|
// 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,
|
// 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.
|
// rather than eagerly computing a return type without knowing the argument types.
|
||||||
Type::Callable(CallableType::single(
|
CallableType::single(self.db(), Signature::new(parameters, Some(Type::unknown())))
|
||||||
self.db(),
|
|
||||||
Signature::new(parameters, Some(Type::unknown())),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type of the first parameter if the given scope is function-like (i.e. function or lambda).
|
/// Returns the type of the first parameter if the given scope is function-like (i.e. function or lambda).
|
||||||
|
@ -4964,8 +4964,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let signatures = callable_type.signatures(self.db());
|
let bindings = callable_type
|
||||||
let bindings = Bindings::match_parameters(signatures, &call_arguments);
|
.bindings(self.db())
|
||||||
|
.match_parameters(&call_arguments);
|
||||||
let call_argument_types =
|
let call_argument_types =
|
||||||
self.infer_argument_types(arguments, call_arguments, &bindings.argument_forms);
|
self.infer_argument_types(arguments, call_arguments, &bindings.argument_forms);
|
||||||
|
|
||||||
|
@ -7282,11 +7283,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
_ => CallArgumentTypes::positional([self.infer_type_expression(slice_node)]),
|
_ => CallArgumentTypes::positional([self.infer_type_expression(slice_node)]),
|
||||||
};
|
};
|
||||||
let signatures = Signatures::single(CallableSignature::single(
|
let binding = Binding::single(value_ty, generic_context.signature(self.db()));
|
||||||
value_ty,
|
let bindings = match Bindings::from(binding)
|
||||||
generic_context.signature(self.db()),
|
.match_parameters(&call_argument_types)
|
||||||
));
|
|
||||||
let bindings = match Bindings::match_parameters(signatures, &call_argument_types)
|
|
||||||
.check_types(self.db(), &call_argument_types)
|
.check_types(self.db(), &call_argument_types)
|
||||||
{
|
{
|
||||||
Ok(bindings) => bindings,
|
Ok(bindings) => bindings,
|
||||||
|
@ -8628,8 +8627,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
CallableType::unknown(db)
|
CallableType::unknown(db)
|
||||||
};
|
};
|
||||||
|
|
||||||
let callable_type = Type::Callable(callable_type);
|
|
||||||
|
|
||||||
// `Signature` / `Parameters` are not a `Type` variant, so we're storing
|
// `Signature` / `Parameters` are not a `Type` variant, so we're storing
|
||||||
// the outer callable type on these expressions instead.
|
// the outer callable type on these expressions instead.
|
||||||
self.store_expression_type(arguments_slice, callable_type);
|
self.store_expression_type(arguments_slice, callable_type);
|
||||||
|
@ -8701,20 +8698,20 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let argument_type = self.infer_expression(arguments_slice);
|
let argument_type = self.infer_expression(arguments_slice);
|
||||||
let signatures = argument_type.signatures(db);
|
let bindings = argument_type.bindings(db);
|
||||||
|
|
||||||
// SAFETY: This is enforced by the constructor methods on `Signatures` even in
|
// SAFETY: This is enforced by the constructor methods on `Bindings` even in
|
||||||
// the case of a non-callable union.
|
// the case of a non-callable union.
|
||||||
let callable_signature = signatures
|
let callable_binding = bindings
|
||||||
.iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.expect("`Signatures` should have at least one `CallableSignature`");
|
.expect("`Bindings` should have at least one `CallableBinding`");
|
||||||
|
|
||||||
let mut signature_iter = callable_signature.iter().map(|signature| {
|
let mut signature_iter = callable_binding.into_iter().map(|binding| {
|
||||||
if argument_type.is_bound_method() {
|
if argument_type.is_bound_method() {
|
||||||
signature.bind_self()
|
binding.signature.bind_self()
|
||||||
} else {
|
} else {
|
||||||
signature.clone()
|
binding.signature.clone()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8734,11 +8731,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
return Type::unknown();
|
return Type::unknown();
|
||||||
};
|
};
|
||||||
|
|
||||||
Type::Callable(CallableType::from_overloads(
|
let signature = CallableSignature::from_overloads(
|
||||||
db,
|
|
||||||
std::iter::once(signature).chain(signature_iter),
|
std::iter::once(signature).chain(signature_iter),
|
||||||
false,
|
);
|
||||||
))
|
Type::Callable(CallableType::new(db, signature, false))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -188,13 +188,13 @@ impl Ty {
|
||||||
|
|
||||||
create_bound_method(db, function, builtins_class)
|
create_bound_method(db, function, builtins_class)
|
||||||
}
|
}
|
||||||
Ty::Callable { params, returns } => Type::Callable(CallableType::single(
|
Ty::Callable { params, returns } => CallableType::single(
|
||||||
db,
|
db,
|
||||||
Signature::new(
|
Signature::new(
|
||||||
params.into_parameters(db),
|
params.into_parameters(db),
|
||||||
returns.map(|ty| ty.into_type(db)),
|
returns.map(|ty| ty.into_type(db)),
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,175 +22,33 @@ use crate::types::{ClassLiteral, TypeMapping, TypeVarInstance, todo_type};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
use ruff_python_ast::{self as ast, name::Name};
|
||||||
|
|
||||||
/// The signature of a possible union of callables.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
|
|
||||||
pub(crate) struct Signatures<'db> {
|
|
||||||
/// The type that is (hopefully) callable.
|
|
||||||
pub(crate) callable_type: Type<'db>,
|
|
||||||
/// The type we'll use for error messages referring to details of the called signature. For calls to functions this
|
|
||||||
/// will be the same as `callable_type`; for other callable instances it may be a `__call__` method.
|
|
||||||
pub(crate) signature_type: Type<'db>,
|
|
||||||
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a non-union
|
|
||||||
/// type.
|
|
||||||
elements: SmallVec<[CallableSignature<'db>; 1]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'db> Signatures<'db> {
|
|
||||||
pub(crate) fn not_callable(signature_type: Type<'db>) -> Self {
|
|
||||||
Self {
|
|
||||||
callable_type: signature_type,
|
|
||||||
signature_type,
|
|
||||||
elements: smallvec![CallableSignature::not_callable(signature_type)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn single(signature: CallableSignature<'db>) -> Self {
|
|
||||||
Self {
|
|
||||||
callable_type: signature.callable_type,
|
|
||||||
signature_type: signature.signature_type,
|
|
||||||
elements: smallvec![signature],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Signatures` from an iterator of [`Signature`]s. Panics if the iterator is
|
|
||||||
/// empty.
|
|
||||||
pub(crate) fn from_union<I>(signature_type: Type<'db>, elements: I) -> Self
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = Signatures<'db>>,
|
|
||||||
{
|
|
||||||
let elements: SmallVec<_> = elements
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|s| s.elements.into_iter())
|
|
||||||
.collect();
|
|
||||||
assert!(!elements.is_empty());
|
|
||||||
Self {
|
|
||||||
callable_type: signature_type,
|
|
||||||
signature_type,
|
|
||||||
elements,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> std::slice::Iter<'_, CallableSignature<'db>> {
|
|
||||||
self.elements.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn replace_callable_type(&mut self, before: Type<'db>, after: Type<'db>) {
|
|
||||||
if self.callable_type == before {
|
|
||||||
self.callable_type = after;
|
|
||||||
}
|
|
||||||
for signature in &mut self.elements {
|
|
||||||
signature.replace_callable_type(before, after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_dunder_call_is_possibly_unbound(&mut self) {
|
|
||||||
for signature in &mut self.elements {
|
|
||||||
signature.dunder_call_is_possibly_unbound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'db> IntoIterator for &'a Signatures<'db> {
|
|
||||||
type Item = &'a CallableSignature<'db>;
|
|
||||||
type IntoIter = std::slice::Iter<'a, CallableSignature<'db>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
||||||
/// [`Signature`] for each overload.
|
/// [`Signature`] for each overload.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub(crate) struct CallableSignature<'db> {
|
pub struct CallableSignature<'db> {
|
||||||
/// The type that is (hopefully) callable.
|
|
||||||
pub(crate) callable_type: Type<'db>,
|
|
||||||
|
|
||||||
/// The type we'll use for error messages referring to details of the called signature. For
|
|
||||||
/// calls to functions this will be the same as `callable_type`; for other callable instances
|
|
||||||
/// it may be a `__call__` method.
|
|
||||||
pub(crate) signature_type: Type<'db>,
|
|
||||||
|
|
||||||
/// If this is a callable object (i.e. called via a `__call__` method), the boundness of
|
|
||||||
/// that call method.
|
|
||||||
pub(crate) dunder_call_is_possibly_unbound: bool,
|
|
||||||
|
|
||||||
/// The type of the bound `self` or `cls` parameter if this signature is for a bound method.
|
|
||||||
pub(crate) bound_type: Option<Type<'db>>,
|
|
||||||
|
|
||||||
/// The signatures of each overload of this callable. Will be empty if the type is not
|
/// The signatures of each overload of this callable. Will be empty if the type is not
|
||||||
/// callable.
|
/// callable.
|
||||||
///
|
|
||||||
/// By using `SmallVec`, we avoid an extra heap allocation for the common case of a
|
|
||||||
/// non-overloaded callable.
|
|
||||||
pub(crate) overloads: SmallVec<[Signature<'db>; 1]>,
|
pub(crate) overloads: SmallVec<[Signature<'db>; 1]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> CallableSignature<'db> {
|
impl<'db> CallableSignature<'db> {
|
||||||
pub(crate) fn not_callable(signature_type: Type<'db>) -> Self {
|
pub(crate) fn single(signature: Signature<'db>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
callable_type: signature_type,
|
|
||||||
signature_type,
|
|
||||||
dunder_call_is_possibly_unbound: false,
|
|
||||||
bound_type: None,
|
|
||||||
overloads: smallvec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn single(signature_type: Type<'db>, signature: Signature<'db>) -> Self {
|
|
||||||
Self {
|
|
||||||
callable_type: signature_type,
|
|
||||||
signature_type,
|
|
||||||
dunder_call_is_possibly_unbound: false,
|
|
||||||
bound_type: None,
|
|
||||||
overloads: smallvec![signature],
|
overloads: smallvec![signature],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `CallableSignature` from an iterator of [`Signature`]s. Returns a
|
/// Creates a new `CallableSignature` from an iterator of [`Signature`]s. Returns a
|
||||||
/// non-callable signature if the iterator is empty.
|
/// non-callable signature if the iterator is empty.
|
||||||
pub(crate) fn from_overloads<I>(signature_type: Type<'db>, overloads: I) -> Self
|
pub(crate) fn from_overloads<I>(overloads: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = Signature<'db>>,
|
I: IntoIterator<Item = Signature<'db>>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
callable_type: signature_type,
|
|
||||||
signature_type,
|
|
||||||
dunder_call_is_possibly_unbound: false,
|
|
||||||
bound_type: None,
|
|
||||||
overloads: overloads.into_iter().collect(),
|
overloads: overloads.into_iter().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a signature for a dynamic callable
|
|
||||||
pub(crate) fn dynamic(signature_type: Type<'db>) -> Self {
|
|
||||||
let signature = Signature {
|
|
||||||
generic_context: None,
|
|
||||||
inherited_generic_context: None,
|
|
||||||
parameters: Parameters::gradual_form(),
|
|
||||||
return_ty: Some(signature_type),
|
|
||||||
};
|
|
||||||
Self::single(signature_type, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a todo signature: (*args: Todo, **kwargs: Todo) -> Todo
|
|
||||||
#[allow(unused_variables)] // 'reason' only unused in debug builds
|
|
||||||
pub(crate) fn todo(reason: &'static str) -> Self {
|
|
||||||
let signature_type = todo_type!(reason);
|
|
||||||
let signature = Signature {
|
|
||||||
generic_context: None,
|
|
||||||
inherited_generic_context: None,
|
|
||||||
parameters: Parameters::todo(),
|
|
||||||
return_ty: Some(signature_type),
|
|
||||||
};
|
|
||||||
Self::single(signature_type, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn with_bound_type(mut self, bound_type: Type<'db>) -> Self {
|
|
||||||
self.bound_type = Some(bound_type);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> std::slice::Iter<'_, Signature<'db>> {
|
pub(crate) fn iter(&self) -> std::slice::Iter<'_, Signature<'db>> {
|
||||||
self.overloads.iter()
|
self.overloads.iter()
|
||||||
}
|
}
|
||||||
|
@ -199,9 +57,169 @@ impl<'db> CallableSignature<'db> {
|
||||||
self.overloads.as_slice()
|
self.overloads.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_callable_type(&mut self, before: Type<'db>, after: Type<'db>) {
|
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||||
if self.callable_type == before {
|
Self::from_overloads(
|
||||||
self.callable_type = after;
|
self.overloads
|
||||||
|
.iter()
|
||||||
|
.map(|signature| signature.normalized(db)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn apply_type_mapping<'a>(
|
||||||
|
&self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
) -> Self {
|
||||||
|
Self::from_overloads(
|
||||||
|
self.overloads
|
||||||
|
.iter()
|
||||||
|
.map(|signature| signature.apply_type_mapping(db, type_mapping)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_legacy_typevars(
|
||||||
|
&self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||||
|
) {
|
||||||
|
for signature in &self.overloads {
|
||||||
|
signature.find_legacy_typevars(db, typevars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bind_self(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
overloads: self.overloads.iter().map(Signature::bind_self).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this callable type is fully static.
|
||||||
|
///
|
||||||
|
/// See [`Type::is_fully_static`] for more details.
|
||||||
|
pub(crate) fn is_fully_static(&self, db: &'db dyn Db) -> bool {
|
||||||
|
self.overloads
|
||||||
|
.iter()
|
||||||
|
.all(|signature| signature.is_fully_static(db))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this callable type is a subtype of another callable type.
|
||||||
|
///
|
||||||
|
/// See [`Type::is_subtype_of`] for more details.
|
||||||
|
pub(crate) fn is_subtype_of(&self, db: &'db dyn Db, other: &Self) -> bool {
|
||||||
|
Self::is_assignable_to_impl(
|
||||||
|
&self.overloads,
|
||||||
|
&other.overloads,
|
||||||
|
&|self_signature, other_signature| self_signature.is_subtype_of(db, other_signature),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this callable type is assignable to another callable type.
|
||||||
|
///
|
||||||
|
/// See [`Type::is_assignable_to`] for more details.
|
||||||
|
pub(crate) fn is_assignable_to(&self, db: &'db dyn Db, other: &Self) -> bool {
|
||||||
|
Self::is_assignable_to_impl(
|
||||||
|
&self.overloads,
|
||||||
|
&other.overloads,
|
||||||
|
&|self_signature, other_signature| self_signature.is_assignable_to(db, other_signature),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for the various relation checks between two, possible overloaded, callable
|
||||||
|
/// types.
|
||||||
|
///
|
||||||
|
/// The `check_signature` closure is used to check the relation between two [`Signature`]s.
|
||||||
|
fn is_assignable_to_impl<F>(
|
||||||
|
self_signatures: &[Signature<'db>],
|
||||||
|
other_signatures: &[Signature<'db>],
|
||||||
|
check_signature: &F,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
F: Fn(&Signature<'db>, &Signature<'db>) -> bool,
|
||||||
|
{
|
||||||
|
match (self_signatures, other_signatures) {
|
||||||
|
([self_signature], [other_signature]) => {
|
||||||
|
// Base case: both callable types contain a single signature.
|
||||||
|
check_signature(self_signature, other_signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `self` is possibly overloaded while `other` is definitely not overloaded.
|
||||||
|
(_, [_]) => self_signatures.iter().any(|self_signature| {
|
||||||
|
Self::is_assignable_to_impl(
|
||||||
|
std::slice::from_ref(self_signature),
|
||||||
|
other_signatures,
|
||||||
|
check_signature,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
// `self` is definitely not overloaded while `other` is possibly overloaded.
|
||||||
|
([_], _) => other_signatures.iter().all(|other_signature| {
|
||||||
|
Self::is_assignable_to_impl(
|
||||||
|
self_signatures,
|
||||||
|
std::slice::from_ref(other_signature),
|
||||||
|
check_signature,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
// `self` is definitely overloaded while `other` is possibly overloaded.
|
||||||
|
(_, _) => other_signatures.iter().all(|other_signature| {
|
||||||
|
Self::is_assignable_to_impl(
|
||||||
|
self_signatures,
|
||||||
|
std::slice::from_ref(other_signature),
|
||||||
|
check_signature,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this callable type is equivalent to another callable type.
|
||||||
|
///
|
||||||
|
/// See [`Type::is_equivalent_to`] for more details.
|
||||||
|
pub(crate) fn is_equivalent_to(&self, db: &'db dyn Db, other: &Self) -> bool {
|
||||||
|
match (self.overloads.as_slice(), other.overloads.as_slice()) {
|
||||||
|
([self_signature], [other_signature]) => {
|
||||||
|
// Common case: both callable types contain a single signature, use the custom
|
||||||
|
// equivalence check instead of delegating it to the subtype check.
|
||||||
|
self_signature.is_equivalent_to(db, other_signature)
|
||||||
|
}
|
||||||
|
(self_signatures, other_signatures) => {
|
||||||
|
if !self_signatures
|
||||||
|
.iter()
|
||||||
|
.chain(other_signatures.iter())
|
||||||
|
.all(|signature| signature.is_fully_static(db))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.is_subtype_of(db, other) && other.is_subtype_of(db, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this callable type is gradual equivalent to another callable type.
|
||||||
|
///
|
||||||
|
/// See [`Type::is_gradual_equivalent_to`] for more details.
|
||||||
|
pub(crate) fn is_gradual_equivalent_to(&self, db: &'db dyn Db, other: &Self) -> bool {
|
||||||
|
match (self.overloads.as_slice(), other.overloads.as_slice()) {
|
||||||
|
([self_signature], [other_signature]) => {
|
||||||
|
self_signature.is_gradual_equivalent_to(db, other_signature)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// TODO: overloads
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn replace_self_reference(&self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
||||||
|
Self {
|
||||||
|
overloads: self
|
||||||
|
.overloads
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|signature| signature.replace_self_reference(db, class))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +281,28 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a signature for a dynamic callable
|
||||||
|
pub(crate) fn dynamic(signature_type: Type<'db>) -> Self {
|
||||||
|
Signature {
|
||||||
|
generic_context: None,
|
||||||
|
inherited_generic_context: None,
|
||||||
|
parameters: Parameters::gradual_form(),
|
||||||
|
return_ty: Some(signature_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a todo signature: (*args: Todo, **kwargs: Todo) -> Todo
|
||||||
|
#[allow(unused_variables)] // 'reason' only unused in debug builds
|
||||||
|
pub(crate) fn todo(reason: &'static str) -> Self {
|
||||||
|
let signature_type = todo_type!(reason);
|
||||||
|
Signature {
|
||||||
|
generic_context: None,
|
||||||
|
inherited_generic_context: None,
|
||||||
|
parameters: Parameters::todo(),
|
||||||
|
return_ty: Some(signature_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a typed signature from a function definition.
|
/// Return a typed signature from a function definition.
|
||||||
pub(super) fn from_function(
|
pub(super) fn from_function(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
@ -295,6 +335,11 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the signature which accepts any parameters and returns an `Unknown` type.
|
||||||
|
pub(crate) fn unknown() -> Self {
|
||||||
|
Self::new(Parameters::unknown(), Some(Type::unknown()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the "bottom" signature, subtype of all other fully-static signatures.
|
/// Return the "bottom" signature, subtype of all other fully-static signatures.
|
||||||
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
||||||
Self::new(Parameters::object(db), Some(Type::Never))
|
Self::new(Parameters::object(db), Some(Type::Never))
|
||||||
|
@ -1750,7 +1795,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
func.signature(&db),
|
func.signature(&db),
|
||||||
&FunctionSignature {
|
&FunctionSignature {
|
||||||
overloads: CallableSignature::single(Type::FunctionLiteral(func), expected_sig),
|
overloads: CallableSignature::single(expected_sig),
|
||||||
implementation: None
|
implementation: None
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue