Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,48 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/type_qualifiers/final.md
## mdtest_snippet.py

```
1 | from typing import Final
2 |
3 | MY_CONSTANT: Final[int] = 1
4 |
5 | # more code
6 |
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
1 | from typing import Final
2 |
3 | MY_CONSTANT: Final[int] = 1
4 |
5 | # more code
6 |
7 | MY_CONSTANT = 2 # error: [invalid-assignment]
8 | from _stat import ST_INO
9 |
10 | ST_INO = 1 # error: [invalid-assignment]
```

# Diagnostics

```
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
2 |
3 | MY_CONSTANT: Final[int] = 1
| --------------------------- Original definition
| ---------- Symbol declared as `Final` here
4 |
5 | # more code
6 |
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

```
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ class C:

<!-- snapshot-diagnostics -->

Annotated assignment:

```py
from typing import Final

Expand All @@ -270,4 +272,12 @@ MY_CONSTANT: Final[int] = 1
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
28 changes: 23 additions & 5 deletions crates/ty_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1663,7 +1663,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {

if !is_local || previous_definition.is_some() {
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!(
"Reassignment of `Final` symbol `{place}` is not allowed"
));
Expand All @@ -1676,10 +1679,25 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// module, but that information is currently not threaded through attribute
// lookup.
if !previous_definition.kind(db).is_import() {
let range = previous_definition.full_range(self.db(), self.module());
diagnostic.annotate(
self.context.secondary(range).message("Original definition"),
);
if let DefinitionKind::AnnotatedAssignment(assignment) =
previous_definition.kind(db)
{
let range = assignment.annotation(self.module()).range();
diagnostic.annotate(
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"),
);
Comment on lines +1692 to +1698
Copy link
Contributor Author

@sharkdp sharkdp Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fallback that currently can't be triggered, I believe, because there are no other annotated definitions that would allow a Final annotation. I guess it doesn't hurt to leave this in here, but I can also remove it.

In particular, function parameters are not allowed to be annotated with Final:

Final may only be used in assignments or variable annotations. Using it in any other position is an error. In particular, Final can’t be used in annotations for function arguments

https://typing.python.org/en/latest/spec/qualifiers.html#uppercase-final

This is something that we still have to implement (added a note to the ticket for typing.Final).

}
diagnostic.set_primary_message("Symbol later reassigned here");
}
}
}
Expand Down
Loading