Skip to content

Commit 555792d

Browse files
authored
[ty] Reject functions with PEP-695 type parameters that shadow type parameters from enclosing scopes (#23619)
1 parent 03af8b2 commit 555792d

13 files changed

Lines changed: 340 additions & 150 deletions

crates/ty/docs/rules.md

Lines changed: 133 additions & 99 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ty_python_semantic/resources/mdtest/function/return_type.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class Bar[T](ABC):
105105
@abstractmethod
106106
def f(self) -> int: ...
107107
@abstractmethod
108-
def g[T](self, x: T) -> T: ...
108+
def g[U](self, x: U) -> U: ...
109109

110110
# error: [empty-body]
111111
def f() -> int: ...

crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ present, they are not included in the class's generic context.
8888

8989
```py
9090
class OuterClass(Generic[T]):
91-
# error: [invalid-generic-class] "Generic class `InnerClass` must not reference type variables bound in an enclosing scope"
91+
# error: [shadowed-type-variable] "Generic class `InnerClass` uses type variable `T` already bound by an enclosing scope"
9292
class InnerClass(list[T]): ...
9393
# revealed: None
9494
reveal_type(generic_context(InnerClass))
9595

9696
def method(self):
97-
# error: [invalid-generic-class] "Generic class `InnerClassInMethod` must not reference type variables bound in an enclosing scope"
97+
# error: [shadowed-type-variable] "Generic class `InnerClassInMethod` uses type variable `T` already bound by an enclosing scope"
9898
class InnerClassInMethod(list[T]): ...
9999
# revealed: None
100100
reveal_type(generic_context(InnerClassInMethod))

crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ class C[T]:
659659
# error: [unresolved-reference]
660660
def cannot_use_outside_of_method(self, u: U): ...
661661

662-
# TODO: error
662+
# error: [shadowed-type-variable]
663663
def cannot_shadow_class_typevar[T](self, t: T): ...
664664

665665
# revealed: ty_extensions.GenericContext[T@C]

crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ def test[T: int](items: list[T]) -> list[T]:
689689
from typing import overload
690690

691691
def outer[T](t: T) -> None:
692-
def inner[T](t: T) -> None: ...
692+
def inner[T](t: T) -> None: ... # error: [shadowed-type-variable]
693693

694694
inner(t)
695695

crates/ty_python_semantic/resources/mdtest/generics/scoping.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,25 @@ We assume that the more general form holds.
242242

243243
### Generic function within generic function
244244

245+
<!-- snapshot-diagnostics -->
246+
245247
```py
246248
def f[T](x: T, y: T) -> None:
247249
def ok[S](a: S, b: S) -> None: ...
248250

249-
# TODO: error
251+
# error: [shadowed-type-variable]
250252
def bad[T](a: T, b: T) -> None: ...
251253
```
252254

253255
### Generic method within generic class
254256

257+
<!-- snapshot-diagnostics -->
258+
255259
```py
256260
class C[T]:
257261
def ok[S](self, a: S, b: S) -> None: ...
258262

259-
# TODO: error
263+
# error: [shadowed-type-variable]
260264
def bad[T](self, a: T, b: T) -> None: ...
261265
```
262266

@@ -269,9 +273,9 @@ from typing import Iterable
269273

270274
def f[T](x: T, y: T) -> None:
271275
class Ok[S]: ...
272-
# error: [invalid-generic-class]
276+
# error: [shadowed-type-variable]
273277
class Bad1[T]: ...
274-
# error: [invalid-generic-class]
278+
# error: [shadowed-type-variable]
275279
class Bad2(Iterable[T]): ...
276280
```
277281

@@ -284,9 +288,9 @@ from typing import Iterable
284288

285289
class C[T]:
286290
class Ok1[S]: ...
287-
# error: [invalid-generic-class]
291+
# error: [shadowed-type-variable]
288292
class Bad1[T]: ...
289-
# error: [invalid-generic-class]
293+
# error: [shadowed-type-variable]
290294
class Bad2(Iterable[T]): ...
291295
```
292296

@@ -310,7 +314,7 @@ list:
310314

311315
```py
312316
class Outer[T]:
313-
# error: [invalid-generic-class]
317+
# error: [shadowed-type-variable]
314318
class Bad(list[T]): ...
315319
```
316320

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
source: crates/ty_test/src/lib.rs
3+
assertion_line: 624
34
expression: snapshot
45
---
56

@@ -17,48 +18,48 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/generics/scoping.md
1718
2 |
1819
3 | def f[T](x: T, y: T) -> None:
1920
4 | class Ok[S]: ...
20-
5 | # error: [invalid-generic-class]
21+
5 | # error: [shadowed-type-variable]
2122
6 | class Bad1[T]: ...
22-
7 | # error: [invalid-generic-class]
23+
7 | # error: [shadowed-type-variable]
2324
8 | class Bad2(Iterable[T]): ...
2425
```
2526

2627
# Diagnostics
2728

2829
```
29-
error[invalid-generic-class]: Generic class `Bad1` must not reference type variables bound in an enclosing scope
30+
error[shadowed-type-variable]: Generic class `Bad1` uses type variable `T` already bound by an enclosing scope
3031
--> src/mdtest_snippet.py:3:5
3132
|
3233
1 | from typing import Iterable
3334
2 |
3435
3 | def f[T](x: T, y: T) -> None:
3536
| ------------------------ Type variable `T` is bound in this enclosing scope
3637
4 | class Ok[S]: ...
37-
5 | # error: [invalid-generic-class]
38+
5 | # error: [shadowed-type-variable]
3839
6 | class Bad1[T]: ...
39-
| ^^^^ `T` referenced in class definition here
40-
7 | # error: [invalid-generic-class]
40+
| ^^^^ `T` used in class definition here
41+
7 | # error: [shadowed-type-variable]
4142
8 | class Bad2(Iterable[T]): ...
4243
|
43-
info: rule `invalid-generic-class` is enabled by default
44+
info: rule `shadowed-type-variable` is enabled by default
4445

4546
```
4647

4748
```
48-
error[invalid-generic-class]: Generic class `Bad2` must not reference type variables bound in an enclosing scope
49+
error[shadowed-type-variable]: Generic class `Bad2` uses type variable `T` already bound by an enclosing scope
4950
--> src/mdtest_snippet.py:3:5
5051
|
5152
1 | from typing import Iterable
5253
2 |
5354
3 | def f[T](x: T, y: T) -> None:
5455
| ------------------------ Type variable `T` is bound in this enclosing scope
5556
4 | class Ok[S]: ...
56-
5 | # error: [invalid-generic-class]
57+
5 | # error: [shadowed-type-variable]
5758
6 | class Bad1[T]: ...
58-
7 | # error: [invalid-generic-class]
59+
7 | # error: [shadowed-type-variable]
5960
8 | class Bad2(Iterable[T]): ...
60-
| ^^^^^^^^^^^^^^^^^ `T` referenced in class definition here
61+
| ^^^^^^^^^^^^^^^^^ `T` used in class definition here
6162
|
62-
info: rule `invalid-generic-class` is enabled by default
63+
info: rule `shadowed-type-variable` is enabled by default
6364

6465
```
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
source: crates/ty_test/src/lib.rs
3+
assertion_line: 624
34
expression: snapshot
45
---
56

@@ -17,48 +18,48 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/generics/scoping.md
1718
2 |
1819
3 | class C[T]:
1920
4 | class Ok1[S]: ...
20-
5 | # error: [invalid-generic-class]
21+
5 | # error: [shadowed-type-variable]
2122
6 | class Bad1[T]: ...
22-
7 | # error: [invalid-generic-class]
23+
7 | # error: [shadowed-type-variable]
2324
8 | class Bad2(Iterable[T]): ...
2425
```
2526

2627
# Diagnostics
2728

2829
```
29-
error[invalid-generic-class]: Generic class `Bad1` must not reference type variables bound in an enclosing scope
30+
error[shadowed-type-variable]: Generic class `Bad1` uses type variable `T` already bound by an enclosing scope
3031
--> src/mdtest_snippet.py:3:7
3132
|
3233
1 | from typing import Iterable
3334
2 |
3435
3 | class C[T]:
3536
| - Type variable `T` is bound in this enclosing scope
3637
4 | class Ok1[S]: ...
37-
5 | # error: [invalid-generic-class]
38+
5 | # error: [shadowed-type-variable]
3839
6 | class Bad1[T]: ...
39-
| ^^^^ `T` referenced in class definition here
40-
7 | # error: [invalid-generic-class]
40+
| ^^^^ `T` used in class definition here
41+
7 | # error: [shadowed-type-variable]
4142
8 | class Bad2(Iterable[T]): ...
4243
|
43-
info: rule `invalid-generic-class` is enabled by default
44+
info: rule `shadowed-type-variable` is enabled by default
4445

4546
```
4647

4748
```
48-
error[invalid-generic-class]: Generic class `Bad2` must not reference type variables bound in an enclosing scope
49+
error[shadowed-type-variable]: Generic class `Bad2` uses type variable `T` already bound by an enclosing scope
4950
--> src/mdtest_snippet.py:3:7
5051
|
5152
1 | from typing import Iterable
5253
2 |
5354
3 | class C[T]:
5455
| - Type variable `T` is bound in this enclosing scope
5556
4 | class Ok1[S]: ...
56-
5 | # error: [invalid-generic-class]
57+
5 | # error: [shadowed-type-variable]
5758
6 | class Bad1[T]: ...
58-
7 | # error: [invalid-generic-class]
59+
7 | # error: [shadowed-type-variable]
5960
8 | class Bad2(Iterable[T]): ...
60-
| ^^^^^^^^^^^^^^^^^ `T` referenced in class definition here
61+
| ^^^^^^^^^^^^^^^^^ `T` used in class definition here
6162
|
62-
info: rule `invalid-generic-class` is enabled by default
63+
info: rule `shadowed-type-variable` is enabled by default
6364

6465
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
assertion_line: 624
4+
expression: snapshot
5+
---
6+
7+
---
8+
mdtest name: scoping.md - Scoping rules for type variables - Nested formal typevars must be distinct - Generic function within generic function
9+
mdtest path: crates/ty_python_semantic/resources/mdtest/generics/scoping.md
10+
---
11+
12+
# Python source files
13+
14+
## mdtest_snippet.py
15+
16+
```
17+
1 | def f[T](x: T, y: T) -> None:
18+
2 | def ok[S](a: S, b: S) -> None: ...
19+
3 |
20+
4 | # error: [shadowed-type-variable]
21+
5 | def bad[T](a: T, b: T) -> None: ...
22+
```
23+
24+
# Diagnostics
25+
26+
```
27+
error[shadowed-type-variable]: Generic function `bad` uses type variable `T` already bound by an enclosing scope
28+
--> src/mdtest_snippet.py:5:9
29+
|
30+
4 | # error: [shadowed-type-variable]
31+
5 | def bad[T](a: T, b: T) -> None: ...
32+
| ^^^ `T` used in function definition here
33+
|
34+
::: src/mdtest_snippet.py:1:5
35+
|
36+
1 | def f[T](x: T, y: T) -> None:
37+
| ------------------------ Type variable `T` is bound in this enclosing scope
38+
2 | def ok[S](a: S, b: S) -> None: ...
39+
|
40+
info: rule `shadowed-type-variable` is enabled by default
41+
42+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
assertion_line: 624
4+
expression: snapshot
5+
---
6+
7+
---
8+
mdtest name: scoping.md - Scoping rules for type variables - Nested formal typevars must be distinct - Generic method within generic class
9+
mdtest path: crates/ty_python_semantic/resources/mdtest/generics/scoping.md
10+
---
11+
12+
# Python source files
13+
14+
## mdtest_snippet.py
15+
16+
```
17+
1 | class C[T]:
18+
2 | def ok[S](self, a: S, b: S) -> None: ...
19+
3 |
20+
4 | # error: [shadowed-type-variable]
21+
5 | def bad[T](self, a: T, b: T) -> None: ...
22+
```
23+
24+
# Diagnostics
25+
26+
```
27+
error[shadowed-type-variable]: Generic function `bad` uses type variable `T` already bound by an enclosing scope
28+
--> src/mdtest_snippet.py:5:9
29+
|
30+
4 | # error: [shadowed-type-variable]
31+
5 | def bad[T](self, a: T, b: T) -> None: ...
32+
| ^^^ `T` used in function definition here
33+
|
34+
::: src/mdtest_snippet.py:1:7
35+
|
36+
1 | class C[T]:
37+
| - Type variable `T` is bound in this enclosing scope
38+
2 | def ok[S](self, a: S, b: S) -> None: ...
39+
|
40+
info: rule `shadowed-type-variable` is enabled by default
41+
42+
```

0 commit comments

Comments
 (0)