mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] Ensure various special-cased builtin functions are understood as assignable to Callable
(#20331)
This commit is contained in:
parent
d23cae870e
commit
8a0edf0da8
2 changed files with 83 additions and 81 deletions
|
@ -610,9 +610,6 @@ static_assert(is_assignable_to(types.FunctionType, Callable))
|
||||||
|
|
||||||
# revealed: <wrapper-descriptor `__get__` of `function` objects>
|
# revealed: <wrapper-descriptor `__get__` of `function` objects>
|
||||||
reveal_type(types.FunctionType.__get__)
|
reveal_type(types.FunctionType.__get__)
|
||||||
|
|
||||||
# TODO: should pass
|
|
||||||
# error: [static-assert-error]
|
|
||||||
static_assert(is_assignable_to(TypeOf[types.FunctionType.__get__], Callable))
|
static_assert(is_assignable_to(TypeOf[types.FunctionType.__get__], Callable))
|
||||||
|
|
||||||
# revealed: def f(obj: type) -> None
|
# revealed: def f(obj: type) -> None
|
||||||
|
@ -633,9 +630,6 @@ static_assert(is_assignable_to(TypeOf[f.__call__], Callable))
|
||||||
|
|
||||||
# revealed: <wrapper-descriptor `__get__` of `property` objects>
|
# revealed: <wrapper-descriptor `__get__` of `property` objects>
|
||||||
reveal_type(property.__get__)
|
reveal_type(property.__get__)
|
||||||
|
|
||||||
# TODO: should pass
|
|
||||||
# error: [static-assert-error]
|
|
||||||
static_assert(is_assignable_to(TypeOf[property.__get__], Callable))
|
static_assert(is_assignable_to(TypeOf[property.__get__], Callable))
|
||||||
|
|
||||||
# revealed: property
|
# revealed: property
|
||||||
|
@ -649,9 +643,6 @@ static_assert(is_assignable_to(TypeOf[MyClass.my_property.__get__], Callable))
|
||||||
|
|
||||||
# revealed: <wrapper-descriptor `__set__` of `property` objects>
|
# revealed: <wrapper-descriptor `__set__` of `property` objects>
|
||||||
reveal_type(property.__set__)
|
reveal_type(property.__set__)
|
||||||
|
|
||||||
# TODO: should pass
|
|
||||||
# error: [static-assert-error]
|
|
||||||
static_assert(is_assignable_to(TypeOf[property.__set__], Callable))
|
static_assert(is_assignable_to(TypeOf[property.__set__], Callable))
|
||||||
|
|
||||||
# revealed: <method-wrapper `__set__` of `property` object>
|
# revealed: <method-wrapper `__set__` of `property` object>
|
||||||
|
|
|
@ -1327,6 +1327,12 @@ impl<'db> Type<'db> {
|
||||||
false,
|
false,
|
||||||
))),
|
))),
|
||||||
|
|
||||||
|
Type::WrapperDescriptor(wrapper_descriptor) => Some(Type::Callable(CallableType::new(
|
||||||
|
db,
|
||||||
|
CallableSignature::from_overloads(wrapper_descriptor.signatures(db)),
|
||||||
|
false,
|
||||||
|
))),
|
||||||
|
|
||||||
Type::Never
|
Type::Never
|
||||||
| Type::DataclassTransformer(_)
|
| Type::DataclassTransformer(_)
|
||||||
| Type::AlwaysTruthy
|
| Type::AlwaysTruthy
|
||||||
|
@ -1340,8 +1346,7 @@ impl<'db> Type<'db> {
|
||||||
| Type::TypedDict(_) => None,
|
| Type::TypedDict(_) => None,
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
Type::WrapperDescriptor(_)
|
Type::DataclassDecorator(_)
|
||||||
| Type::DataclassDecorator(_)
|
|
||||||
| Type::ModuleLiteral(_)
|
| Type::ModuleLiteral(_)
|
||||||
| Type::SpecialForm(_)
|
| Type::SpecialForm(_)
|
||||||
| Type::KnownInstance(_)
|
| Type::KnownInstance(_)
|
||||||
|
@ -1352,6 +1357,7 @@ impl<'db> Type<'db> {
|
||||||
| Type::BoundSuper(_) => None,
|
| Type::BoundSuper(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if this type is a [subtype of] type `target`.
|
/// Return true if this type is a [subtype of] type `target`.
|
||||||
///
|
///
|
||||||
/// For fully static types, this means that the set of objects represented by `self` is a
|
/// For fully static types, this means that the set of objects represented by `self` is a
|
||||||
|
@ -4018,77 +4024,9 @@ impl<'db> Type<'db> {
|
||||||
CallableBinding::from_overloads(self, method.signatures(db)).into()
|
CallableBinding::from_overloads(self, method.signatures(db)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::WrapperDescriptor(
|
Type::WrapperDescriptor(wrapper_descriptor) => {
|
||||||
kind @ (WrapperDescriptorKind::FunctionTypeDunderGet
|
CallableBinding::from_overloads(self, wrapper_descriptor.signatures(db)).into()
|
||||||
| WrapperDescriptorKind::PropertyDunderGet),
|
|
||||||
) => {
|
|
||||||
// Here, we also model `types.FunctionType.__get__` (or builtins.property.__get__),
|
|
||||||
// but now we consider a call to this as a function, i.e. we also expect the `self`
|
|
||||||
// argument to be passed in.
|
|
||||||
|
|
||||||
// TODO: Consider merging this signature with the one in the previous match clause,
|
|
||||||
// since the previous one is just this signature with the `self` parameters
|
|
||||||
// removed.
|
|
||||||
let descriptor = match kind {
|
|
||||||
WrapperDescriptorKind::FunctionTypeDunderGet => {
|
|
||||||
KnownClass::FunctionType.to_instance(db)
|
|
||||||
}
|
}
|
||||||
WrapperDescriptorKind::PropertyDunderGet => {
|
|
||||||
KnownClass::Property.to_instance(db)
|
|
||||||
}
|
|
||||||
WrapperDescriptorKind::PropertyDunderSet => {
|
|
||||||
unreachable!("Not part of outer match pattern")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
CallableBinding::from_overloads(
|
|
||||||
self,
|
|
||||||
[
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_only(Some(Name::new_static("self")))
|
|
||||||
.with_annotated_type(descriptor),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("instance")))
|
|
||||||
.with_annotated_type(Type::none(db)),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("owner")))
|
|
||||||
.with_annotated_type(KnownClass::Type.to_instance(db)),
|
|
||||||
]),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_only(Some(Name::new_static("self")))
|
|
||||||
.with_annotated_type(descriptor),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("instance")))
|
|
||||||
.with_annotated_type(Type::object(db)),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("owner")))
|
|
||||||
.with_annotated_type(UnionType::from_elements(
|
|
||||||
db,
|
|
||||||
[KnownClass::Type.to_instance(db), Type::none(db)],
|
|
||||||
))
|
|
||||||
.with_default_type(Type::none(db)),
|
|
||||||
]),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::WrapperDescriptor(WrapperDescriptorKind::PropertyDunderSet) => Binding::single(
|
|
||||||
self,
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_only(Some(Name::new_static("self")))
|
|
||||||
.with_annotated_type(KnownClass::Property.to_instance(db)),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("instance")))
|
|
||||||
.with_annotated_type(Type::object(db)),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("value")))
|
|
||||||
.with_annotated_type(Type::object(db)),
|
|
||||||
]),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.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
|
||||||
|
@ -9417,6 +9355,10 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
//
|
//
|
||||||
// For `builtins.property.__get__`, we use the same signature. The return types are not
|
// For `builtins.property.__get__`, we use the same signature. The return types are not
|
||||||
// specified yet, they will be dynamically added in `Bindings::evaluate_known_cases`.
|
// specified yet, they will be dynamically added in `Bindings::evaluate_known_cases`.
|
||||||
|
//
|
||||||
|
// TODO: Consider merging these synthesized signatures with the ones in
|
||||||
|
// [`WrapperDescriptorKind::signatures`], since this one is just that signature
|
||||||
|
// with the `self` parameters removed.
|
||||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||||
| KnownBoundMethodType::PropertyDunderGet(_) => Either::Left(Either::Left(
|
| KnownBoundMethodType::PropertyDunderGet(_) => Either::Left(Either::Left(
|
||||||
[
|
[
|
||||||
|
@ -9503,6 +9445,75 @@ pub enum WrapperDescriptorKind {
|
||||||
PropertyDunderSet,
|
PropertyDunderSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WrapperDescriptorKind {
|
||||||
|
fn signatures(self, db: &dyn Db) -> impl Iterator<Item = Signature<'_>> {
|
||||||
|
/// Similar to what we do in [`KnownBoundMethod::signatures`],
|
||||||
|
/// here we also model `types.FunctionType.__get__` (or builtins.property.__get__),
|
||||||
|
/// but now we consider a call to this as a function, i.e. we also expect the `self`
|
||||||
|
/// argument to be passed in.
|
||||||
|
///
|
||||||
|
/// TODO: Consider merging these synthesized signatures with the ones in
|
||||||
|
/// [`KnownBoundMethod::signatures`], since that one is just this signature
|
||||||
|
/// with the `self` parameters removed.
|
||||||
|
fn dunder_get_signatures(db: &dyn Db, class: KnownClass) -> [Signature<'_>; 2] {
|
||||||
|
let type_instance = KnownClass::Type.to_instance(db);
|
||||||
|
let none = Type::none(db);
|
||||||
|
let descriptor = class.to_instance(db);
|
||||||
|
[
|
||||||
|
Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("self")))
|
||||||
|
.with_annotated_type(descriptor),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("instance")))
|
||||||
|
.with_annotated_type(none),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("owner")))
|
||||||
|
.with_annotated_type(type_instance),
|
||||||
|
]),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("self")))
|
||||||
|
.with_annotated_type(descriptor),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("instance")))
|
||||||
|
.with_annotated_type(Type::object(db)),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("owner")))
|
||||||
|
.with_annotated_type(UnionType::from_elements(
|
||||||
|
db,
|
||||||
|
[type_instance, none],
|
||||||
|
))
|
||||||
|
.with_default_type(none),
|
||||||
|
]),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
WrapperDescriptorKind::FunctionTypeDunderGet => {
|
||||||
|
Either::Left(dunder_get_signatures(db, KnownClass::FunctionType).into_iter())
|
||||||
|
}
|
||||||
|
WrapperDescriptorKind::PropertyDunderGet => {
|
||||||
|
Either::Left(dunder_get_signatures(db, KnownClass::Property).into_iter())
|
||||||
|
}
|
||||||
|
WrapperDescriptorKind::PropertyDunderSet => {
|
||||||
|
let object = Type::object(db);
|
||||||
|
Either::Right(std::iter::once(Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("self")))
|
||||||
|
.with_annotated_type(KnownClass::Property.to_instance(db)),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("instance")))
|
||||||
|
.with_annotated_type(object),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("value")))
|
||||||
|
.with_annotated_type(object),
|
||||||
|
]),
|
||||||
|
None,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Ordering
|
/// # Ordering
|
||||||
/// Ordering is based on the module literal's salsa-assigned id and not on its values.
|
/// Ordering is based on the module literal's salsa-assigned id and not on its values.
|
||||||
/// The id may change between runs, or when the module literal was garbage collected and recreated.
|
/// The id may change between runs, or when the module literal was garbage collected and recreated.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue