Compare commits

...

16 Commits

Author SHA1 Message Date
David Peter
6a3101b0be [ty] Improve specialization-error diagnostics 2025-09-10 11:44:33 +02:00
David Peter
0f371f6efd Merge remote-tracking branch 'origin/main' into david/signature-implicit-self 2025-09-10 11:12:03 +02:00
David Peter
cbaac56cf1 [ty] Use 'unknown' specialization for upper bound on Self 2025-09-10 11:04:54 +02:00
David Peter
99ecb657f6 Minor fixes 2025-09-09 08:47:14 +02:00
David Peter
31566d67cb Update tests 2025-09-08 15:49:40 +02:00
David Peter
f0b0d2ef87 [ty] Merge legacy and PEP 695 generic contexts 2025-09-08 15:42:21 +02:00
David Peter
4c7f7d199b Update diagnostic count 2025-09-08 13:30:56 +02:00
David Peter
a66add41be Make Self an inferrable TypeVar 2025-09-08 13:08:16 +02:00
Glyphack
4064fa28fc Add more examples for self mdtest 2025-09-06 00:08:23 +02:00
Glyphack
a51982bac6 Update mdtests 2025-09-05 20:59:21 +02:00
Shaygan Hooshyari
a27013bdd3 Merge branch 'main' into typing-self-argument 2025-09-05 20:42:39 +02:00
Glyphack
c24236cc73 Update mdtests 2025-09-05 18:28:22 +02:00
Glyphack
0b19caedec Update mdtests 2025-09-05 00:02:39 +02:00
Glyphack
178f48fc0b Update way to identify self in signature 2025-09-04 22:05:05 +02:00
Glyphack
f34b6d8245 Don't display the implicit typing.Self type 2025-09-04 22:01:55 +02:00
Glyphack
8dd183e55c Assume type of self is typing.Self in signature 2025-09-04 22:01:55 +02:00
22 changed files with 333 additions and 69 deletions

View File

@@ -232,7 +232,7 @@ static STATIC_FRAME: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLo
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
500,
600,
)
});

View File

@@ -3030,6 +3030,12 @@ impl Parameters {
.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.
pub fn iter(&self) -> ParametersIterator<'_> {
ParametersIterator::new(self)

View File

@@ -2,7 +2,7 @@
```toml
[environment]
python-version = "3.11"
python-version = "3.13"
```
`Self` is treated as if it were a `TypeVar` bound to the class it's being used on.
@@ -30,16 +30,9 @@ class Shape:
def nested_func_without_enclosing_binding(self):
def inner(x: Self):
# TODO: revealed: Self@nested_func_without_enclosing_binding
# (The outer method binds an implicit `Self`)
reveal_type(x) # revealed: Self@inner
reveal_type(x) # revealed: Self@nested_func_without_enclosing_binding
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_func()) # revealed: Shape
@@ -55,6 +48,102 @@ class Outer:
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()
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]()
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
```toml
@@ -147,6 +236,23 @@ class Shape:
return self
```
## `Self` for classes with a default value for their generic parameter
This is a regression test for <https://github.com/astral-sh/ty/issues/1156>.
```py
from typing import Self
class Container[T = bytes]:
def __init__(self: Self, data: T | None = None) -> None:
self.data = data
reveal_type(Container()) # revealed: Container[bytes]
reveal_type(Container(1)) # revealed: Container[int]
reveal_type(Container("a")) # revealed: Container[str]
reveal_type(Container(b"a")) # revealed: Container[bytes]
```
## Invalid Usage
`Self` cannot be used in the signature of a function or variable.
@@ -193,6 +299,30 @@ class MyMetaclass(type):
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()
```
## Binding a method fixes `Self`
When a method is bound, any instances of `Self` in its signature are "fixed", since we now know the

View File

@@ -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
```

View File

@@ -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()

View File

@@ -562,17 +562,17 @@ class C(Generic[T]):
return u
reveal_type(generic_context(C)) # revealed: tuple[T@C]
reveal_type(generic_context(C.method)) # revealed: None
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(C.method)) # revealed: tuple[Self@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].method)) # revealed: None
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
c: C[int] = C[int]()
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
reveal_type(generic_context(c)) # revealed: None
reveal_type(generic_context(c.method)) # revealed: None
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
```
## Specializations propagate

View File

@@ -504,17 +504,17 @@ class C[T]:
def cannot_shadow_class_typevar[T](self, t: T): ...
reveal_type(generic_context(C)) # revealed: tuple[T@C]
reveal_type(generic_context(C.method)) # revealed: None
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(C.method)) # revealed: tuple[Self@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].method)) # revealed: None
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
c: C[int] = C[int]()
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
reveal_type(generic_context(c)) # revealed: None
reveal_type(generic_context(c.method)) # revealed: None
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
```
## Specializations propagate

View File

@@ -512,7 +512,5 @@ class C:
def _(x: 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[Unknown, int]
reveal_type(C().implicit_self(x)) # revealed: tuple[C, int]
```

View File

@@ -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]):
@@ -154,7 +156,7 @@ from ty_extensions import generic_context
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(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:

View File

@@ -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
```

View File

@@ -357,8 +357,12 @@ class Invariant[T]:
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)
```

View File

@@ -313,7 +313,7 @@ type A = list[Union["A", str]]
def f(x: A):
reveal_type(x) # revealed: list[A | str]
for item in x:
reveal_type(item) # revealed: list[A | str] | str
reveal_type(item) # revealed: list[Any | str] | str
```
#### With new-style union
@@ -324,7 +324,7 @@ type A = list["A" | str]
def f(x: A):
reveal_type(x) # revealed: list[A | str]
for item in x:
reveal_type(item) # revealed: list[A | str] | str
reveal_type(item) # revealed: list[Any | str] | str
```
#### With Optional
@@ -337,5 +337,5 @@ type A = list[Optional[Union["A", str]]]
def f(x: A):
reveal_type(x) # revealed: list[A | str | None]
for item in x:
reveal_type(item) # revealed: list[A | str | None] | str | None
reveal_type(item) # revealed: list[Any | str | None] | str | None
```

View File

@@ -74,7 +74,7 @@ error[invalid-argument-type]: Argument to function `f` is incorrect
10 | reveal_type(f(True)) # revealed: Literal[True]
11 | # error: [invalid-argument-type]
12 | reveal_type(f("string")) # revealed: Unknown
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy upper bound of type variable `T`
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy upper bound of type variable `T` (int)
|
info: Type variable defined here
--> src/mdtest_snippet.py:4:1

View File

@@ -89,7 +89,7 @@ error[invalid-argument-type]: Argument to function `f` is incorrect
11 | reveal_type(f(None)) # revealed: None
12 | # error: [invalid-argument-type]
13 | reveal_type(f("string")) # revealed: Unknown
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy constraints of type variable `T`
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy constraints of type variable `T` (int, None)
|
info: Type variable defined here
--> src/mdtest_snippet.py:4:1

View File

@@ -71,7 +71,7 @@ error[invalid-argument-type]: Argument to function `f` is incorrect
7 | reveal_type(f(True)) # revealed: Literal[True]
8 | # error: [invalid-argument-type]
9 | reveal_type(f("string")) # revealed: Unknown
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy upper bound of type variable `T`
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy upper bound of type variable `T` (int)
|
info: Type variable defined here
--> src/mdtest_snippet.py:3:7

View File

@@ -86,7 +86,7 @@ error[invalid-argument-type]: Argument to function `f` is incorrect
8 | reveal_type(f(None)) # revealed: None
9 | # error: [invalid-argument-type]
10 | reveal_type(f("string")) # revealed: Unknown
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy constraints of type variable `T`
| ^^^^^^^^ Argument type `Literal["string"]` does not satisfy constraints of type variable `T` (int, None)
|
info: Type variable defined here
--> src/mdtest_snippet.py:3:7

View File

@@ -188,7 +188,7 @@ error[invalid-argument-type]: Argument to function `f4` is incorrect
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
60 | x = f(3)
| ^ Argument type `Literal[3]` does not satisfy upper bound of type variable `T`
| ^ Argument type `Literal[3]` does not satisfy upper bound of type variable `T` (str)
|
info: Type variable defined here
--> src/mdtest_snippet.py:13:8

View File

@@ -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())
```

View File

@@ -5708,9 +5708,7 @@ impl<'db> Type<'db> {
],
});
};
let instance = Type::ClassLiteral(class).to_instance(db).expect(
"nearest_enclosing_class must return type that can be instantiated",
);
let instance = Type::instance(db, class.unknown_specialization(db));
let class_definition = class.definition(db);
let typevar = TypeVarInstance::new(
db,

View File

@@ -3086,14 +3086,20 @@ impl<'db> BindingError<'db> {
String::new()
}
));
diag.set_primary_message(format_args!(
"Argument type `{argument_ty_display}` does not satisfy {} of type variable `{}`",
match error {
SpecializationError::MismatchedBound {..} => "upper bound",
SpecializationError::MismatchedConstraint {..} => "constraints",
},
typevar.name(context.db()),
));
let typevar_name = typevar.name(context.db());
match error {
SpecializationError::MismatchedBound { .. } => {
diag.set_primary_message(format_args!("Argument type `{argument_ty_display}` does not satisfy upper bound of type variable `{typevar_name}` ({})",
typevar.upper_bound(context.db()).expect("type variable should have an upper bound if this error occurs").display(context.db())
));
}
SpecializationError::MismatchedConstraint { .. } => {
diag.set_primary_message(format_args!("Argument type `{argument_ty_display}` does not satisfy constraints of type variable `{typevar_name}` ({})",
typevar.constraints(context.db()).expect("type variable should have constraints if this error occurs").iter().map(|ty| ty.display(context.db())).join(", ")
));
}
}
if let Some(typevar_definition) = typevar.definition(context.db()) {
let module = parsed_module(context.db(), typevar_definition.file(context.db()))

View File

@@ -1150,11 +1150,13 @@ impl Display for DisplayParameter<'_> {
if let Some(name) = self.param.display_name() {
f.write_str(&name)?;
if let Some(annotated_type) = self.param.annotated_type() {
write!(
f,
": {}",
annotated_type.display_with(self.db, self.settings)
)?;
if !self.param.has_synthetic_annotation() {
write!(
f,
": {}",
annotated_type.display_with(self.db, self.settings)
)?;
}
}
// Default value can only be specified if `name` is given.
if let Some(default_ty) = self.param.default_type() {
@@ -1167,7 +1169,9 @@ impl Display for DisplayParameter<'_> {
} else if let Some(ty) = self.param.annotated_type() {
// This case is specifically for the `Callable` signature where name and default value
// cannot be provided.
ty.display_with(self.db, self.settings).fmt(f)?;
if !self.param.has_synthetic_annotation() {
ty.display_with(self.db, self.settings).fmt(f)?;
}
}
Ok(())
}

View File

@@ -15,18 +15,49 @@ use std::{collections::HashMap, slice::Iter};
use itertools::{EitherOrBoth, Itertools};
use smallvec::{SmallVec, smallvec_inline};
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
use crate::semantic_index::definition::Definition;
use super::{
DynamicType, Type, TypeVarVariance, definition_expression_type, infer_definition_types,
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::generics::{GenericContext, walk_generic_context};
use crate::types::function::FunctionType;
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,
TypeMapping, TypeRelation, VarianceInferable, todo_type,
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
VarianceInferable, todo_type,
};
use crate::{Db, FxOrderSet};
use ruff_db::parsed::parsed_module;
use ruff_python_ast::{self as ast, name::Name};
fn infer_method_type<'db>(
db: &'db dyn Db,
definition: Definition<'db>,
) -> Option<FunctionType<'db>> {
let class_scope_id = definition.scope(db);
let file = class_scope_id.file(db);
let index = semantic_index(db, file);
let module = parsed_module(db, file).load(db);
let DefinitionKind::Function(func_def) = definition.kind(db) else {
return None;
};
let class_scope = index.scope(class_scope_id.file_scope_id(db));
class_scope.node().as_class()?;
let method_definition = index.expect_single_definition(func_def.node(&module));
let func_type = infer_definition_types(db, method_definition)
.declaration_type(method_definition)
.inner_type()
.into_function_literal()?;
Some(func_type)
}
/// The signature of a single callable. If the callable is overloaded, there is a separate
/// [`Signature`] for each overload.
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
@@ -1207,16 +1238,78 @@ impl<'db> Parameters<'db> {
);
}
let method_type = infer_method_type(db, definition);
let is_method = method_type.is_some();
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| {
Parameter::from_node_and_kind(
db,
definition,
&arg.parameter,
ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
},
)
if is_method
&& !is_staticmethod
&& !is_classmethod
&& arg.parameter.annotation().is_none()
&& parameters.index(arg.name().id()) == Some(0)
{
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: 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(
db,
definition,
&arg.parameter,
ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
},
)
}
});
let variadic = vararg.as_ref().map(|arg| {
@@ -1378,6 +1471,10 @@ pub(crate) struct Parameter<'db> {
/// Annotated type of the parameter.
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>,
pub(crate) form: ParameterForm,
}
@@ -1386,6 +1483,7 @@ impl<'db> Parameter<'db> {
pub(crate) fn positional_only(name: Option<Name>) -> Self {
Self {
annotated_type: None,
synthetic_annotation: false,
kind: ParameterKind::PositionalOnly {
name,
default_type: None,
@@ -1397,6 +1495,7 @@ impl<'db> Parameter<'db> {
pub(crate) fn positional_or_keyword(name: Name) -> Self {
Self {
annotated_type: None,
synthetic_annotation: false,
kind: ParameterKind::PositionalOrKeyword {
name,
default_type: None,
@@ -1408,6 +1507,7 @@ impl<'db> Parameter<'db> {
pub(crate) fn variadic(name: Name) -> Self {
Self {
annotated_type: None,
synthetic_annotation: false,
kind: ParameterKind::Variadic { name },
form: ParameterForm::Value,
}
@@ -1416,6 +1516,7 @@ impl<'db> Parameter<'db> {
pub(crate) fn keyword_only(name: Name) -> Self {
Self {
annotated_type: None,
synthetic_annotation: false,
kind: ParameterKind::KeywordOnly {
name,
default_type: None,
@@ -1427,6 +1528,7 @@ impl<'db> Parameter<'db> {
pub(crate) fn keyword_variadic(name: Name) -> Self {
Self {
annotated_type: None,
synthetic_annotation: false,
kind: ParameterKind::KeywordVariadic { name },
form: ParameterForm::Value,
}
@@ -1465,6 +1567,7 @@ impl<'db> Parameter<'db> {
.annotated_type
.map(|ty| ty.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,
}
}
@@ -1480,6 +1583,7 @@ impl<'db> Parameter<'db> {
) -> Self {
let Parameter {
annotated_type,
synthetic_annotation,
kind,
form,
} = self;
@@ -1523,6 +1627,7 @@ impl<'db> Parameter<'db> {
Self {
annotated_type: Some(annotated_type),
synthetic_annotation: *synthetic_annotation,
kind,
form: *form,
}
@@ -1543,6 +1648,7 @@ impl<'db> Parameter<'db> {
}),
kind,
form: ParameterForm::Value,
synthetic_annotation: false,
}
}
@@ -1597,6 +1703,11 @@ impl<'db> Parameter<'db> {
&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).
pub(crate) fn name(&self) -> Option<&ast::name::Name> {
match &self.kind {