Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
0e3d511
Infer type of self as typing.Self in method body
Glyphack Jun 4, 2025
6913d99
Handle type var in SuperOwnerKind::try_from_type
Glyphack Jun 5, 2025
466c93e
Update class super test
Glyphack Jun 10, 2025
3f2c487
Mark failing tests
Glyphack Jun 11, 2025
fd732c5
Don't emit diagnostic for assigning to self
Glyphack Jul 22, 2025
b6449d3
Don't unwrap the self type carelessly
Glyphack Jun 11, 2025
deb7a96
Mark attributes failing tests as todo
Glyphack Jun 14, 2025
3586737
Fix trait upcasting coercion is experimental
Glyphack Jun 14, 2025
c21827d
Fix too many iterations on comtypes
Glyphack Aug 28, 2025
495278d
Apply comments
Glyphack Jul 17, 2025
854ddee
Temp fix completions
Glyphack Jul 17, 2025
6bf8513
Add apprise to bad.txt
Glyphack Jul 17, 2025
2c24356
Remove apprise from good.txt
Glyphack Jun 23, 2025
9a6ae4f
Link issue
Glyphack Jun 24, 2025
ce1d951
Remove upcast
Glyphack Jul 9, 2025
e9aec22
Update TODO mdtests
Glyphack Jul 17, 2025
49c310a
Update snapshots
Glyphack Jul 9, 2025
c3d7304
Move discord.py to bad.txt
Glyphack Jul 17, 2025
053f651
Increase max_diagnostics
Glyphack Jul 9, 2025
1f71b74
Disallow complex exprs in constraint builder
Glyphack Jul 22, 2025
2acdbc7
Add test for cycle count
Glyphack Jul 22, 2025
4818f33
Revert falling back to Ambiguous for complex expressions
Glyphack Jul 24, 2025
ae79edf
Use infer_expression_types instead of infer_expression_types in Reach…
Glyphack Jul 24, 2025
08e8695
Remove gridout test case
Glyphack Aug 28, 2025
241bf63
Set correct typevar_binding_context
Glyphack Aug 29, 2025
d0acd44
Decrease MAX_UNION_LITERALS to avoid panic
Glyphack Aug 29, 2025
f67cc04
Handle both NonInferableTypeVar in super args
Glyphack Aug 30, 2025
5054fda
Update snapshots
Glyphack Aug 30, 2025
e877537
Update mdtest assertion
Glyphack Aug 30, 2025
58b02a2
Increase diagnostic count
Glyphack Aug 30, 2025
4205dbf
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Sep 3, 2025
0df523f
Minor review comments
sharkdp Sep 3, 2025
f24f495
Move failing projects to bad.txt
sharkdp Sep 3, 2025
d08f955
Treat __new__ as a static method
sharkdp Sep 3, 2025
a37ee79
Disable sympy in walltime benchmarks
sharkdp Sep 3, 2025
8305105
Disable prefect in memory statistics
sharkdp Sep 3, 2025
51640f2
Bring back TODO comment
sharkdp Sep 3, 2025
45421b6
Remove comment
sharkdp Sep 3, 2025
347892f
Add comment why benchmark was disabled
sharkdp Sep 3, 2025
5be3a5a
Disable pandas as a walltime benchmark
sharkdp Sep 3, 2025
baf30b4
Minor adjustments to special_first_method_parameter_type
sharkdp Sep 3, 2025
2a62a7d
Increase another threshold
sharkdp Sep 3, 2025
921119a
Merge branch 'main' into typing-self-function-scope
MichaReiser Sep 23, 2025
a02b03f
Discard changes to playground/ty/src/Editor/Editor.tsx
MichaReiser Sep 23, 2025
9caa105
Revert mypy primer changes
MichaReiser Sep 23, 2025
c0043d2
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Sep 30, 2025
32be31d
Update Salsa
MichaReiser Sep 30, 2025
56db0b9
Revert "Update Salsa"
sharkdp Oct 7, 2025
0056416
Remove FIXME comment
sharkdp Oct 7, 2025
c1cab1f
Minor rewording/renaming
sharkdp Oct 7, 2025
da56e21
Use new typing_self helper
sharkdp Oct 7, 2025
fa83f8f
Remove confusing TODO annotations
sharkdp Oct 7, 2025
b696145
Minor comment cleanup
sharkdp Oct 7, 2025
13457d2
Merge remote-tracking branch 'origin/main' into typing-self-function-…
sharkdp Oct 7, 2025
7eae73c
Merge branch 'main' into typing-self-function-scope
MichaReiser Oct 11, 2025
8bba6ef
Update Salsa
MichaReiser Oct 11, 2025
cbc7305
Fix expected diagnosatic counts
MichaReiser Oct 11, 2025
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
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
rustc-hash = { version = "2.0.0" }
rustc-stable-hash = { version = "0.1.2" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "29ab321b45d00daa4315fa2a06f7207759a8c87e", default-features = false, features = [
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "f89c430644e15efe0ec74ef6108e045c98d0db80", default-features = false, features = [
"compact_str",
"macros",
"salsa_unstable",
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff_benchmark/benches/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ fn attrs(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
100,
110,
);

bench_project(&benchmark, criterion);
Expand All @@ -684,7 +684,7 @@ fn anyio(criterion: &mut Criterion) {
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
100,
150,
);

bench_project(&benchmark, criterion);
Expand Down
6 changes: 3 additions & 3 deletions crates/ruff_benchmark/benches/ty_walltime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ static SYMPY: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
13000,
15000,
);

static TANJUN: Benchmark = Benchmark::new(
Expand All @@ -210,7 +210,7 @@ static TANJUN: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
100,
320,
);

static STATIC_FRAME: Benchmark = Benchmark::new(
Expand All @@ -226,7 +226,7 @@ static STATIC_FRAME: Benchmark = Benchmark::new(
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
630,
710,
);

#[track_caller]
Expand Down
33 changes: 25 additions & 8 deletions crates/ty_ide/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1861,14 +1861,31 @@ class Quux:
",
);

// FIXME: This should list completions on `self`, which should
// include, at least, `foo` and `bar`. At time of writing
// (2025-06-04), the type of `self` is inferred as `Unknown` in
// this context. This in turn prevents us from getting a list
// of available attributes.
//
// See: https://github.com/astral-sh/ty/issues/159
assert_snapshot!(test.completions_without_builtins(), @"<No completions found>");
assert_snapshot!(test.completions_without_builtins(), @r"
__annotations__
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__getattribute__
__getstate__
__hash__
__init__
__init_subclass__
__module__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
");
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_ide/src/semantic_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1802,12 +1802,12 @@ class BoundedContainer[T: int, U = str]:
"get_first" @ 642..651: Method [definition]
"self" @ 652..656: SelfParameter
"T" @ 661..662: TypeParameter
"self" @ 679..683: Variable
"self" @ 679..683: TypeParameter
"value1" @ 684..690: Variable
"get_second" @ 700..710: Method [definition]
"self" @ 711..715: SelfParameter
"U" @ 720..721: TypeParameter
"self" @ 738..742: Variable
"self" @ 738..742: TypeParameter
"value2" @ 743..749: Variable
"BoundedContainer" @ 798..814: Class [definition]
"T" @ 815..816: TypeParameter [definition]
Expand Down
42 changes: 24 additions & 18 deletions crates/ty_python_semantic/resources/mdtest/annotations/self.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ from typing import Self

class A:
def implicit_self(self) -> Self:
# TODO: This should be Self@implicit_self
reveal_type(self) # revealed: Unknown
reveal_type(self) # revealed: Self@implicit_self

return self

Expand Down Expand Up @@ -127,19 +126,16 @@ The name `self` is not special in any way.
```py
class B:
def name_does_not_matter(this) -> Self:
# TODO: Should reveal Self@name_does_not_matter
reveal_type(this) # revealed: Unknown
reveal_type(this) # revealed: Self@name_does_not_matter

return this

def positional_only(self, /, x: int) -> Self:
# TODO: Should reveal Self@positional_only
reveal_type(self) # revealed: Unknown
reveal_type(self) # revealed: Self@positional_only
return self

def keyword_only(self, *, x: int) -> Self:
# TODO: Should reveal Self@keyword_only
reveal_type(self) # revealed: Unknown
reveal_type(self) # revealed: Self@keyword_only
return self

@property
Expand All @@ -165,8 +161,7 @@ T = TypeVar("T")

class G(Generic[T]):
def id(self) -> Self:
# TODO: Should reveal Self@id
reveal_type(self) # revealed: Unknown
reveal_type(self) # revealed: Self@id

return self

Expand Down Expand Up @@ -252,6 +247,20 @@ class LinkedList:
reveal_type(LinkedList().next()) # revealed: LinkedList
```

Attributes can also refer to a generic parameter:

```py
from typing import Generic, TypeVar

T = TypeVar("T")

class C(Generic[T]):
foo: T
def method(self) -> None:
reveal_type(self) # revealed: Self@method
reveal_type(self.foo) # revealed: T@C
```

## Generic Classes

```py
Expand Down Expand Up @@ -342,31 +351,28 @@ b: Self

# TODO: "Self" cannot be used in a function with a `self` or `cls` parameter that has a type annotation other than "Self"
class Foo:
# TODO: rejected Self because self has a different type
# TODO: This `self: T` annotation should be rejected because `T` is not `Self`
def has_existing_self_annotation(self: T) -> Self:
return self # error: [invalid-return-type]

def return_concrete_type(self) -> Self:
# TODO: tell user to use "Foo" instead of "Self"
# TODO: We could emit a hint that suggests annotating with `Foo` instead of `Self`
# error: [invalid-return-type]
return Foo()

@staticmethod
# TODO: reject because of staticmethod
# TODO: The usage of `Self` here should be rejected because this is a static method
def make() -> Self:
# error: [invalid-return-type]
return Foo()

class Bar(Generic[T]):
foo: T
def bar(self) -> T:
return self.foo
class Bar(Generic[T]): ...

# error: [invalid-type-form]
class Baz(Bar[Self]): ...

class MyMetaclass(type):
# TODO: rejected
# TODO: reject the Self usage. because self cannot be used within a metaclass.
def __new__(cls) -> Self:
return super().__new__(cls)
```
Expand Down
22 changes: 16 additions & 6 deletions crates/ty_python_semantic/resources/mdtest/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
c_instance = C(1)

reveal_type(c_instance.inferred_from_value) # revealed: Unknown | Literal[1, "a"]

# TODO: Same here. This should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown | Literal[1, "a"]

# There is no special handling of attributes that are (directly) assigned to a declared parameter,
# which means we union with `Unknown` here, since the attribute itself is not declared. This is
Expand Down Expand Up @@ -177,8 +175,7 @@

reveal_type(c_instance.inferred_from_value) # revealed: Unknown | Literal[1, "a"]

# TODO: Should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown | Literal[1, "a"]

reveal_type(c_instance.inferred_from_param) # revealed: Unknown | int | None

Expand Down Expand Up @@ -399,9 +396,19 @@

class C:
def __init__(self) -> None:
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
[... for self.a in IntIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
# error: [unresolved-attribute]
[... for (self.b, self.c) in TupleIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
# error: [unresolved-attribute]
[... for self.d in IntIterable() for self.e in IntIterable()]
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
[[... for self.f in IntIterable()] for _ in IntIterable()]
[[... for self.g in IntIterable()] for self in [D()]]

Expand Down Expand Up @@ -598,6 +605,8 @@
self.c = c
if False:
def set_e(self, e: str) -> None:
# TODO: Should not emit this diagnostic
# error: [unresolved-attribute]
self.e = e

# TODO: this would ideally be `Unknown | Literal[1]`
Expand Down Expand Up @@ -685,7 +694,7 @@
pure_class_variable2: ClassVar = 1

def method(self):
# TODO: this should be an error
# error: [invalid-attribute-access] "Cannot assign to ClassVar `pure_class_variable1` from an instance of type `Self@method`"
self.pure_class_variable1 = "value set through instance"

reveal_type(C.pure_class_variable1) # revealed: str
Expand Down Expand Up @@ -874,7 +883,7 @@

def set_attributes(self) -> None:
# TODO: This should be an `invalid-assignment` error
self.overwritten_in_subclass_method = None

Check failure on line 886 in crates/ty_python_semantic/resources/mdtest/attributes.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unexpected error: [invalid-assignment] "Object of type `None` is not assignable to attribute `overwritten_in_subclass_method` of type `str`"

# TODO: This should be an `invalid-assignment` error
self.pure_overwritten_in_subclass_method = None
Expand Down Expand Up @@ -1815,6 +1824,7 @@

class ThisFails:
def __init__(self):
# error: [invalid-assignment] "Implicit shadowing of function `__getattribute__`"
self.__getattribute__ = external_getattribute

# error: [unresolved-attribute]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class C:
return str(key)

def f(self):
# TODO: This should emit an `invalid-assignment` diagnostic once we understand the type of `self`
# error: [invalid-assignment] "Implicit shadowing of function `__getitem__`"
self.__getitem__ = None

# This is still fine, and simply calls the `__getitem__` method on the class
Expand Down
15 changes: 8 additions & 7 deletions crates/ty_python_semantic/resources/mdtest/class/super.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,12 @@ class A:

class B(A):
def __init__(self, a: int):
# TODO: Once `Self` is supported, this should be `<super: <class 'B'>, B>`
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'B'>, B>
super().__init__(a)

@classmethod
def f(cls):
# TODO: Once `Self` is supported, this should be `<super: <class 'B'>, <class 'B'>>`
# TODO: Once `cls` is supported, this should be `<super: <class 'B'>, <class 'B'>>`
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
super().f()

Expand Down Expand Up @@ -149,15 +148,15 @@ from __future__ import annotations

class A:
def test(self):
reveal_type(super()) # revealed: <super: <class 'A'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'A'>, A>

class B:
def test(self):
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'B'>, B>

class C(A.B):
def test(self):
reveal_type(super()) # revealed: <super: <class 'C'>, Unknown>
reveal_type(super()) # revealed: <super: <class 'C'>, C>

def inner(t: C):
reveal_type(super()) # revealed: <super: <class 'B'>, C>
Expand Down Expand Up @@ -279,6 +278,8 @@ class A[T]:

class B[T](A[T]):
def f(self, b: T) -> T:
# TODO: https://github.com/astral-sh/ty/issues/697
# error: [invalid-super-argument] "`Self@f` is not an instance or subclass of `<class 'B'>` in `super(<class 'B'>, Self@f)` call"
return super().f(b)
```

Expand Down Expand Up @@ -399,7 +400,7 @@ class A:
class B(A):
def __init__(self, a: int):
super().__init__(a)
# TODO: Once `Self` is supported, this should raise `unresolved-attribute` error
# error: [unresolved-attribute] "Type `<super: <class 'B'>, B>` has no attribute `a`"
super().a

# error: [unresolved-attribute] "Type `<super: <class 'B'>, B>` has no attribute `a`"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def f1(flag: bool):
attr = DataDescriptor()

def f(self):
# error: [invalid-assignment] "Invalid assignment to data descriptor attribute `attr` on type `Self@f` with custom `__set__` method"
self.attr = "normal"

reveal_type(C1().attr) # revealed: Unknown | Literal["data", "normal"]
Expand Down
3 changes: 1 addition & 2 deletions crates/ty_python_semantic/resources/mdtest/named_tuple.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ class SuperUser(User):
def now_called_robert(self):
self.name = "Robert" # fine because overridden with a mutable attribute

# TODO: this should cause us to emit an error as we're assigning to a read-only property
# inherited from the `NamedTuple` superclass (requires https://github.com/astral-sh/ty/issues/159)
# error: 9 [invalid-assignment] "Cannot assign to read-only property `nickname` on object of type `Self@now_called_robert`"
self.nickname = "Bob"

james = SuperUser(0, "James", 42, "Jimmy")
Expand Down
Loading
Loading