Skip to content

Commit 5ca9c15

Browse files
authored
[ty] Better invalid-assignment diagnostics (#21476)
## Summary Improve the diagnostic range for `invalid-assignment` diagnostics, and add source annotations for the value and target type. closes astral-sh/ty#1556 ### Before <img width="836" height="601" alt="image" src="https://github.com/user-attachments/assets/a48219bb-58a8-4a83-b290-d09ef50ce5f0" /> ### After <img width="857" height="742" alt="image" src="https://github.com/user-attachments/assets/cfcaa4f4-94fb-459e-8d64-97050dfecb50" /> ## Ecosystem impact Very good! Due to the wider diagnostic range, we now pick up more `# type: ignore` directives that were supposed to suppress an invalid assignment diagnostic. ## Test Plan New snapshot tests
1 parent 7a739d6 commit 5ca9c15

16 files changed

+451
-103
lines changed

.github/workflows/ty-ecosystem-analyzer.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
6868
cd ..
6969
70-
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@11aa5472cf9d6b9e019c401505a093112942d7bf"
70+
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@0aff03414da5d242e97a9f43fb502e085637a4a1"
7171
7272
ecosystem-analyzer \
7373
--repository ruff \

.github/workflows/ty-ecosystem-report.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
5353
cd ..
5454
55-
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@11aa5472cf9d6b9e019c401505a093112942d7bf"
55+
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@0aff03414da5d242e97a9f43fb502e085637a4a1"
5656
5757
ecosystem-analyzer \
5858
--verbose \

crates/ty/docs/rules.md

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

crates/ty/tests/cli/main.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,24 @@ fn test_quiet_output() -> anyhow::Result<()> {
4141
let case = CliTest::with_file("test.py", "x: int = 'foo'")?;
4242

4343
// By default, we emit a diagnostic
44-
assert_cmd_snapshot!(case.command(), @r###"
44+
assert_cmd_snapshot!(case.command(), @r#"
4545
success: false
4646
exit_code: 1
4747
----- stdout -----
4848
error[invalid-assignment]: Object of type `Literal["foo"]` is not assignable to `int`
49-
--> test.py:1:1
49+
--> test.py:1:4
5050
|
5151
1 | x: int = 'foo'
52-
| ^
52+
| --- ^^^^^ Incompatible value of type `Literal["foo"]`
53+
| |
54+
| Declared type
5355
|
5456
info: rule `invalid-assignment` is enabled by default
5557
5658
Found 1 diagnostic
5759
5860
----- stderr -----
59-
"###);
61+
"#);
6062

6163
// With `quiet`, the diagnostic is not displayed, just the summary message
6264
assert_cmd_snapshot!(case.command().arg("--quiet"), @r"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Invalid assignment diagnostics
2+
3+
<!-- snapshot-diagnostics -->
4+
5+
## Annotated assignment
6+
7+
```py
8+
x: int = "three" # error: [invalid-assignment]
9+
```
10+
11+
## Unannotated assignment
12+
13+
```py
14+
x: int
15+
x = "three" # error: [invalid-assignment]
16+
```
17+
18+
## Named expression
19+
20+
```py
21+
x: int
22+
23+
(x := "three") # error: [invalid-assignment]
24+
```
25+
26+
## Multiline expressions
27+
28+
```py
29+
# fmt: off
30+
31+
# error: [invalid-assignment]
32+
x: str = (
33+
1 + 2 + (
34+
3 + 4 + 5
35+
)
36+
)
37+
```
38+
39+
## Multiple targets
40+
41+
```py
42+
x: int
43+
y: str
44+
45+
x, y = ("a", "b") # error: [invalid-assignment]
46+
47+
x, y = (0, 0) # error: [invalid-assignment]
48+
```
49+
50+
## Shadowing of classes and functions
51+
52+
See [shadowing.md](./shadowing.md).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Annotated assignment
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | x: int = "three" # error: [invalid-assignment]
16+
```
17+
18+
# Diagnostics
19+
20+
```
21+
error[invalid-assignment]: Object of type `Literal["three"]` is not assignable to `int`
22+
--> src/mdtest_snippet.py:1:4
23+
|
24+
1 | x: int = "three" # error: [invalid-assignment]
25+
| --- ^^^^^^^ Incompatible value of type `Literal["three"]`
26+
| |
27+
| Declared type
28+
|
29+
info: rule `invalid-assignment` is enabled by default
30+
31+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Multiline expressions
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | # fmt: off
16+
2 |
17+
3 | # error: [invalid-assignment]
18+
4 | x: str = (
19+
5 | 1 + 2 + (
20+
6 | 3 + 4 + 5
21+
7 | )
22+
8 | )
23+
```
24+
25+
# Diagnostics
26+
27+
```
28+
error[invalid-assignment]: Object of type `Literal[15]` is not assignable to `str`
29+
--> src/mdtest_snippet.py:4:4
30+
|
31+
3 | # error: [invalid-assignment]
32+
4 | x: str = (
33+
| ____---___^
34+
| | |
35+
| | Declared type
36+
5 | | 1 + 2 + (
37+
6 | | 3 + 4 + 5
38+
7 | | )
39+
8 | | )
40+
| |_^ Incompatible value of type `Literal[15]`
41+
|
42+
info: rule `invalid-assignment` is enabled by default
43+
44+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Multiple targets
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | x: int
16+
2 | y: str
17+
3 |
18+
4 | x, y = ("a", "b") # error: [invalid-assignment]
19+
5 |
20+
6 | x, y = (0, 0) # error: [invalid-assignment]
21+
```
22+
23+
# Diagnostics
24+
25+
```
26+
error[invalid-assignment]: Object of type `tuple[Literal["a"], Literal["b"]]` is not assignable to `int`
27+
--> src/mdtest_snippet.py:4:1
28+
|
29+
2 | y: str
30+
3 |
31+
4 | x, y = ("a", "b") # error: [invalid-assignment]
32+
| - ^^^^^^^^^^ Incompatible value of type `tuple[Literal["a"], Literal["b"]]`
33+
| |
34+
| Declared type `int`
35+
5 |
36+
6 | x, y = (0, 0) # error: [invalid-assignment]
37+
|
38+
info: rule `invalid-assignment` is enabled by default
39+
40+
```
41+
42+
```
43+
error[invalid-assignment]: Object of type `tuple[Literal[0], Literal[0]]` is not assignable to `str`
44+
--> src/mdtest_snippet.py:6:4
45+
|
46+
4 | x, y = ("a", "b") # error: [invalid-assignment]
47+
5 |
48+
6 | x, y = (0, 0) # error: [invalid-assignment]
49+
| - ^^^^^^ Incompatible value of type `tuple[Literal[0], Literal[0]]`
50+
| |
51+
| Declared type `str`
52+
|
53+
info: rule `invalid-assignment` is enabled by default
54+
55+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Named expression
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | x: int
16+
2 |
17+
3 | (x := "three") # error: [invalid-assignment]
18+
```
19+
20+
# Diagnostics
21+
22+
```
23+
error[invalid-assignment]: Object of type `Literal["three"]` is not assignable to `int`
24+
--> src/mdtest_snippet.py:3:2
25+
|
26+
1 | x: int
27+
2 |
28+
3 | (x := "three") # error: [invalid-assignment]
29+
| - ^^^^^^^ Incompatible value of type `Literal["three"]`
30+
| |
31+
| Declared type `int`
32+
|
33+
info: rule `invalid-assignment` is enabled by default
34+
35+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Unannotated assignment
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | x: int
16+
2 | x = "three" # error: [invalid-assignment]
17+
```
18+
19+
# Diagnostics
20+
21+
```
22+
error[invalid-assignment]: Object of type `Literal["three"]` is not assignable to `int`
23+
--> src/mdtest_snippet.py:2:1
24+
|
25+
1 | x: int
26+
2 | x = "three" # error: [invalid-assignment]
27+
| - ^^^^^^^ Incompatible value of type `Literal["three"]`
28+
| |
29+
| Declared type `int`
30+
|
31+
info: rule `invalid-assignment` is enabled by default
32+
33+
```

0 commit comments

Comments
 (0)