mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:18 +00:00
[ty] Improved diagnostic for reassignments of Final
symbols (#19214)
## Summary Implement [this suggestion](https://github.com/astral-sh/ruff/pull/19178#discussion_r2192658146) by @AlexWaygood. 
This commit is contained in:
parent
a8f2c26143
commit
1a099886ab
3 changed files with 60 additions and 15 deletions
|
@ -12,30 +12,47 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/type_qualifiers/final.md
|
||||||
## mdtest_snippet.py
|
## mdtest_snippet.py
|
||||||
|
|
||||||
```
|
```
|
||||||
1 | from typing import Final
|
1 | from typing import Final
|
||||||
2 |
|
2 |
|
||||||
3 | MY_CONSTANT: Final[int] = 1
|
3 | MY_CONSTANT: Final[int] = 1
|
||||||
4 |
|
4 |
|
||||||
5 | # more code
|
5 | # more code
|
||||||
6 |
|
6 |
|
||||||
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
|
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
|
||||||
|
8 | from _stat import ST_INO
|
||||||
|
9 |
|
||||||
|
10 | ST_INO = 1 # error: [invalid-assignment]
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
error[invalid-assignment]: Reassignment of `Final` symbol `MY_CONSTANT` is not allowed
|
error[invalid-assignment]: Reassignment of `Final` symbol `MY_CONSTANT` is not allowed
|
||||||
--> src/mdtest_snippet.py:3:1
|
--> src/mdtest_snippet.py:3:14
|
||||||
|
|
|
|
||||||
1 | from typing import Final
|
1 | from typing import Final
|
||||||
2 |
|
2 |
|
||||||
3 | MY_CONSTANT: Final[int] = 1
|
3 | MY_CONSTANT: Final[int] = 1
|
||||||
| --------------------------- Original definition
|
| ---------- Symbol declared as `Final` here
|
||||||
4 |
|
4 |
|
||||||
5 | # more code
|
5 | # more code
|
||||||
6 |
|
6 |
|
||||||
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
|
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
|
||||||
| ^^^^^^^^^^^ Reassignment of `Final` symbol
|
| ^^^^^^^^^^^^^^^ Symbol later reassigned here
|
||||||
|
8 | from _stat import ST_INO
|
||||||
|
|
|
||||||
|
info: rule `invalid-assignment` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-assignment]: Reassignment of `Final` symbol `ST_INO` is not allowed
|
||||||
|
--> src/mdtest_snippet.py:10:1
|
||||||
|
|
|
||||||
|
8 | from _stat import ST_INO
|
||||||
|
9 |
|
||||||
|
10 | ST_INO = 1 # error: [invalid-assignment]
|
||||||
|
| ^^^^^^^^^^ Reassignment of `Final` symbol
|
||||||
|
|
|
|
||||||
info: rule `invalid-assignment` is enabled by default
|
info: rule `invalid-assignment` is enabled by default
|
||||||
|
|
||||||
|
|
|
@ -260,6 +260,8 @@ class C:
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
Annotated assignment:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
|
@ -270,4 +272,12 @@ MY_CONSTANT: Final[int] = 1
|
||||||
MY_CONSTANT = 2 # error: [invalid-assignment]
|
MY_CONSTANT = 2 # error: [invalid-assignment]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Imported `Final` symbol:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from _stat import ST_INO
|
||||||
|
|
||||||
|
ST_INO = 1 # error: [invalid-assignment]
|
||||||
|
```
|
||||||
|
|
||||||
[`typing.final`]: https://docs.python.org/3/library/typing.html#typing.Final
|
[`typing.final`]: https://docs.python.org/3/library/typing.html#typing.Final
|
||||||
|
|
|
@ -1663,7 +1663,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
if !is_local || previous_definition.is_some() {
|
if !is_local || previous_definition.is_some() {
|
||||||
let place = place_table.place_expr(binding.place(db));
|
let place = place_table.place_expr(binding.place(db));
|
||||||
if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, node) {
|
if let Some(builder) = self.context.report_lint(
|
||||||
|
&INVALID_ASSIGNMENT,
|
||||||
|
binding.full_range(self.db(), self.module()),
|
||||||
|
) {
|
||||||
let mut diagnostic = builder.into_diagnostic(format_args!(
|
let mut diagnostic = builder.into_diagnostic(format_args!(
|
||||||
"Reassignment of `Final` symbol `{place}` is not allowed"
|
"Reassignment of `Final` symbol `{place}` is not allowed"
|
||||||
));
|
));
|
||||||
|
@ -1676,10 +1679,25 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// module, but that information is currently not threaded through attribute
|
// module, but that information is currently not threaded through attribute
|
||||||
// lookup.
|
// lookup.
|
||||||
if !previous_definition.kind(db).is_import() {
|
if !previous_definition.kind(db).is_import() {
|
||||||
let range = previous_definition.full_range(self.db(), self.module());
|
if let DefinitionKind::AnnotatedAssignment(assignment) =
|
||||||
|
previous_definition.kind(db)
|
||||||
|
{
|
||||||
|
let range = assignment.annotation(self.module()).range();
|
||||||
diagnostic.annotate(
|
diagnostic.annotate(
|
||||||
self.context.secondary(range).message("Original definition"),
|
self.context
|
||||||
|
.secondary(range)
|
||||||
|
.message("Symbol declared as `Final` here"),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
let range =
|
||||||
|
previous_definition.full_range(self.db(), self.module());
|
||||||
|
diagnostic.annotate(
|
||||||
|
self.context
|
||||||
|
.secondary(range)
|
||||||
|
.message("Symbol declared as `Final` here"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
diagnostic.set_primary_message("Symbol later reassigned here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue