red_knot_python_semantic: migrate INVALID_ASSIGNMENT for shadowing

We mostly keep things the same here, but the message has been moved from
the annotation to the diagnostic's top-line message. I think this is
perhaps a little worse, but some bigger improvements could be made here.
Indeed, we could perhaps even add a "fix" here.
This commit is contained in:
Andrew Gallant 2025-04-16 10:29:41 -04:00 committed by Andrew Gallant
parent 890ba725d9
commit 6dc2d29966
9 changed files with 109 additions and 17 deletions

View file

@ -0,0 +1,19 @@
# Shadowing
<!-- snapshot-diagnostics -->
## Implicit class shadowing
```py
class C: ...
C = 1 # error: [invalid-assignment]
```
## Implicit function shadowing
```py
def f(): ...
f = 1 # error: [invalid-assignment]
```

View file

@ -5,7 +5,7 @@
```py
class C: ...
C = 1 # error: "Implicit shadowing of class `C`; annotate to make it explicit if this is intentional"
C = 1 # error: "Implicit shadowing of class `C`"
```
## Explicit

View file

@ -15,7 +15,7 @@ def f(x: str):
```py
def f(): ...
f = 1 # error: "Implicit shadowing of function `f`; annotate to make it explicit if this is intentional"
f = 1 # error: "Implicit shadowing of function `f`"
```
## Explicit shadowing

View file

@ -26,13 +26,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/attrib
# Diagnostics
```
error: lint:invalid-assignment
error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
--> /src/mdtest_snippet.py:6:1
|
4 | instance = C()
5 | instance.attr = 1 # fine
6 | instance.attr = "wrong" # error: [invalid-assignment]
| ^^^^^^^^^^^^^ Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
| ^^^^^^^^^^^^^
7 |
8 | C.attr = 1 # fine
|
@ -40,12 +40,12 @@ error: lint:invalid-assignment
```
```
error: lint:invalid-assignment
error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
--> /src/mdtest_snippet.py:9:1
|
8 | C.attr = 1 # fine
9 | C.attr = "wrong" # error: [invalid-assignment]
| ^^^^^^ Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
| ^^^^^^
|
```

View file

@ -26,13 +26,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/attrib
# Diagnostics
```
error: lint:invalid-assignment
error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
--> /src/mdtest_snippet.py:7:1
|
5 | instance = C()
6 | instance.attr = 1 # fine
7 | instance.attr = "wrong" # error: [invalid-assignment]
| ^^^^^^^^^^^^^ Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
| ^^^^^^^^^^^^^
8 |
9 | C.attr = 1 # error: [invalid-attribute-access]
|

View file

@ -27,12 +27,12 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/attrib
# Diagnostics
```
error: lint:invalid-assignment
error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
--> /src/mdtest_snippet.py:7:1
|
6 | C.attr = 1 # fine
7 | C.attr = "wrong" # error: [invalid-assignment]
| ^^^^^^ Object of type `Literal["wrong"]` is not assignable to attribute `attr` of type `int`
| ^^^^^^
8 |
9 | instance = C()
|

View file

@ -0,0 +1,33 @@
---
source: crates/red_knot_test/src/lib.rs
expression: snapshot
---
---
mdtest name: shadowing.md - Shadowing - Implicit class shadowing
mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/shadowing.md
---
# Python source files
## mdtest_snippet.py
```
1 | class C: ...
2 |
3 | C = 1 # error: [invalid-assignment]
```
# Diagnostics
```
error: lint:invalid-assignment: Implicit shadowing of class `C`
--> /src/mdtest_snippet.py:3:1
|
1 | class C: ...
2 |
3 | C = 1 # error: [invalid-assignment]
| ^
|
info: Annotate to make it explicit if this is intentional
```

View file

@ -0,0 +1,33 @@
---
source: crates/red_knot_test/src/lib.rs
expression: snapshot
---
---
mdtest name: shadowing.md - Shadowing - Implicit function shadowing
mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/shadowing.md
---
# Python source files
## mdtest_snippet.py
```
1 | def f(): ...
2 |
3 | f = 1 # error: [invalid-assignment]
```
# Diagnostics
```
error: lint:invalid-assignment: Implicit shadowing of function `f`
--> /src/mdtest_snippet.py:3:1
|
1 | def f(): ...
2 |
3 | f = 1 # error: [invalid-assignment]
| ^
|
info: Annotate to make it explicit if this is intentional
```

View file

@ -1116,19 +1116,26 @@ fn report_invalid_assignment_with_message(
target_ty: Type,
message: std::fmt::Arguments,
) {
let Some(builder) = context.report_lint(&INVALID_ASSIGNMENT, node) else {
return;
};
match target_ty {
Type::ClassLiteral(class) => {
context.report_lint_old(&INVALID_ASSIGNMENT, node, format_args!(
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
class.name(context.db())));
let mut diag = builder.into_diagnostic(format_args!(
"Implicit shadowing of class `{}`",
class.name(context.db()),
));
diag.info("Annotate to make it explicit if this is intentional");
}
Type::FunctionLiteral(function) => {
context.report_lint_old(&INVALID_ASSIGNMENT, node, format_args!(
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
function.name(context.db())));
let mut diag = builder.into_diagnostic(format_args!(
"Implicit shadowing of function `{}`",
function.name(context.db()),
));
diag.info("Annotate to make it explicit if this is intentional");
}
_ => {
context.report_lint_old(&INVALID_ASSIGNMENT, node, message);
builder.into_diagnostic(message);
}
}
}