Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/annotations/self.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,30 @@ reveal_type(Container("a")) # revealed: Container[str]
reveal_type(Container(b"a")) # revealed: Container[bytes]
```

## Implicit self for classes with a default value for their generic parameter

```py
from typing import Self, TypeVar, Generic

class Container[T = bytes]:
def method(self) -> Self:
return self

def _(c: Container[str], d: Container):
reveal_type(c.method()) # revealed: Container[str]
reveal_type(d.method()) # revealed: Container[bytes]

T = TypeVar("T", default=bytes)

class LegacyContainer(Generic[T]):
def method(self) -> Self:
return self

def _(c: LegacyContainer[str], d: LegacyContainer):
reveal_type(c.method()) # revealed: LegacyContainer[str]
reveal_type(d.method()) # revealed: LegacyContainer[bytes]
```

## Invalid Usage

`Self` cannot be used in the signature of a function or variable.
Expand Down
6 changes: 6 additions & 0 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7735,6 +7735,12 @@ pub enum BindingContext<'db> {
Synthetic,
}

impl<'db> From<Definition<'db>> for BindingContext<'db> {
fn from(definition: Definition<'db>) -> Self {
BindingContext::Definition(definition)
}
}

impl<'db> BindingContext<'db> {
fn name(self, db: &'db dyn Db) -> Option<String> {
match self {
Expand Down
29 changes: 17 additions & 12 deletions crates/ty_python_semantic/src/types/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ use crate::types::function::FunctionType;
use crate::types::generics::{GenericContext, typing_self, walk_generic_context};
use crate::types::infer::nearest_enclosing_class;
use crate::types::{
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass,
MaterializationKind, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable,
todo_type,
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
TypeMapping, TypeRelation, VarianceInferable, todo_type,
};
use crate::{Db, FxOrderSet};
use ruff_python_ast::{self as ast, name::Name};
Expand Down Expand Up @@ -415,9 +414,7 @@ impl<'db> Signature<'db> {
let plain_return_ty = definition_expression_type(db, definition, returns.as_ref())
.apply_type_mapping(
db,
&TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition(
definition,
))),
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
);
if function_node.is_async && !is_generator {
KnownClass::CoroutineType
Expand Down Expand Up @@ -1256,8 +1253,18 @@ impl<'db> Parameters<'db> {
let class = nearest_enclosing_class(db, index, scope_id).unwrap();

Some(
typing_self(db, scope_id, typevar_binding_context, class, &Type::TypeVar)
.expect("We should always find the surrounding class for an implicit self: Self annotation"),
// It looks like unnecessary work here that we create the implicit Self
// annotation using non-inferable typevars and then immediately apply
// `MarkTypeVarsInferable` to it. However, this is currently necessary to
// ensure that implicit-Self and explicit Self annotations are both treated
// the same. Marking type vars inferable will cause reification of lazy
// typevar defaults/bounds/constraints; this needs to happen for both
// implicit and explicit Self so they remain the "same" typevar.
typing_self(db, scope_id, typevar_binding_context, class, &Type::NonInferableTypeVar)
.expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping(
db,
&TypeMapping::MarkTypeVarsInferable(None),
)
)
} else {
// For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or
Expand Down Expand Up @@ -1680,9 +1687,7 @@ impl<'db> Parameter<'db> {
annotated_type: parameter.annotation().map(|annotation| {
definition_expression_type(db, definition, annotation).apply_type_mapping(
db,
&TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition(
definition,
))),
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
)
}),
kind,
Expand Down
Loading