mirror of https://github.com/astral-sh/ruff
[ty] Support implicit type of `cls` in signatures (#21771)
## Summary Extends https://github.com/astral-sh/ruff/pull/20517 to support the implicit type of `cls` in `@classmethod` signatures. Part of https://github.com/astral-sh/ty/issues/159.
This commit is contained in:
parent
1b44d7e2a7
commit
29bf2cd201
|
|
@ -607,7 +607,7 @@ class X:
|
|||
def __init__(self, val: int): ...
|
||||
def make_another(self) -> Self:
|
||||
reveal_type(self.__new__) # revealed: def __new__(cls) -> Self@__new__
|
||||
return self.__new__(X)
|
||||
return self.__new__(type(self))
|
||||
```
|
||||
|
||||
## Builtin functions and methods
|
||||
|
|
|
|||
|
|
@ -271,8 +271,7 @@ reveal_type(Person._make) # revealed: bound method <class 'Person'>._make(itera
|
|||
reveal_type(Person._asdict) # revealed: def _asdict(self) -> dict[str, Any]
|
||||
reveal_type(Person._replace) # revealed: def _replace(self, **kwargs: Any) -> Self@_replace
|
||||
|
||||
# TODO: should be `Person` once we support implicit type of `self`
|
||||
reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
||||
reveal_type(Person._make(("Alice", 42))) # revealed: Person
|
||||
|
||||
person = Person("Alice", 42)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ use ruff_python_ast::ParameterWithDefault;
|
|||
use smallvec::{SmallVec, smallvec_inline};
|
||||
|
||||
use super::{
|
||||
DynamicType, Type, TypeVarVariance, definition_expression_type, infer_definition_types,
|
||||
semantic_index,
|
||||
ClassType, DynamicType, Type, TypeVarVariance, definition_expression_type,
|
||||
infer_definition_types, semantic_index,
|
||||
};
|
||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
|
|
@ -31,8 +31,8 @@ use crate::types::infer::nearest_enclosing_class;
|
|||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableTypeKind, ClassLiteral,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
KnownClass, MaterializationKind, NormalizedVisitor, ParamSpecAttrKind, TypeContext,
|
||||
TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
KnownClass, MaterializationKind, NormalizedVisitor, ParamSpecAttrKind, SubclassOfInner,
|
||||
SubclassOfType, TypeContext, TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
|
@ -1675,8 +1675,8 @@ impl<'db> Parameters<'db> {
|
|||
};
|
||||
|
||||
let method_info = infer_method_information(db, definition);
|
||||
let is_static_or_classmethod =
|
||||
method_info.is_some_and(|f| f.is_staticmethod || f.is_classmethod);
|
||||
let is_staticmethod = method_info.is_some_and(|f| f.is_staticmethod);
|
||||
let is_classmethod = method_info.is_some_and(|f| f.is_classmethod);
|
||||
|
||||
let inferred_annotation = |arg: &ParameterWithDefault| {
|
||||
if let Some(MethodInformation {
|
||||
|
|
@ -1685,7 +1685,7 @@ impl<'db> Parameters<'db> {
|
|||
class_is_generic,
|
||||
..
|
||||
}) = method_info
|
||||
&& !is_static_or_classmethod
|
||||
&& !is_staticmethod
|
||||
&& arg.parameter.annotation().is_none()
|
||||
&& parameters.index(arg.name().id()) == Some(0)
|
||||
{
|
||||
|
|
@ -1700,17 +1700,31 @@ impl<'db> Parameters<'db> {
|
|||
let index = semantic_index(db, scope_id.file(db));
|
||||
let class = nearest_enclosing_class(db, index, scope_id).unwrap();
|
||||
|
||||
Some(
|
||||
typing_self(db, scope_id, typevar_binding_context, class)
|
||||
.map(Type::TypeVar)
|
||||
.expect("We should always find the surrounding class for an implicit self: Self annotation"),
|
||||
)
|
||||
let typing_self = typing_self(db, scope_id, typevar_binding_context, class)
|
||||
.expect("We should always find the surrounding class for an implicit self: Self annotation");
|
||||
|
||||
if is_classmethod {
|
||||
Some(SubclassOfType::from(
|
||||
db,
|
||||
SubclassOfInner::TypeVar(typing_self),
|
||||
))
|
||||
} else {
|
||||
Some(Type::TypeVar(typing_self))
|
||||
}
|
||||
} else {
|
||||
// For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or
|
||||
// have additional type parameters), the implicit `Self` type of the `self` parameter would
|
||||
// be the only type variable, so we can just use the class directly.
|
||||
// have additional type parameters), the implicit `Self` type of the `self`, or the implicit
|
||||
// `type[Self]` type of the `cls` parameter, would be the only type variable, so we can just
|
||||
// use the class directly.
|
||||
if is_classmethod {
|
||||
Some(SubclassOfType::from(
|
||||
db,
|
||||
SubclassOfInner::Class(ClassType::NonGeneric(class_literal)),
|
||||
))
|
||||
} else {
|
||||
Some(class_literal.to_non_generic_instance(db))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue