-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement flake8-future-annotations
FA100
#3979
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
0456611
Implement flake8-future-annotations
TylerYep d5c81de
Remove commented code, remove autofix for now
TylerYep cc9e9ce
Remove .python-version
TylerYep 9acaee8
Merge branch 'main' into pr
charliermarsh 73b4653
Defer context checks if future is enabled
charliermarsh 69abeb0
Move list into typing
charliermarsh 4373751
Remove Deque and DefaultDict
TylerYep 822cdb7
Merge branch 'main' into pr
charliermarsh fd41668
Merge branch 'main' into pr
TylerYep 8bdd0c1
Fix fmt
TylerYep 4569ac6
Revert "Remove Deque and DefaultDict"
TylerYep e6775ea
Fix mkdocs
TylerYep d872a25
Fix mkdocs
TylerYep b82b04f
Merge branch 'main' into pr
TylerYep 92cc016
Rebase onto main
TylerYep 64e2012
Fix lint error
TylerYep b52241f
Merge branch 'main' into pr
charliermarsh af9be69
Fix rebased future_annotations() check
charliermarsh e16416b
Merge branch 'main' into pr
TylerYep 33a73b9
Fix bad rebase
TylerYep d47ef9a
Merge branch 'main' into pr
charliermarsh 41f65c4
Include PEP 604
charliermarsh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/edge_case.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from typing import List | ||
import typing as t | ||
|
||
|
||
def main(_: List[int]) -> None: | ||
a_list: t.List[str] = [] | ||
a_list.append("hello") |
6 changes: 6 additions & 0 deletions
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from typing import List | ||
|
||
|
||
def main() -> None: | ||
a_list: List[str] = [] | ||
a_list.append("hello") |
8 changes: 8 additions & 0 deletions
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/from_typing_import_many.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from typing import Dict, List, Optional, Set, Union, cast | ||
|
||
|
||
def main() -> None: | ||
a_list: List[Optional[str]] = [] | ||
a_list.append("hello") | ||
a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {}) | ||
a_dict[1] = {True, False} |
6 changes: 6 additions & 0 deletions
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import typing | ||
|
||
|
||
def main() -> None: | ||
a_list: typing.List[str] = [] | ||
a_list.append("hello") |
6 changes: 6 additions & 0 deletions
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/import_typing_as.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import typing as t | ||
|
||
|
||
def main() -> None: | ||
a_list: t.List[str] = [] | ||
a_list.append("hello") |
7 changes: 7 additions & 0 deletions
7
...ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_lowercase.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
def main() -> None: | ||
a_list: list[str] = [] | ||
a_list.append("hello") | ||
|
||
|
||
def hello(y: dict[str, int]) -> None: | ||
del y |
7 changes: 7 additions & 0 deletions
7
crates/ruff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
def main() -> None: | ||
a_list: list[str] | None = [] | ||
a_list.append("hello") | ||
|
||
|
||
def hello(y: dict[str, int] | None) -> None: | ||
del y |
8 changes: 8 additions & 0 deletions
8
...ff/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union_inner.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
def main() -> None: | ||
a_list: list[str | None] = [] | ||
a_list.append("hello") | ||
|
||
|
||
def hello(y: dict[str | None, int]) -> None: | ||
z: tuple[str, str | None, str] = tuple(y) | ||
del z |
3 changes: 3 additions & 0 deletions
3
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_no_types.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
def main() -> str: | ||
a_str = "hello" | ||
return a_str |
10 changes: 10 additions & 0 deletions
10
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_non_simplifiable_types.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from typing import NamedTuple | ||
|
||
|
||
class Stuff(NamedTuple): | ||
x: int | ||
|
||
|
||
def main() -> None: | ||
a_list = Stuff(5) | ||
print(a_list) |
6 changes: 6 additions & 0 deletions
6
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_uses_future.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from __future__ import annotations | ||
|
||
|
||
def main() -> None: | ||
a_list: list[str] = [] | ||
a_list.append("hello") |
8 changes: 8 additions & 0 deletions
8
crates/ruff/resources/test/fixtures/flake8_future_annotations/ok_variable_name.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import typing | ||
|
||
IRRELEVANT = typing.TypeVar | ||
|
||
|
||
def main() -> None: | ||
List: list[str] = [] | ||
List.append("hello") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//! Rules from [flake8-future-annotations](https://pypi.org/project/flake8-future-annotations/). | ||
pub(crate) mod rules; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::path::Path; | ||
|
||
use anyhow::Result; | ||
use test_case::test_case; | ||
|
||
use crate::registry::Rule; | ||
use crate::settings::types::PythonVersion; | ||
use crate::test::test_path; | ||
use crate::{assert_messages, settings}; | ||
|
||
#[test_case(Path::new("edge_case.py"); "edge_case")] | ||
#[test_case(Path::new("from_typing_import.py"); "from_typing_import")] | ||
#[test_case(Path::new("from_typing_import_many.py"); "from_typing_import_many")] | ||
#[test_case(Path::new("import_typing.py"); "import_typing")] | ||
#[test_case(Path::new("import_typing_as.py"); "import_typing_as")] | ||
#[test_case(Path::new("no_future_import_uses_lowercase.py"); "no_future_import_uses_lowercase")] | ||
#[test_case(Path::new("no_future_import_uses_union.py"); "no_future_import_uses_union")] | ||
#[test_case(Path::new("no_future_import_uses_union_inner.py"); "no_future_import_uses_union_inner")] | ||
#[test_case(Path::new("ok_no_types.py"); "ok_no_types")] | ||
#[test_case(Path::new("ok_non_simplifiable_types.py"); "ok_non_simplifiable_types")] | ||
#[test_case(Path::new("ok_uses_future.py"); "ok_uses_future")] | ||
#[test_case(Path::new("ok_variable_name.py"); "ok_variable_name")] | ||
fn rules(path: &Path) -> Result<()> { | ||
let snapshot = path.to_string_lossy().into_owned(); | ||
let diagnostics = test_path( | ||
Path::new("flake8_future_annotations").join(path).as_path(), | ||
&settings::Settings { | ||
target_version: PythonVersion::Py37, | ||
..settings::Settings::for_rule(Rule::MissingFutureAnnotationsImport) | ||
}, | ||
)?; | ||
assert_messages!(snapshot, diagnostics); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use rustpython_parser::ast::Expr; | ||
|
||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::call_path::format_call_path; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for missing `from __future__ import annotations` imports upon | ||
/// detecting type annotations that can be written more succinctly under | ||
/// PEP 563. | ||
/// | ||
/// ## Why is this bad? | ||
/// PEP 563 enabled the use of a number of convenient type annotations, such as | ||
/// `list[str]` instead of `List[str]`, or `str | None` instead of | ||
/// `Optional[str]`. However, these annotations are only available on Python | ||
/// 3.9 and higher, _unless_ the `from __future__ import annotations` import is present. | ||
/// | ||
/// By adding the `__future__` import, the pyupgrade rules can automatically | ||
/// migrate existing code to use the new syntax, even for older Python versions. | ||
/// This rule thus pairs well with pyupgrade and with Ruff's pyupgrade rules. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from typing import List, Dict, Optional | ||
/// | ||
/// | ||
/// def function(a_dict: Dict[str, Optional[int]]) -> None: | ||
/// a_list: List[str] = [] | ||
/// a_list.append("hello") | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from __future__ import annotations | ||
/// | ||
/// from typing import List, Dict, Optional | ||
/// | ||
/// | ||
/// def function(a_dict: Dict[str, Optional[int]]) -> None: | ||
/// a_list: List[str] = [] | ||
/// a_list.append("hello") | ||
/// ``` | ||
/// | ||
/// After running the additional pyupgrade rules: | ||
/// ```python | ||
/// from __future__ import annotations | ||
/// | ||
/// | ||
/// def function(a_dict: dict[str, int | None]) -> None: | ||
/// a_list: list[str] = [] | ||
/// a_list.append("hello") | ||
/// ``` | ||
#[violation] | ||
pub struct MissingFutureAnnotationsImport { | ||
name: String, | ||
} | ||
|
||
impl Violation for MissingFutureAnnotationsImport { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let MissingFutureAnnotationsImport { name } = self; | ||
format!("Missing `from __future__ import annotations`, but uses `{name}`") | ||
} | ||
} | ||
|
||
/// FA100 | ||
pub(crate) fn missing_future_annotations(checker: &mut Checker, expr: &Expr) { | ||
if let Some(binding) = checker.ctx.resolve_call_path(expr) { | ||
checker.diagnostics.push(Diagnostic::new( | ||
MissingFutureAnnotationsImport { | ||
name: format_call_path(&binding), | ||
}, | ||
expr.range(), | ||
)); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...re_annotations/snapshots/ruff__rules__flake8_future_annotations__tests__edge_case.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
source: crates/ruff/src/rules/flake8_future_annotations/mod.rs | ||
--- | ||
edge_case.py:6:13: FA100 Missing `from __future__ import annotations`, but uses `typing.List` | ||
| | ||
6 | def main(_: List[int]) -> None: | ||
7 | a_list: t.List[str] = [] | ||
| ^^^^^^ FA100 | ||
8 | a_list.append("hello") | ||
| | ||
|
||
|
12 changes: 12 additions & 0 deletions
12
...tions/snapshots/ruff__rules__flake8_future_annotations__tests__from_typing_import.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
source: crates/ruff/src/rules/flake8_future_annotations/mod.rs | ||
--- | ||
from_typing_import.py:5:13: FA100 Missing `from __future__ import annotations`, but uses `typing.List` | ||
| | ||
5 | def main() -> None: | ||
6 | a_list: List[str] = [] | ||
| ^^^^ FA100 | ||
7 | a_list.append("hello") | ||
| | ||
|
||
|
22 changes: 22 additions & 0 deletions
22
.../snapshots/ruff__rules__flake8_future_annotations__tests__from_typing_import_many.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
source: crates/ruff/src/rules/flake8_future_annotations/mod.rs | ||
--- | ||
from_typing_import_many.py:5:13: FA100 Missing `from __future__ import annotations`, but uses `typing.List` | ||
| | ||
5 | def main() -> None: | ||
6 | a_list: List[Optional[str]] = [] | ||
| ^^^^ FA100 | ||
7 | a_list.append("hello") | ||
8 | a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {}) | ||
| | ||
|
||
from_typing_import_many.py:5:18: FA100 Missing `from __future__ import annotations`, but uses `typing.Optional` | ||
| | ||
5 | def main() -> None: | ||
6 | a_list: List[Optional[str]] = [] | ||
| ^^^^^^^^ FA100 | ||
7 | a_list.append("hello") | ||
8 | a_dict = cast(Dict[int | None, Union[int, Set[bool]]], {}) | ||
| | ||
|
||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is a lot to read, I'd like to clean this up... But the idea is: mirror the logic below for
Rule::NonPEP585Annotation
, but flip the!self.ctx.future_annotations()
. So we're asking: would we trigger here if__future__
annotations were enabled?