Skip to content
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

[pyflakes] Visit forward annotations in TypeAliasType as types (F401) #15829

Merged
merged 7 commits into from
Jan 30, 2025
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
67 changes: 67 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pyflakes/F401_34.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Regression tests for https://github.com/astral-sh/ruff/issues/15812"""


def f():
from typing import Union

from typing_extensions import TypeAliasType

Json = TypeAliasType(
"Json",
"Union[dict[str, Json], list[Json], str, int, float, bool, None]",
)


def f():
from typing import Union

from typing_extensions import TypeAliasType, TypeVar

T = TypeVar("T")
V = TypeVar("V")
Json = TypeAliasType(
"Json",
"Union[dict[str, Json], list[Json], str, int, float, bool, T, V, None]",
type_params=(T, V),
)


def f():
from typing import Union

from typing_extensions import TypeAliasType

Json = TypeAliasType(
value="Union[dict[str, Json], list[Json], str, int, float, bool, None]",
name="Json",
)


# strictly speaking it's a false positive to emit F401 for both of these, but
# we can't really be expected to understand that the strings here are type
# expressions (and type checkers probably wouldn't understand them as type
# expressions either!)
def f():
from typing import Union

from typing_extensions import TypeAliasType

args = [
"Json",
"Union[dict[str, Json], list[Json], str, int, float, bool, None]",
]

Json = TypeAliasType(*args)


def f():
from typing import Union

from typing_extensions import TypeAliasType

kwargs = {
"name": "Json",
"value": "Union[dict[str, Json], list[Json], str, int, float, bool, None]",
}

Json = TypeAliasType(**kwargs)
29 changes: 26 additions & 3 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::str::Quote;
use ruff_python_ast::visitor::{walk_except_handler, walk_pattern, Visitor};
use ruff_python_ast::{
self as ast, AnyParameterRef, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext,
FStringElement, Keyword, MatchCase, ModModule, Parameter, Parameters, Pattern, Stmt, Suite,
UnaryOp,
self as ast, AnyParameterRef, ArgOrKeyword, Comprehension, ElifElseClause, ExceptHandler, Expr,
ExprContext, FStringElement, Keyword, MatchCase, ModModule, Parameter, Parameters, Pattern,
Stmt, Suite, UnaryOp,
};
use ruff_python_ast::{helpers, str, visitor, PySourceType};
use ruff_python_codegen::{Generator, Stylist};
Expand Down Expand Up @@ -1269,6 +1269,11 @@ impl<'a> Visitor<'a> for Checker<'a> {
.match_typing_qualified_name(&qualified_name, "TypeVar")
{
Some(typing::Callable::TypeVar)
} else if self
.semantic
.match_typing_qualified_name(&qualified_name, "TypeAliasType")
{
Some(typing::Callable::TypeAliasType)
} else if self
.semantic
.match_typing_qualified_name(&qualified_name, "NamedTuple")
Expand Down Expand Up @@ -1354,6 +1359,24 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
}
Some(typing::Callable::TypeAliasType) => {
// Ex) TypeAliasType("Json", "Union[dict[str, Json]]", type_params=())
for (i, arg) in arguments.arguments_source_order().enumerate() {
match (i, arg) {
(1, ArgOrKeyword::Arg(arg)) => self.visit_type_definition(arg),
(_, ArgOrKeyword::Arg(arg)) => self.visit_non_type_definition(arg),
(_, ArgOrKeyword::Keyword(Keyword { arg, value, .. })) => {
if let Some(id) = arg {
if matches!(&**id, "value" | "type_params") {
self.visit_type_definition(value);
} else {
self.visit_non_type_definition(value);
}
}
}
}
}
}
Some(typing::Callable::NamedTuple) => {
// Ex) NamedTuple("a", [("a", int)])
let mut args = arguments.args.iter();
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pyflakes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod tests {
#[test_case(Rule::UnusedImport, Path::new("F401_22.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_23.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_32.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_34.py"))]
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"))]
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.ipynb"))]
#[test_case(Rule::UndefinedLocalWithImportStar, Path::new("F403.py"))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F401_34.py:45:24: F401 [*] `typing.Union` imported but unused
|
43 | # expressions either!)
44 | def f():
45 | from typing import Union
| ^^^^^ F401
46 |
47 | from typing_extensions import TypeAliasType
|
= help: Remove unused import: `typing.Union`

ℹ Safe fix
42 42 | # expressions (and type checkers probably wouldn't understand them as type
43 43 | # expressions either!)
44 44 | def f():
45 |- from typing import Union
46 45 |
47 46 | from typing_extensions import TypeAliasType
48 47 |

F401_34.py:58:24: F401 [*] `typing.Union` imported but unused
|
57 | def f():
58 | from typing import Union
| ^^^^^ F401
59 |
60 | from typing_extensions import TypeAliasType
|
= help: Remove unused import: `typing.Union`

ℹ Safe fix
55 55 |
56 56 |
57 57 | def f():
58 |- from typing import Union
59 58 |
60 59 | from typing_extensions import TypeAliasType
61 60 |
1 change: 1 addition & 0 deletions crates/ruff_python_semantic/src/analyze/typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum Callable {
NamedTuple,
TypedDict,
MypyExtension,
TypeAliasType,
}

#[derive(Debug, Copy, Clone)]
Expand Down
Loading