mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Assume type of self is typing.Self
in method calls (#18007)
Part of https://github.com/astral-sh/ty/issues/159 This PR only adjusts the signature of a method so if it has a `self` argument then that argument will have type of `Typing.Self` even if it's not specified. If user provides an explicit annotation then Ty will not override that annotation. - https://github.com/astral-sh/ty/issues/1131 - https://github.com/astral-sh/ty/issues/1157 - https://github.com/astral-sh/ty/issues/1156 - https://github.com/astral-sh/ty/issues/1173 - https://github.com/astral-sh/ruff/pull/20328 - https://github.com/astral-sh/ty/issues/1163 - https://github.com/astral-sh/ty/issues/1196 Added mdtests. Also some tests need https://github.com/astral-sh/ruff/pull/18473 to work completely. So I added a todo for those new cases that I added. --------- Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
parent
24fec9ac1c
commit
3062e71cff
18 changed files with 383 additions and 103 deletions
|
@ -3030,6 +3030,12 @@ impl Parameters {
|
||||||
.find(|arg| arg.parameter.name.as_str() == name)
|
.find(|arg| arg.parameter.name.as_str() == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the index of the parameter with the given name
|
||||||
|
pub fn index(&self, name: &str) -> Option<usize> {
|
||||||
|
self.iter_non_variadic_params()
|
||||||
|
.position(|arg| arg.parameter.name.as_str() == name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all parameters included in this [`Parameters`] node.
|
/// Returns an iterator over all parameters included in this [`Parameters`] node.
|
||||||
pub fn iter(&self) -> ParametersIterator<'_> {
|
pub fn iter(&self) -> ParametersIterator<'_> {
|
||||||
ParametersIterator::new(self)
|
ParametersIterator::new(self)
|
||||||
|
|
|
@ -33,11 +33,6 @@ class Shape:
|
||||||
reveal_type(x) # revealed: Self@nested_func_without_enclosing_binding
|
reveal_type(x) # revealed: Self@nested_func_without_enclosing_binding
|
||||||
inner(self)
|
inner(self)
|
||||||
|
|
||||||
def implicit_self(self) -> Self:
|
|
||||||
# TODO: first argument in a method should be considered as "typing.Self"
|
|
||||||
reveal_type(self) # revealed: Unknown
|
|
||||||
return self
|
|
||||||
|
|
||||||
reveal_type(Shape().nested_type()) # revealed: list[Shape]
|
reveal_type(Shape().nested_type()) # revealed: list[Shape]
|
||||||
reveal_type(Shape().nested_func()) # revealed: Shape
|
reveal_type(Shape().nested_func()) # revealed: Shape
|
||||||
|
|
||||||
|
@ -53,6 +48,104 @@ class Outer:
|
||||||
return self
|
return self
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Detection of implicit Self
|
||||||
|
|
||||||
|
In instance methods, the first parameter (regardless of its name) is assumed to have type
|
||||||
|
`typing.Self` unless it has an explicit annotation. This does not apply to `@classmethod` and
|
||||||
|
`@staticmethod`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.11"
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
class A:
|
||||||
|
def implicit_self(self) -> Self:
|
||||||
|
# TODO: first argument in a method should be considered as "typing.Self"
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
return self
|
||||||
|
|
||||||
|
def foo(self) -> int:
|
||||||
|
def first_arg_is_not_self(a: int) -> int:
|
||||||
|
return a
|
||||||
|
return first_arg_is_not_self(1)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def bar(cls): ...
|
||||||
|
@staticmethod
|
||||||
|
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: A
|
||||||
|
reveal_type(a.implicit_self) # revealed: bound method A.implicit_self() -> A
|
||||||
|
```
|
||||||
|
|
||||||
|
If the method is a class or static method then first argument is not self:
|
||||||
|
|
||||||
|
```py
|
||||||
|
A.bar()
|
||||||
|
a.static(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
"self" name is not special; any first parameter name is treated as Self.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Self, Generic, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class B:
|
||||||
|
def implicit_this(this) -> Self:
|
||||||
|
# TODO: Should reveal Self@implicit_this
|
||||||
|
reveal_type(this) # revealed: Unknown
|
||||||
|
return this
|
||||||
|
|
||||||
|
def ponly(self, /, x: int) -> None:
|
||||||
|
# TODO: Should reveal Self@ponly
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
|
||||||
|
def kwonly(self, *, x: int) -> None:
|
||||||
|
# TODO: Should reveal Self@kwonly
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
# TODO: Should reveal Self@name
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
return "b"
|
||||||
|
|
||||||
|
B.ponly(B(), 1)
|
||||||
|
B.name
|
||||||
|
B.kwonly(B(), x=1)
|
||||||
|
|
||||||
|
class G(Generic[T]):
|
||||||
|
def id(self) -> Self:
|
||||||
|
# TODO: Should reveal Self@id
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
return self
|
||||||
|
|
||||||
|
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: G[int]
|
||||||
|
```
|
||||||
|
|
||||||
|
Free functions and nested functions do not use implicit `Self`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
def not_a_method(self):
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
|
||||||
|
class C:
|
||||||
|
def outer(self) -> None:
|
||||||
|
def inner(self):
|
||||||
|
reveal_type(self) # revealed: Unknown
|
||||||
|
```
|
||||||
|
|
||||||
## typing_extensions
|
## typing_extensions
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
@ -208,6 +301,53 @@ class MyMetaclass(type):
|
||||||
return super().__new__(cls)
|
return super().__new__(cls)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Explicit Annotation Overrides Implicit `Self`
|
||||||
|
|
||||||
|
If the first parameter is explicitly annotated, that annotation takes precedence over the implicit
|
||||||
|
`Self` treatment.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.11"
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Explicit:
|
||||||
|
# TODO: Should warn the user if self is overriden with a type that is not subtype of the class
|
||||||
|
def bad(self: int) -> None:
|
||||||
|
reveal_type(self) # revealed: int
|
||||||
|
|
||||||
|
def forward(self: "Explicit") -> None:
|
||||||
|
reveal_type(self) # revealed: Explicit
|
||||||
|
|
||||||
|
e = Explicit()
|
||||||
|
# error: [invalid-argument-type] "Argument to bound method `bad` is incorrect: Expected `int`, found `Explicit`"
|
||||||
|
e.bad()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type of Implicit Self
|
||||||
|
|
||||||
|
The assigned type to self argument depends on the method signature. When the method is defined in a
|
||||||
|
non-generic class and has no other mention of `typing.Self` (for example in return type) then type
|
||||||
|
of `self` is instance of the class.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
class C:
|
||||||
|
def f(self) -> Self:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def z(self) -> None: ...
|
||||||
|
|
||||||
|
C.z(1) # error: [invalid-argument-type] "Argument to function `z` is incorrect: Expected `C`, found `Literal[1]`"
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Argument type `Literal[1]` does not satisfy upper bound `C` of type variable `Self`"
|
||||||
|
C.f(1)
|
||||||
|
```
|
||||||
|
|
||||||
## Binding a method fixes `Self`
|
## Binding a method fixes `Self`
|
||||||
|
|
||||||
When a method is bound, any instances of `Self` in its signature are "fixed", since we now know the
|
When a method is bound, any instances of `Self` in its signature are "fixed", since we now know the
|
||||||
|
|
|
@ -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:
|
When we call the function object itself, we need to pass the `instance` explicitly:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
C.f(1) # error: [missing-argument]
|
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `C`, found `Literal[1]`"
|
||||||
|
# error: [missing-argument]
|
||||||
|
C.f(1)
|
||||||
|
|
||||||
reveal_type(C.f(C(), 1)) # revealed: str
|
reveal_type(C.f(C(), 1)) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
|
@ -431,6 +431,8 @@ def _(flag: bool):
|
||||||
reveal_type(C7.union_of_class_data_descriptor_and_attribute) # revealed: Literal["data", 2]
|
reveal_type(C7.union_of_class_data_descriptor_and_attribute) # revealed: Literal["data", 2]
|
||||||
|
|
||||||
C7.union_of_metaclass_attributes = 2 if flag else 1
|
C7.union_of_metaclass_attributes = 2 if flag else 1
|
||||||
|
# TODO: https://github.com/astral-sh/ty/issues/1163
|
||||||
|
# error: [invalid-assignment]
|
||||||
C7.union_of_metaclass_data_descriptor_and_attribute = 2 if flag else 100
|
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_attributes = 2 if flag else 1
|
||||||
C7.union_of_class_data_descriptor_and_attribute = 2 if flag else DataDescriptor()
|
C7.union_of_class_data_descriptor_and_attribute = 2 if flag else DataDescriptor()
|
||||||
|
|
|
@ -562,17 +562,17 @@ class C(Generic[T]):
|
||||||
return u
|
return u
|
||||||
|
|
||||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||||
reveal_type(generic_context(C.method)) # revealed: None
|
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
reveal_type(generic_context(C[int])) # revealed: None
|
reveal_type(generic_context(C[int])) # revealed: None
|
||||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
|
|
||||||
c: C[int] = C[int]()
|
c: C[int] = C[int]()
|
||||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||||
reveal_type(generic_context(c)) # revealed: None
|
reveal_type(generic_context(c)) # revealed: None
|
||||||
reveal_type(generic_context(c.method)) # revealed: None
|
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Specializations propagate
|
## Specializations propagate
|
||||||
|
|
|
@ -504,17 +504,17 @@ class C[T]:
|
||||||
def cannot_shadow_class_typevar[T](self, t: T): ...
|
def cannot_shadow_class_typevar[T](self, t: T): ...
|
||||||
|
|
||||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||||
reveal_type(generic_context(C.method)) # revealed: None
|
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
reveal_type(generic_context(C[int])) # revealed: None
|
reveal_type(generic_context(C[int])) # revealed: None
|
||||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
|
|
||||||
c: C[int] = C[int]()
|
c: C[int] = C[int]()
|
||||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||||
reveal_type(generic_context(c)) # revealed: None
|
reveal_type(generic_context(c)) # revealed: None
|
||||||
reveal_type(generic_context(c.method)) # revealed: None
|
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
|
||||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Specializations propagate
|
## Specializations propagate
|
||||||
|
|
|
@ -534,6 +534,5 @@ class C:
|
||||||
def _(x: int):
|
def _(x: int):
|
||||||
reveal_type(C().explicit_self(x)) # revealed: tuple[C, int]
|
reveal_type(C().explicit_self(x)) # revealed: tuple[C, int]
|
||||||
|
|
||||||
# TODO: this should be `tuple[C, int]` as well, once we support implicit `self`
|
reveal_type(C().implicit_self(x)) # revealed: tuple[C, int]
|
||||||
reveal_type(C().implicit_self(x)) # revealed: tuple[Unknown, int]
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -117,6 +117,7 @@ reveal_type(bound_method.__func__) # revealed: def f(self, x: int) -> str
|
||||||
reveal_type(C[int]().f(1)) # revealed: str
|
reveal_type(C[int]().f(1)) # revealed: str
|
||||||
reveal_type(bound_method(1)) # revealed: str
|
reveal_type(bound_method(1)) # revealed: str
|
||||||
|
|
||||||
|
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Argument type `Literal[1]` does not satisfy upper bound `C[Unknown]` of type variable `Self`"
|
||||||
C[int].f(1) # error: [missing-argument]
|
C[int].f(1) # error: [missing-argument]
|
||||||
reveal_type(C[int].f(C[int](), 1)) # revealed: str
|
reveal_type(C[int].f(C[int](), 1)) # revealed: str
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ from ty_extensions import generic_context
|
||||||
legacy.m("string", None) # error: [invalid-argument-type]
|
legacy.m("string", None) # error: [invalid-argument-type]
|
||||||
reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m
|
reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m
|
||||||
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
|
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
|
||||||
reveal_type(generic_context(legacy.m)) # revealed: tuple[S@m]
|
reveal_type(generic_context(legacy.m)) # revealed: tuple[Self@m, S@m]
|
||||||
```
|
```
|
||||||
|
|
||||||
With PEP 695 syntax, it is clearer that the method uses a separate typevar:
|
With PEP 695 syntax, it is clearer that the method uses a separate typevar:
|
||||||
|
|
|
@ -277,9 +277,9 @@ reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
||||||
|
|
||||||
person = Person("Alice", 42)
|
person = Person("Alice", 42)
|
||||||
|
|
||||||
|
# error: [invalid-argument-type] "Argument to bound method `_asdict` is incorrect: Expected `NamedTupleFallback`, found `Person`"
|
||||||
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
||||||
# TODO: should be `Person` once we support implicit type of `self`
|
reveal_type(person._replace(name="Bob")) # revealed: Person
|
||||||
reveal_type(person._replace(name="Bob")) # revealed: Unknown
|
|
||||||
```
|
```
|
||||||
|
|
||||||
When accessing them on child classes of generic `NamedTuple`s, the return type is specialized
|
When accessing them on child classes of generic `NamedTuple`s, the return type is specialized
|
||||||
|
@ -296,8 +296,7 @@ class Box(NamedTuple, Generic[T]):
|
||||||
class IntBox(Box[int]):
|
class IntBox(Box[int]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TODO: should be `IntBox` once we support the implicit type of `self`
|
reveal_type(IntBox(1)._replace(content=42)) # revealed: IntBox
|
||||||
reveal_type(IntBox(1)._replace(content=42)) # revealed: Unknown
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## `collections.namedtuple`
|
## `collections.namedtuple`
|
||||||
|
|
|
@ -363,8 +363,12 @@ class Invariant[T]:
|
||||||
def _(x: object):
|
def _(x: object):
|
||||||
if isinstance(x, Invariant):
|
if isinstance(x, Invariant):
|
||||||
reveal_type(x) # revealed: Top[Invariant[Unknown]]
|
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 `Bottom[Invariant[Unknown]]` of type variable `Self`"
|
||||||
reveal_type(x.get()) # revealed: object
|
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 `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 `Bottom[Invariant[Unknown]]` of type variable `Self`"
|
||||||
x.push(42)
|
x.push(42)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -325,7 +325,7 @@ type A = list[Union["A", str]]
|
||||||
def f(x: A):
|
def f(x: A):
|
||||||
reveal_type(x) # revealed: list[A | str]
|
reveal_type(x) # revealed: list[A | str]
|
||||||
for item in x:
|
for item in x:
|
||||||
reveal_type(item) # revealed: list[A | str] | str
|
reveal_type(item) # revealed: list[Any | str] | str
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With new-style union
|
#### With new-style union
|
||||||
|
@ -336,7 +336,7 @@ type A = list["A" | str]
|
||||||
def f(x: A):
|
def f(x: A):
|
||||||
reveal_type(x) # revealed: list[A | str]
|
reveal_type(x) # revealed: list[A | str]
|
||||||
for item in x:
|
for item in x:
|
||||||
reveal_type(item) # revealed: list[A | str] | str
|
reveal_type(item) # revealed: list[Any | str] | str
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With Optional
|
#### With Optional
|
||||||
|
@ -349,7 +349,7 @@ type A = list[Optional[Union["A", str]]]
|
||||||
def f(x: A):
|
def f(x: A):
|
||||||
reveal_type(x) # revealed: list[A | str | None]
|
reveal_type(x) # revealed: list[A | str | None]
|
||||||
for item in x:
|
for item in x:
|
||||||
reveal_type(item) # revealed: list[A | str | None] | str | None
|
reveal_type(item) # revealed: list[Any | str | None] | str | None
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tuple comparison
|
### Tuple comparison
|
||||||
|
|
|
@ -1948,8 +1948,6 @@ static_assert(is_subtype_of(TypeOf[A.g], Callable[[int], int]))
|
||||||
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[float], int]))
|
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[float], int]))
|
||||||
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[], int]))
|
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[], int]))
|
||||||
|
|
||||||
# TODO: This assertion should be true
|
|
||||||
# error: [static-assert-error] "Static assertion error: argument of type `ty_extensions.ConstraintSet[never]` is statically known to be falsy"
|
|
||||||
static_assert(is_subtype_of(TypeOf[A.f], Callable[[A, int], int]))
|
static_assert(is_subtype_of(TypeOf[A.f], Callable[[A, int], int]))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -657,15 +657,13 @@ alice: Employee = {"name": "Alice", "employee_id": 1}
|
||||||
eve: Employee = {"name": "Eve"}
|
eve: Employee = {"name": "Eve"}
|
||||||
|
|
||||||
def combine(p: Person, e: Employee):
|
def combine(p: Person, e: Employee):
|
||||||
# TODO: Should be `Person` once we support the implicit type of self
|
reveal_type(p.copy()) # revealed: Person
|
||||||
reveal_type(p.copy()) # revealed: Unknown
|
reveal_type(e.copy()) # revealed: Employee
|
||||||
# TODO: Should be `Employee` once we support the implicit type of self
|
|
||||||
reveal_type(e.copy()) # revealed: Unknown
|
|
||||||
|
|
||||||
reveal_type(p | p) # revealed: Person
|
reveal_type(p | p) # revealed: Person
|
||||||
reveal_type(e | e) # revealed: Employee
|
reveal_type(e | e) # revealed: Employee
|
||||||
|
|
||||||
# TODO: Should be `Person` once we support the implicit type of self and subtyping for TypedDicts
|
# TODO: Should be `Person` once we support subtyping for TypedDicts
|
||||||
reveal_type(p | e) # revealed: Employee
|
reveal_type(p | e) # revealed: Employee
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -254,8 +254,7 @@ async def long_running_task():
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
# TODO: should be `TaskGroup`
|
reveal_type(tg) # revealed: TaskGroup
|
||||||
reveal_type(tg) # revealed: Unknown
|
|
||||||
|
|
||||||
tg.create_task(long_running_task())
|
tg.create_task(long_running_task())
|
||||||
```
|
```
|
||||||
|
|
|
@ -52,8 +52,8 @@ use crate::types::function::{
|
||||||
DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction,
|
DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction,
|
||||||
};
|
};
|
||||||
use crate::types::generics::{
|
use crate::types::generics::{
|
||||||
GenericContext, PartialSpecialization, Specialization, bind_typevar, walk_generic_context,
|
GenericContext, PartialSpecialization, Specialization, bind_typevar, get_self_type,
|
||||||
walk_partial_specialization, walk_specialization,
|
walk_generic_context, walk_partial_specialization, walk_specialization,
|
||||||
};
|
};
|
||||||
pub use crate::types::ide_support::{
|
pub use crate::types::ide_support::{
|
||||||
CallSignatureDetails, Member, MemberWithDefinition, all_members, call_signature_details,
|
CallSignatureDetails, Member, MemberWithDefinition, all_members, call_signature_details,
|
||||||
|
@ -5703,7 +5703,6 @@ impl<'db> Type<'db> {
|
||||||
.build()),
|
.build()),
|
||||||
|
|
||||||
SpecialFormType::TypingSelf => {
|
SpecialFormType::TypingSelf => {
|
||||||
let module = parsed_module(db, scope_id.file(db)).load(db);
|
|
||||||
let index = semantic_index(db, scope_id.file(db));
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
let Some(class) = nearest_enclosing_class(db, index, scope_id) else {
|
let Some(class) = nearest_enclosing_class(db, index, scope_id) else {
|
||||||
return Err(InvalidTypeExpressionError {
|
return Err(InvalidTypeExpressionError {
|
||||||
|
@ -5713,42 +5712,8 @@ impl<'db> Type<'db> {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let self_type = get_self_type(db, scope_id, typevar_binding_context, class);
|
||||||
let upper_bound = Type::instance(
|
Ok(self_type.map(Type::NonInferableTypeVar).unwrap_or(*self))
|
||||||
db,
|
|
||||||
class.apply_specialization(db, |generic_context| {
|
|
||||||
let types = generic_context
|
|
||||||
.variables(db)
|
|
||||||
.iter()
|
|
||||||
.map(|typevar| Type::NonInferableTypeVar(*typevar));
|
|
||||||
|
|
||||||
generic_context.specialize(db, types.collect())
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let class_definition = class.definition(db);
|
|
||||||
let typevar = TypeVarInstance::new(
|
|
||||||
db,
|
|
||||||
ast::name::Name::new_static("Self"),
|
|
||||||
Some(class_definition),
|
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).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,
|
|
||||||
);
|
|
||||||
Ok(bind_typevar(
|
|
||||||
db,
|
|
||||||
&module,
|
|
||||||
index,
|
|
||||||
scope_id.file_scope_id(db),
|
|
||||||
typevar_binding_context,
|
|
||||||
typevar,
|
|
||||||
)
|
|
||||||
.map(Type::NonInferableTypeVar)
|
|
||||||
.unwrap_or(*self))
|
|
||||||
}
|
}
|
||||||
SpecialFormType::TypeAlias => Ok(Type::Dynamic(DynamicType::TodoTypeAlias)),
|
SpecialFormType::TypeAlias => Ok(Type::Dynamic(DynamicType::TodoTypeAlias)),
|
||||||
SpecialFormType::TypedDict => Err(InvalidTypeExpressionError {
|
SpecialFormType::TypedDict => Err(InvalidTypeExpressionError {
|
||||||
|
|
|
@ -1149,12 +1149,14 @@ impl Display for DisplayParameter<'_> {
|
||||||
if let Some(name) = self.param.display_name() {
|
if let Some(name) = self.param.display_name() {
|
||||||
f.write_str(&name)?;
|
f.write_str(&name)?;
|
||||||
if let Some(annotated_type) = self.param.annotated_type() {
|
if let Some(annotated_type) = self.param.annotated_type() {
|
||||||
|
if !self.param.has_synthetic_annotation() {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
": {}",
|
": {}",
|
||||||
annotated_type.display_with(self.db, self.settings)
|
annotated_type.display_with(self.db, self.settings)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Default value can only be specified if `name` is given.
|
// Default value can only be specified if `name` is given.
|
||||||
if let Some(default_ty) = self.param.default_type() {
|
if let Some(default_ty) = self.param.default_type() {
|
||||||
if self.param.annotated_type().is_some() {
|
if self.param.annotated_type().is_some() {
|
||||||
|
@ -1166,8 +1168,10 @@ impl Display for DisplayParameter<'_> {
|
||||||
} else if let Some(ty) = self.param.annotated_type() {
|
} else if let Some(ty) = self.param.annotated_type() {
|
||||||
// This case is specifically for the `Callable` signature where name and default value
|
// This case is specifically for the `Callable` signature where name and default value
|
||||||
// cannot be provided.
|
// cannot be provided.
|
||||||
|
if !self.param.has_synthetic_annotation() {
|
||||||
ty.display_with(self.db, self.settings).fmt(f)?;
|
ty.display_with(self.db, self.settings).fmt(f)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ use std::borrow::Cow;
|
||||||
use crate::types::constraints::ConstraintSet;
|
use crate::types::constraints::ConstraintSet;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_db::parsed::ParsedModuleRef;
|
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::semantic_index::SemanticIndex;
|
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind};
|
use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId};
|
||||||
|
use crate::semantic_index::{SemanticIndex, semantic_index};
|
||||||
use crate::types::class::ClassType;
|
use crate::types::class::ClassType;
|
||||||
use crate::types::class_base::ClassBase;
|
use crate::types::class_base::ClassBase;
|
||||||
use crate::types::infer::infer_definition_types;
|
use crate::types::infer::infer_definition_types;
|
||||||
|
@ -17,10 +17,10 @@ use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||||
IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor,
|
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind,
|
||||||
Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
NormalizedVisitor, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||||
TypeVarVariance, UnionType, binding_type, declaration_type,
|
TypeVarKind, TypeVarVariance, UnionType, binding_type, declaration_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
@ -103,6 +103,51 @@ pub(crate) fn bind_typevar<'db>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_self_type<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
scope_id: ScopeId,
|
||||||
|
typevar_binding_context: Option<Definition<'db>>,
|
||||||
|
class: ClassLiteral<'db>,
|
||||||
|
) -> Option<BoundTypeVarInstance<'db>> {
|
||||||
|
// TODO: remove duplication with Type::in_type_expression
|
||||||
|
let module = parsed_module(db, scope_id.file(db)).load(db);
|
||||||
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
|
|
||||||
|
let upper_bound = Type::instance(
|
||||||
|
db,
|
||||||
|
class.apply_specialization(db, |generic_context| {
|
||||||
|
let types = generic_context
|
||||||
|
.variables(db)
|
||||||
|
.iter()
|
||||||
|
.map(|typevar| Type::NonInferableTypeVar(*typevar));
|
||||||
|
|
||||||
|
generic_context.specialize(db, types.collect())
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let class_definition = class.definition(db);
|
||||||
|
let typevar = TypeVarInstance::new(
|
||||||
|
db,
|
||||||
|
ast::name::Name::new_static("Self"),
|
||||||
|
Some(class_definition),
|
||||||
|
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).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,
|
||||||
|
);
|
||||||
|
bind_typevar(
|
||||||
|
db,
|
||||||
|
&module,
|
||||||
|
index,
|
||||||
|
scope_id.file_scope_id(db),
|
||||||
|
typevar_binding_context,
|
||||||
|
typevar,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// A list of formal type variables for a generic function, class, or type alias.
|
/// A list of formal type variables for a generic function, class, or type alias.
|
||||||
///
|
///
|
||||||
/// TODO: Handle nested generic contexts better, with actual parent links to the lexically
|
/// TODO: Handle nested generic contexts better, with actual parent links to the lexically
|
||||||
|
|
|
@ -15,18 +15,58 @@ use std::{collections::HashMap, slice::Iter};
|
||||||
use itertools::{EitherOrBoth, Itertools};
|
use itertools::{EitherOrBoth, Itertools};
|
||||||
use smallvec::{SmallVec, smallvec_inline};
|
use smallvec::{SmallVec, smallvec_inline};
|
||||||
|
|
||||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
use super::{
|
||||||
|
DynamicType, Type, TypeVarVariance, definition_expression_type, infer_definition_types,
|
||||||
|
semantic_index,
|
||||||
|
};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
use crate::types::function::FunctionType;
|
||||||
|
use crate::types::generics::{GenericContext, get_self_type, walk_generic_context};
|
||||||
|
use crate::types::infer::nearest_enclosing_class;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
|
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassType,
|
||||||
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
|
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass,
|
||||||
TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
MaterializationKind, NormalizedVisitor, TypeMapping, TypeRelation, TypeVarKind,
|
||||||
|
VarianceInferable, 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};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct MethodInformation<'db> {
|
||||||
|
method_type: FunctionType<'db>,
|
||||||
|
class_type: ClassType<'db>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_method_information<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) -> Option<MethodInformation<'db>> {
|
||||||
|
let class_scope_id = definition.scope(db);
|
||||||
|
let file = class_scope_id.file(db);
|
||||||
|
let index = semantic_index(db, file);
|
||||||
|
|
||||||
|
let class_scope = index.scope(class_scope_id.file_scope_id(db));
|
||||||
|
let class_node = class_scope.node().as_class()?;
|
||||||
|
|
||||||
|
let method_type = infer_definition_types(db, definition)
|
||||||
|
.declaration_type(definition)
|
||||||
|
.inner_type()
|
||||||
|
.into_function_literal()?;
|
||||||
|
|
||||||
|
let class_def = index.expect_single_definition(class_node);
|
||||||
|
let class_literal = infer_definition_types(db, class_def)
|
||||||
|
.declaration_type(class_def)
|
||||||
|
.inner_type();
|
||||||
|
let class_type = class_literal.to_class_type(db)?;
|
||||||
|
|
||||||
|
Some(MethodInformation {
|
||||||
|
method_type,
|
||||||
|
class_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// 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, get_size2::GetSize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||||
|
@ -1219,8 +1259,56 @@ impl<'db> Parameters<'db> {
|
||||||
.map(pos_only_param),
|
.map(pos_only_param),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
let method_info = infer_method_information(db, definition);
|
||||||
|
let is_classmethod = method_info.is_some_and(|f| f.method_type.is_classmethod(db));
|
||||||
|
let is_staticmethod = method_info.is_some_and(|f| f.method_type.is_staticmethod(db));
|
||||||
|
|
||||||
let positional_or_keyword = pos_or_keyword_iter.map(|arg| {
|
let positional_or_keyword = pos_or_keyword_iter.map(|arg| {
|
||||||
|
if let Some(MethodInformation {
|
||||||
|
method_type: method,
|
||||||
|
class_type: class,
|
||||||
|
}) = method_info
|
||||||
|
{
|
||||||
|
if !is_staticmethod
|
||||||
|
&& !is_classmethod
|
||||||
|
&& arg.parameter.annotation().is_none()
|
||||||
|
&& parameters.index(arg.name().id()) == Some(0)
|
||||||
|
{
|
||||||
|
let method_has_self_in_generic_context =
|
||||||
|
method.signature(db).overloads.iter().any(|s| {
|
||||||
|
if let Some(context) = s.generic_context {
|
||||||
|
context
|
||||||
|
.variables(db)
|
||||||
|
.iter()
|
||||||
|
.any(|v| v.typevar(db).kind(db) == TypeVarKind::TypingSelf);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let implicit_annotation = if !method_has_self_in_generic_context
|
||||||
|
&& class.is_not_generic()
|
||||||
|
{
|
||||||
|
Type::instance(db, class)
|
||||||
|
} else {
|
||||||
|
let scope_id = definition.scope(db);
|
||||||
|
let typevar_binding_context = Some(definition);
|
||||||
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
|
let class = nearest_enclosing_class(db, index, scope_id).unwrap();
|
||||||
|
Type::TypeVar(
|
||||||
|
get_self_type(db, scope_id, typevar_binding_context, class).unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Parameter {
|
||||||
|
annotated_type: Some(implicit_annotation),
|
||||||
|
synthetic_annotation: true,
|
||||||
|
kind: ParameterKind::PositionalOrKeyword {
|
||||||
|
name: arg.parameter.name.id.clone(),
|
||||||
|
default_type: default_type(arg),
|
||||||
|
},
|
||||||
|
form: ParameterForm::Value,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Parameter::from_node_and_kind(
|
Parameter::from_node_and_kind(
|
||||||
db,
|
db,
|
||||||
definition,
|
definition,
|
||||||
|
@ -1230,6 +1318,18 @@ impl<'db> Parameters<'db> {
|
||||||
default_type: default_type(arg),
|
default_type: default_type(arg),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Parameter::from_node_and_kind(
|
||||||
|
db,
|
||||||
|
definition,
|
||||||
|
&arg.parameter,
|
||||||
|
ParameterKind::PositionalOrKeyword {
|
||||||
|
name: arg.parameter.name.id.clone(),
|
||||||
|
default_type: default_type(arg),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let variadic = vararg.as_ref().map(|arg| {
|
let variadic = vararg.as_ref().map(|arg| {
|
||||||
|
@ -1402,6 +1502,10 @@ pub(crate) struct Parameter<'db> {
|
||||||
/// Annotated type of the parameter.
|
/// Annotated type of the parameter.
|
||||||
annotated_type: Option<Type<'db>>,
|
annotated_type: Option<Type<'db>>,
|
||||||
|
|
||||||
|
/// If the type of parameter was inferred e.g. the first argument of a method has type
|
||||||
|
/// `typing.Self`.
|
||||||
|
synthetic_annotation: bool,
|
||||||
|
|
||||||
kind: ParameterKind<'db>,
|
kind: ParameterKind<'db>,
|
||||||
pub(crate) form: ParameterForm,
|
pub(crate) form: ParameterForm,
|
||||||
}
|
}
|
||||||
|
@ -1410,6 +1514,7 @@ impl<'db> Parameter<'db> {
|
||||||
pub(crate) fn positional_only(name: Option<Name>) -> Self {
|
pub(crate) fn positional_only(name: Option<Name>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: None,
|
annotated_type: None,
|
||||||
|
synthetic_annotation: false,
|
||||||
kind: ParameterKind::PositionalOnly {
|
kind: ParameterKind::PositionalOnly {
|
||||||
name,
|
name,
|
||||||
default_type: None,
|
default_type: None,
|
||||||
|
@ -1421,6 +1526,7 @@ impl<'db> Parameter<'db> {
|
||||||
pub(crate) fn positional_or_keyword(name: Name) -> Self {
|
pub(crate) fn positional_or_keyword(name: Name) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: None,
|
annotated_type: None,
|
||||||
|
synthetic_annotation: false,
|
||||||
kind: ParameterKind::PositionalOrKeyword {
|
kind: ParameterKind::PositionalOrKeyword {
|
||||||
name,
|
name,
|
||||||
default_type: None,
|
default_type: None,
|
||||||
|
@ -1432,6 +1538,7 @@ impl<'db> Parameter<'db> {
|
||||||
pub(crate) fn variadic(name: Name) -> Self {
|
pub(crate) fn variadic(name: Name) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: None,
|
annotated_type: None,
|
||||||
|
synthetic_annotation: false,
|
||||||
kind: ParameterKind::Variadic { name },
|
kind: ParameterKind::Variadic { name },
|
||||||
form: ParameterForm::Value,
|
form: ParameterForm::Value,
|
||||||
}
|
}
|
||||||
|
@ -1440,6 +1547,7 @@ impl<'db> Parameter<'db> {
|
||||||
pub(crate) fn keyword_only(name: Name) -> Self {
|
pub(crate) fn keyword_only(name: Name) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: None,
|
annotated_type: None,
|
||||||
|
synthetic_annotation: false,
|
||||||
kind: ParameterKind::KeywordOnly {
|
kind: ParameterKind::KeywordOnly {
|
||||||
name,
|
name,
|
||||||
default_type: None,
|
default_type: None,
|
||||||
|
@ -1451,6 +1559,7 @@ impl<'db> Parameter<'db> {
|
||||||
pub(crate) fn keyword_variadic(name: Name) -> Self {
|
pub(crate) fn keyword_variadic(name: Name) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: None,
|
annotated_type: None,
|
||||||
|
synthetic_annotation: false,
|
||||||
kind: ParameterKind::KeywordVariadic { name },
|
kind: ParameterKind::KeywordVariadic { name },
|
||||||
form: ParameterForm::Value,
|
form: ParameterForm::Value,
|
||||||
}
|
}
|
||||||
|
@ -1489,6 +1598,7 @@ impl<'db> Parameter<'db> {
|
||||||
.annotated_type
|
.annotated_type
|
||||||
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)),
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)),
|
||||||
kind: self.kind.apply_type_mapping_impl(db, type_mapping, visitor),
|
kind: self.kind.apply_type_mapping_impl(db, type_mapping, visitor),
|
||||||
|
synthetic_annotation: self.synthetic_annotation,
|
||||||
form: self.form,
|
form: self.form,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1504,6 +1614,7 @@ impl<'db> Parameter<'db> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let Parameter {
|
let Parameter {
|
||||||
annotated_type,
|
annotated_type,
|
||||||
|
synthetic_annotation,
|
||||||
kind,
|
kind,
|
||||||
form,
|
form,
|
||||||
} = self;
|
} = self;
|
||||||
|
@ -1547,6 +1658,7 @@ impl<'db> Parameter<'db> {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
annotated_type: Some(annotated_type),
|
annotated_type: Some(annotated_type),
|
||||||
|
synthetic_annotation: *synthetic_annotation,
|
||||||
kind,
|
kind,
|
||||||
form: *form,
|
form: *form,
|
||||||
}
|
}
|
||||||
|
@ -1569,6 +1681,7 @@ impl<'db> Parameter<'db> {
|
||||||
}),
|
}),
|
||||||
kind,
|
kind,
|
||||||
form: ParameterForm::Value,
|
form: ParameterForm::Value,
|
||||||
|
synthetic_annotation: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1623,6 +1736,11 @@ impl<'db> Parameter<'db> {
|
||||||
&self.kind
|
&self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the type of the parameter was inferred.
|
||||||
|
pub(crate) fn has_synthetic_annotation(&self) -> bool {
|
||||||
|
self.synthetic_annotation
|
||||||
|
}
|
||||||
|
|
||||||
/// Name of the parameter (if it has one).
|
/// Name of the parameter (if it has one).
|
||||||
pub(crate) fn name(&self) -> Option<&ast::name::Name> {
|
pub(crate) fn name(&self) -> Option<&ast::name::Name> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue