Skip to content

Commit 7233df1

Browse files
committed
[ty] Infer type of implicit self in properties
1 parent 3490611 commit 7233df1

File tree

4 files changed

+40
-8
lines changed

4 files changed

+40
-8
lines changed

crates/ruff_benchmark/benches/ty_walltime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ static STATIC_FRAME: Benchmark = Benchmark::new(
226226
max_dep_date: "2025-08-09",
227227
python_version: PythonVersion::PY311,
228228
},
229-
750,
229+
800,
230230
);
231231

232232
#[track_caller]

crates/ty_ide/src/semantic_tokens.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1413,7 +1413,7 @@ u = List.__name__ # __name__ should be variable<CURSOR>
14131413
"property" @ 168..176: Decorator
14141414
"prop" @ 185..189: Method [definition]
14151415
"self" @ 190..194: SelfParameter
1416-
"self" @ 212..216: Variable
1416+
"self" @ 212..216: TypeParameter
14171417
"CONSTANT" @ 217..225: Variable [readonly]
14181418
"obj" @ 227..230: Variable
14191419
"MyClass" @ 233..240: Class

crates/ty_python_semantic/resources/mdtest/annotations/self.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ A.implicit_self(1)
116116
Passing `self` implicitly also verifies the type:
117117

118118
```py
119-
from typing import Never
119+
from typing import Never, Callable
120120

121121
class Strange:
122122
def can_not_be_called(self: Never) -> None: ...
@@ -139,6 +139,9 @@ The first parameter of instance methods always has type `Self`, if it is not exp
139139
The name `self` is not special in any way.
140140

141141
```py
142+
def some_decorator(f: Callable) -> Callable:
143+
return f
144+
142145
class B:
143146
def name_does_not_matter(this) -> Self:
144147
reveal_type(this) # revealed: Self@name_does_not_matter
@@ -153,18 +156,47 @@ class B:
153156
reveal_type(self) # revealed: Self@keyword_only
154157
return self
155158

159+
@some_decorator
160+
def decorated_method(self) -> Self:
161+
reveal_type(self) # revealed: Self@decorated_method
162+
return self
163+
156164
@property
157165
def a_property(self) -> Self:
158-
# TODO: Should reveal Self@a_property
159-
reveal_type(self) # revealed: Unknown
166+
reveal_type(self) # revealed: Self@a_property
160167
return self
161168

169+
async def async_method(self) -> Self:
170+
reveal_type(self) # revealed: Self@async_method
171+
return self
172+
173+
@staticmethod
174+
def static_method(self):
175+
# The parameter can be called `self`, but it is not treated as `Self`
176+
reveal_type(self) # revealed: Unknown
177+
178+
@staticmethod
179+
@some_decorator
180+
def decorated_static_method(self):
181+
# TODO: this should be Unknown
182+
reveal_type(self) # revealed: Self@decorated_static_method
183+
184+
@some_decorator
185+
@staticmethod
186+
def decorated_static_method_2(self):
187+
# TODO: this should be Unknown
188+
reveal_type(self) # revealed: Self@decorated_static_method_2
189+
162190
reveal_type(B().name_does_not_matter()) # revealed: B
163191
reveal_type(B().positional_only(1)) # revealed: B
164192
reveal_type(B().keyword_only(x=1)) # revealed: B
193+
reveal_type(B().decorated_method()) # revealed: Unknown
165194

166195
# TODO: this should be B
167196
reveal_type(B().a_property) # revealed: Unknown
197+
198+
async def _():
199+
reveal_type(await B().async_method()) # revealed: B
168200
```
169201

170202
This also works for generic classes:

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2583,12 +2583,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
25832583
let method = infer_definition_types(db, method_definition)
25842584
.declaration_type(method_definition)
25852585
.inner_type()
2586-
.as_function_literal()?;
2586+
.as_function_literal();
25872587

2588-
if method.is_classmethod(db) {
2588+
if method.is_some_and(|m| m.is_classmethod(db)) {
25892589
// TODO: set the type for `cls` argument
25902590
return None;
2591-
} else if method.is_staticmethod(db) {
2591+
} else if method.is_some_and(|m| m.is_staticmethod(db)) {
25922592
return None;
25932593
}
25942594

0 commit comments

Comments
 (0)