mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-19 11:45:40 +00:00
[flake8-future-annotations] Add autofix (FA100) (#18903)
Summary -- This PR resolves the easiest part of https://github.com/astral-sh/ruff/issues/18502 by adding an autofix that just adds `from __future__ import annotations` at the top of the file, in the same way as FA102, which already has an identical unsafe fix. Test Plan -- Existing snapshots, updated to add the fixes.
This commit is contained in:
parent
c1fed55d51
commit
7783cea14f
6 changed files with 81 additions and 17 deletions
|
|
@ -1,9 +1,11 @@
|
|||
use ruff_diagnostics::Fix;
|
||||
use ruff_python_ast::Expr;
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_semantic::{MemberNameImport, NameImport};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Violation;
|
||||
use crate::AlwaysFixableViolation;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
|
|
@ -61,6 +63,10 @@ use crate::checkers::ast::Checker;
|
|||
/// def func(obj: dict[str, int | None]) -> None: ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe, as adding `from __future__ import annotations`
|
||||
/// may change the semantics of the program.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `target-version`
|
||||
#[derive(ViolationMetadata)]
|
||||
|
|
@ -68,12 +74,16 @@ pub(crate) struct FutureRewritableTypeAnnotation {
|
|||
name: String,
|
||||
}
|
||||
|
||||
impl Violation for FutureRewritableTypeAnnotation {
|
||||
impl AlwaysFixableViolation for FutureRewritableTypeAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FutureRewritableTypeAnnotation { name } = self;
|
||||
format!("Add `from __future__ import annotations` to simplify `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
"Add `from __future__ import annotations`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// FA100
|
||||
|
|
@ -83,7 +93,17 @@ pub(crate) fn future_rewritable_type_annotation(checker: &Checker, expr: &Expr)
|
|||
.resolve_qualified_name(expr)
|
||||
.map(|binding| binding.to_string());
|
||||
|
||||
if let Some(name) = name {
|
||||
checker.report_diagnostic(FutureRewritableTypeAnnotation { name }, expr.range());
|
||||
}
|
||||
let Some(name) = name else { return };
|
||||
|
||||
let import = &NameImport::ImportFrom(MemberNameImport::member(
|
||||
"__future__".to_string(),
|
||||
"annotations".to_string(),
|
||||
));
|
||||
checker
|
||||
.report_diagnostic(FutureRewritableTypeAnnotation { name }, expr.range())
|
||||
.set_fix(Fix::unsafe_edit(
|
||||
checker
|
||||
.importer()
|
||||
.add_import(import, ruff_text_size::TextSize::default()),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,32 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
edge_case.py:5:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
edge_case.py:5:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
5 | def main(_: List[int]) -> None:
|
||||
| ^^^^ FA100
|
||||
6 | a_list: t.List[str] = []
|
||||
7 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
edge_case.py:6:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | from typing import List
|
||||
2 3 | import typing as t
|
||||
3 4 |
|
||||
|
||||
edge_case.py:6:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
5 | def main(_: List[int]) -> None:
|
||||
6 | a_list: t.List[str] = []
|
||||
| ^^^^^^ FA100
|
||||
7 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | from typing import List
|
||||
2 3 | import typing as t
|
||||
3 4 |
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
from_typing_import.py:5:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
from_typing_import.py:5:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
4 | def main() -> None:
|
||||
5 | a_list: List[str] = []
|
||||
| ^^^^ FA100
|
||||
6 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | from typing import List
|
||||
2 3 |
|
||||
3 4 |
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
from_typing_import_many.py:5:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
from_typing_import_many.py:5:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
4 | def main() -> None:
|
||||
5 | a_list: List[Optional[str]] = []
|
||||
|
|
@ -10,8 +9,15 @@ from_typing_import_many.py:5:13: FA100 Add `from __future__ import annotations`
|
|||
6 | a_list.append("hello")
|
||||
7 | a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {})
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
from_typing_import_many.py:5:18: FA100 Add `from __future__ import annotations` to simplify `typing.Optional`
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | from typing import Dict, List, Optional, Set, Union, cast
|
||||
2 3 |
|
||||
3 4 |
|
||||
|
||||
from_typing_import_many.py:5:18: FA100 [*] Add `from __future__ import annotations` to simplify `typing.Optional`
|
||||
|
|
||||
4 | def main() -> None:
|
||||
5 | a_list: List[Optional[str]] = []
|
||||
|
|
@ -19,3 +25,10 @@ from_typing_import_many.py:5:18: FA100 Add `from __future__ import annotations`
|
|||
6 | a_list.append("hello")
|
||||
7 | a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {})
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | from typing import Dict, List, Optional, Set, Union, cast
|
||||
2 3 |
|
||||
3 4 |
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
import_typing.py:5:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
import_typing.py:5:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
4 | def main() -> None:
|
||||
5 | a_list: typing.List[str] = []
|
||||
| ^^^^^^^^^^^ FA100
|
||||
6 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | import typing
|
||||
2 3 |
|
||||
3 4 |
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
import_typing_as.py:5:13: FA100 Add `from __future__ import annotations` to simplify `typing.List`
|
||||
import_typing_as.py:5:13: FA100 [*] Add `from __future__ import annotations` to simplify `typing.List`
|
||||
|
|
||||
4 | def main() -> None:
|
||||
5 | a_list: t.List[str] = []
|
||||
| ^^^^^^ FA100
|
||||
6 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | import typing as t
|
||||
2 3 |
|
||||
3 4 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue