mirror of https://github.com/astral-sh/ruff
Make Self an inferrable TypeVar
This commit is contained in:
parent
4064fa28fc
commit
a66add41be
|
|
@ -79,8 +79,7 @@ class A:
|
|||
def static(x): ...
|
||||
|
||||
a = A()
|
||||
# TODO: Should reveal Self@implicit_self. Requires implicit self in method body(https://github.com/astral-sh/ruff/pull/18473)
|
||||
reveal_type(a.implicit_self()) # revealed: Unknown
|
||||
reveal_type(a.implicit_self()) # revealed: A
|
||||
reveal_type(a.implicit_self) # revealed: bound method A.implicit_self() -> A
|
||||
```
|
||||
|
||||
|
|
@ -130,8 +129,7 @@ class G(Generic[T]):
|
|||
|
||||
g = G[int]()
|
||||
|
||||
# TODO: Should reveal Self@id Requires implicit self in method body(https://github.com/astral-sh/ruff/pull/18473)
|
||||
reveal_type(G[int].id(g)) # revealed: Unknown
|
||||
reveal_type(G[int].id(g)) # revealed: G[int]
|
||||
```
|
||||
|
||||
Free functions and nested functions do not use implicit `Self`:
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ reveal_type(bound_method(1)) # revealed: str
|
|||
When we call the function object itself, we need to pass the `instance` explicitly:
|
||||
|
||||
```py
|
||||
C.f(1) # error: [missing-argument]
|
||||
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Argument type `Literal[1]` does not satisfy upper bound of type variable `Self`"
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of function `f`"
|
||||
C.f(1)
|
||||
|
||||
reveal_type(C.f(C(), 1)) # revealed: str
|
||||
```
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ def _(flag: bool):
|
|||
reveal_type(C7.union_of_class_data_descriptor_and_attribute) # revealed: Literal["data", 2]
|
||||
|
||||
C7.union_of_metaclass_attributes = 2 if flag else 1
|
||||
# error: [invalid-assignment] "Invalid assignment to data descriptor attribute `union_of_metaclass_data_descriptor_and_attribute` on type `<class 'C7'>` with custom `__set__` method"
|
||||
C7.union_of_metaclass_data_descriptor_and_attribute = 2 if flag else 100
|
||||
C7.union_of_class_attributes = 2 if flag else 1
|
||||
C7.union_of_class_data_descriptor_and_attribute = 2 if flag else DataDescriptor()
|
||||
|
|
|
|||
|
|
@ -117,7 +117,9 @@ reveal_type(bound_method.__func__) # revealed: def f(self, x: int) -> str
|
|||
reveal_type(C[int]().f(1)) # revealed: str
|
||||
reveal_type(bound_method(1)) # revealed: str
|
||||
|
||||
C[int].f(1) # error: [missing-argument]
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of function `f`"
|
||||
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Argument type `Literal[1]` does not satisfy upper bound of type variable `Self`"
|
||||
C[int].f(1)
|
||||
reveal_type(C[int].f(C[int](), 1)) # revealed: str
|
||||
|
||||
class D[U](C[U]):
|
||||
|
|
|
|||
|
|
@ -277,8 +277,11 @@ reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
|||
|
||||
person = Person("Alice", 42)
|
||||
|
||||
# TODO: should not be an error
|
||||
# error: [invalid-argument-type]
|
||||
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
||||
# TODO: should be `Person` once we support `Self`
|
||||
# TODO: should be `Person` once we support `Self`, should not be an error
|
||||
# error: [invalid-argument-type]
|
||||
reveal_type(person._replace(name="Bob")) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -358,9 +358,11 @@ def _(x: object):
|
|||
if isinstance(x, Invariant):
|
||||
reveal_type(x) # revealed: Top[Invariant[Unknown]]
|
||||
# error: [invalid-argument-type] "Argument to bound method `get` is incorrect: Expected `Self@get`, found `Top[Invariant[Unknown]]`"
|
||||
# error: [invalid-argument-type] "Argument to bound method `get` is incorrect: Argument type `Top[Invariant[Unknown]]` does not satisfy upper bound of type variable `Self`"
|
||||
reveal_type(x.get()) # revealed: object
|
||||
# error: [invalid-argument-type] "Argument to bound method `push` is incorrect: Expected `Never`, found `Literal[42]`"
|
||||
# error: [invalid-argument-type] "Argument to bound method `push` is incorrect: Expected `Self@push`, found `Top[Invariant[Unknown]]`"
|
||||
# error: [invalid-argument-type] "Argument to bound method `push` is incorrect: Argument type `Top[Invariant[Unknown]]` does not satisfy upper bound of type variable `Self`"
|
||||
x.push(42)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -254,8 +254,7 @@ async def long_running_task():
|
|||
|
||||
async def main():
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
# TODO: should be `TaskGroup`
|
||||
reveal_type(tg) # revealed: Unknown
|
||||
reveal_type(tg) # revealed: TaskGroup
|
||||
|
||||
tg.create_task(long_running_task())
|
||||
```
|
||||
|
|
|
|||
|
|
@ -20,13 +20,16 @@ use super::{
|
|||
semantic_index,
|
||||
};
|
||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||
use crate::semantic_index::scope::ScopeId;
|
||||
use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::function::FunctionType;
|
||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
||||
use crate::types::generics::{GenericContext, bind_typevar, walk_generic_context};
|
||||
use crate::types::infer::nearest_enclosing_class;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
|
||||
SpecialFormType, TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
||||
VarianceInferable, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
|
|
@ -1227,6 +1230,42 @@ impl<'db> Parameters<'db> {
|
|||
let is_classmethod = method_type.is_some_and(|f| f.is_classmethod(db));
|
||||
let is_staticmethod = method_type.is_some_and(|f| f.is_staticmethod(db));
|
||||
|
||||
// TODO: remove duplication with Type::in_type_expression
|
||||
let get_self_type = |scope_id: ScopeId, typevar_binding_context| {
|
||||
{
|
||||
let module = parsed_module(db, scope_id.file(db)).load(db);
|
||||
let index = semantic_index(db, scope_id.file(db));
|
||||
let class = nearest_enclosing_class(db, index, scope_id).unwrap();
|
||||
let instance = Type::ClassLiteral(class)
|
||||
.to_instance(db)
|
||||
.expect("nearest_enclosing_class must return type that can be instantiated");
|
||||
let class_definition = class.definition(db);
|
||||
let typevar = TypeVarInstance::new(
|
||||
db,
|
||||
ast::name::Name::new_static("Self"),
|
||||
Some(class_definition),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(instance).into()),
|
||||
// According to the [spec], we can consider `Self`
|
||||
// equivalent to an invariant type variable
|
||||
// [spec]: https://typing.python.org/en/latest/spec/generics.html#self
|
||||
Some(TypeVarVariance::Invariant),
|
||||
None,
|
||||
TypeVarKind::TypingSelf,
|
||||
);
|
||||
Type::TypeVar(
|
||||
bind_typevar(
|
||||
db,
|
||||
&module,
|
||||
index,
|
||||
scope_id.file_scope_id(db),
|
||||
typevar_binding_context,
|
||||
typevar,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let positional_or_keyword = pos_or_keyword_iter.map(|arg| {
|
||||
if is_method
|
||||
&& !is_staticmethod
|
||||
|
|
@ -1234,11 +1273,12 @@ impl<'db> Parameters<'db> {
|
|||
&& arg.parameter.annotation().is_none()
|
||||
&& parameters.index(arg.name().id()) == Some(0)
|
||||
{
|
||||
let implicit_annotation = Type::SpecialForm(SpecialFormType::TypingSelf)
|
||||
.in_type_expression(db, definition.scope(db), Some(definition))
|
||||
.ok();
|
||||
let scope_id = definition.scope(db);
|
||||
let typevar_binding_context = Some(definition);
|
||||
let implicit_annotation = get_self_type(scope_id, typevar_binding_context);
|
||||
|
||||
Parameter {
|
||||
annotated_type: implicit_annotation,
|
||||
annotated_type: Some(implicit_annotation),
|
||||
synthetic_annotation: true,
|
||||
kind: ParameterKind::PositionalOrKeyword {
|
||||
name: arg.parameter.name.id.clone(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue