mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:26 +00:00
[ty] Add into_callable method for Type (#19130)
Some checks are pending
[ty Playground] Release / publish (push) Waiting to run
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 / mkdocs (push) Waiting to run
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 / 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-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
Some checks are pending
[ty Playground] Release / publish (push) Waiting to run
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 / mkdocs (push) Waiting to run
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 / 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-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
## Summary Was just playing around with this, there's definitely more to do with this function, but it seems like maybe a better option than having so many arms in has_relation_to for (_, Callable). --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
parent
333191b7f7
commit
3be83d36a5
1 changed files with 74 additions and 50 deletions
|
@ -1095,6 +1095,74 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_callable(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
match self {
|
||||
Type::Callable(_) => Some(self),
|
||||
|
||||
Type::Dynamic(_) => Some(CallableType::single(db, Signature::dynamic(self))),
|
||||
|
||||
Type::FunctionLiteral(function_literal) => {
|
||||
Some(function_literal.into_callable_type(db))
|
||||
}
|
||||
Type::BoundMethod(bound_method) => Some(bound_method.into_callable_type(db)),
|
||||
|
||||
Type::NominalInstance(_) | Type::ProtocolInstance(_) => {
|
||||
let call_symbol = self
|
||||
.member_lookup_with_policy(
|
||||
db,
|
||||
Name::new_static("__call__"),
|
||||
MemberLookupPolicy::NO_INSTANCE_FALLBACK,
|
||||
)
|
||||
.place;
|
||||
|
||||
if let Place::Type(ty, Boundness::Bound) = call_symbol {
|
||||
ty.into_callable(db)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Type::ClassLiteral(class_literal) => {
|
||||
Some(ClassType::NonGeneric(class_literal).into_callable(db))
|
||||
}
|
||||
|
||||
Type::GenericAlias(alias) => Some(ClassType::Generic(alias).into_callable(db)),
|
||||
|
||||
// TODO: This is unsound so in future we can consider an opt-in option to disable it.
|
||||
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Class(class) => Some(class.into_callable(db)),
|
||||
SubclassOfInner::Dynamic(dynamic) => Some(CallableType::single(
|
||||
db,
|
||||
Signature::new(Parameters::unknown(), Some(Type::Dynamic(dynamic))),
|
||||
)),
|
||||
},
|
||||
|
||||
Type::Union(union) => union.try_map(db, |element| element.into_callable(db)),
|
||||
|
||||
Type::Never
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::IntLiteral(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::LiteralString
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::Tuple(_)
|
||||
| Type::TypeIs(_) => None,
|
||||
|
||||
// TODO
|
||||
Type::MethodWrapper(_)
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::Intersection(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::BoundSuper(_) => None,
|
||||
}
|
||||
}
|
||||
/// 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
|
||||
|
@ -1305,24 +1373,14 @@ impl<'db> Type<'db> {
|
|||
| Type::ModuleLiteral(_),
|
||||
) => false,
|
||||
|
||||
(Type::NominalInstance(_) | Type::ProtocolInstance(_), Type::Callable(_)) => {
|
||||
let call_symbol = self
|
||||
.member_lookup_with_policy(
|
||||
db,
|
||||
Name::new_static("__call__"),
|
||||
MemberLookupPolicy::NO_INSTANCE_FALLBACK,
|
||||
)
|
||||
.place;
|
||||
// If the type of __call__ is a subtype of a callable type, this instance is.
|
||||
// Don't add other special cases here; our subtyping of a callable type
|
||||
// shouldn't get out of sync with the calls we will actually allow.
|
||||
if let Place::Type(t, Boundness::Bound) = call_symbol {
|
||||
t.has_relation_to(db, target, relation)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
(Type::Callable(self_callable), Type::Callable(other_callable)) => {
|
||||
self_callable.has_relation_to(db, other_callable, relation)
|
||||
}
|
||||
|
||||
(_, Type::Callable(_)) => self
|
||||
.into_callable(db)
|
||||
.is_some_and(|callable| callable.has_relation_to(db, target, relation)),
|
||||
|
||||
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => {
|
||||
left.has_relation_to(db, right, relation)
|
||||
}
|
||||
|
@ -1349,16 +1407,6 @@ impl<'db> Type<'db> {
|
|||
) => (self.literal_fallback_instance(db))
|
||||
.is_some_and(|instance| instance.has_relation_to(db, target, relation)),
|
||||
|
||||
(Type::FunctionLiteral(self_function_literal), Type::Callable(_)) => {
|
||||
self_function_literal
|
||||
.into_callable_type(db)
|
||||
.has_relation_to(db, target, relation)
|
||||
}
|
||||
|
||||
(Type::BoundMethod(self_bound_method), Type::Callable(_)) => self_bound_method
|
||||
.into_callable_type(db)
|
||||
.has_relation_to(db, target, relation),
|
||||
|
||||
// A `FunctionLiteral` type is a single-valued type like the other literals handled above,
|
||||
// so it also, for now, just delegates to its instance fallback.
|
||||
(Type::FunctionLiteral(_), _) => KnownClass::FunctionType
|
||||
|
@ -1376,10 +1424,6 @@ impl<'db> Type<'db> {
|
|||
.to_instance(db)
|
||||
.has_relation_to(db, target, relation),
|
||||
|
||||
(Type::Callable(self_callable), Type::Callable(other_callable)) => {
|
||||
self_callable.has_relation_to(db, other_callable, relation)
|
||||
}
|
||||
|
||||
(Type::DataclassDecorator(_) | Type::DataclassTransformer(_), _) => {
|
||||
// TODO: Implement subtyping using an equivalent `Callable` type.
|
||||
false
|
||||
|
@ -1456,26 +1500,6 @@ impl<'db> Type<'db> {
|
|||
self_subclass_ty.has_relation_to(db, target_subclass_ty, relation)
|
||||
}
|
||||
|
||||
(Type::ClassLiteral(class_literal), Type::Callable(_)) => {
|
||||
ClassType::NonGeneric(class_literal)
|
||||
.into_callable(db)
|
||||
.has_relation_to(db, target, relation)
|
||||
}
|
||||
|
||||
(Type::GenericAlias(alias), Type::Callable(_)) => ClassType::Generic(alias)
|
||||
.into_callable(db)
|
||||
.has_relation_to(db, target, relation),
|
||||
|
||||
// TODO: This is unsound so in future we can consider an opt-in option to disable it.
|
||||
(Type::SubclassOf(subclass_of_ty), Type::Callable(_))
|
||||
if subclass_of_ty.subclass_of().into_class().is_some() =>
|
||||
{
|
||||
let class = subclass_of_ty.subclass_of().into_class().unwrap();
|
||||
class
|
||||
.into_callable(db)
|
||||
.has_relation_to(db, target, relation)
|
||||
}
|
||||
|
||||
// `Literal[str]` is a subtype of `type` because the `str` class object is an instance of its metaclass `type`.
|
||||
// `Literal[abc.ABC]` is a subtype of `abc.ABCMeta` because the `abc.ABC` class object
|
||||
// is an instance of its metaclass `abc.ABCMeta`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue