Skip to content

Commit f36262d

Browse files
authored
Fixes how the checker visits typing.cast/typing.NewType arguments (#17538)
1 parent e45f23b commit f36262d

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed

crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,11 @@ def f():
8989
int # TC006
9090
, 6.0
9191
)
92+
93+
94+
def f():
95+
# Keyword arguments
96+
from typing import cast
97+
98+
cast(typ=int, val=3.0) # TC006
99+
cast(val=3.0, typ=int) # TC006

crates/ruff_linter/src/checkers/ast/mod.rs

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,25 +1539,37 @@ impl<'a> Visitor<'a> for Checker<'a> {
15391539
}
15401540
}
15411541
Some(typing::Callable::Cast) => {
1542-
let mut args = arguments.args.iter();
1543-
if let Some(arg) = args.next() {
1544-
self.visit_type_definition(arg);
1545-
1546-
if !self.source_type.is_stub() && self.enabled(Rule::RuntimeCastValue) {
1547-
flake8_type_checking::rules::runtime_cast_value(self, arg);
1542+
for (i, arg) in arguments.arguments_source_order().enumerate() {
1543+
match (i, arg) {
1544+
(0, ArgOrKeyword::Arg(arg)) => self.visit_cast_type_argument(arg),
1545+
(_, ArgOrKeyword::Arg(arg)) => self.visit_non_type_definition(arg),
1546+
(_, ArgOrKeyword::Keyword(Keyword { arg, value, .. })) => {
1547+
if let Some(id) = arg {
1548+
if id == "typ" {
1549+
self.visit_cast_type_argument(value);
1550+
} else {
1551+
self.visit_non_type_definition(value);
1552+
}
1553+
}
1554+
}
15481555
}
15491556
}
1550-
for arg in args {
1551-
self.visit_expr(arg);
1552-
}
15531557
}
15541558
Some(typing::Callable::NewType) => {
1555-
let mut args = arguments.args.iter();
1556-
if let Some(arg) = args.next() {
1557-
self.visit_non_type_definition(arg);
1558-
}
1559-
for arg in args {
1560-
self.visit_type_definition(arg);
1559+
for (i, arg) in arguments.arguments_source_order().enumerate() {
1560+
match (i, arg) {
1561+
(1, ArgOrKeyword::Arg(arg)) => self.visit_type_definition(arg),
1562+
(_, ArgOrKeyword::Arg(arg)) => self.visit_non_type_definition(arg),
1563+
(_, ArgOrKeyword::Keyword(Keyword { arg, value, .. })) => {
1564+
if let Some(id) = arg {
1565+
if id == "tp" {
1566+
self.visit_type_definition(value);
1567+
} else {
1568+
self.visit_non_type_definition(value);
1569+
}
1570+
}
1571+
}
1572+
}
15611573
}
15621574
}
15631575
Some(typing::Callable::TypeVar) => {
@@ -2209,6 +2221,15 @@ impl<'a> Checker<'a> {
22092221
self.semantic.flags = snapshot;
22102222
}
22112223

2224+
/// Visit an [`Expr`], and treat it as the `typ` argument to `typing.cast`.
2225+
fn visit_cast_type_argument(&mut self, arg: &'a Expr) {
2226+
self.visit_type_definition(arg);
2227+
2228+
if !self.source_type.is_stub() && self.enabled(Rule::RuntimeCastValue) {
2229+
flake8_type_checking::rules::runtime_cast_value(self, arg);
2230+
}
2231+
}
2232+
22122233
/// Visit an [`Expr`], and treat it as a boolean test. This is useful for detecting whether an
22132234
/// expressions return value is significant, or whether the calling context only relies on
22142235
/// its truthiness.

crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.py.snap

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,37 @@ TC006.py:89:9: TC006 [*] Add quotes to type expression in `typing.cast()`
212212
89 |+ "int" # TC006
213213
90 90 | , 6.0
214214
91 91 | )
215+
92 92 |
216+
217+
TC006.py:98:14: TC006 [*] Add quotes to type expression in `typing.cast()`
218+
|
219+
96 | from typing import cast
220+
97 |
221+
98 | cast(typ=int, val=3.0) # TC006
222+
| ^^^ TC006
223+
99 | cast(val=3.0, typ=int) # TC006
224+
|
225+
= help: Add quotes
226+
227+
Safe fix
228+
95 95 | # Keyword arguments
229+
96 96 | from typing import cast
230+
97 97 |
231+
98 |- cast(typ=int, val=3.0) # TC006
232+
98 |+ cast(typ="int", val=3.0) # TC006
233+
99 99 | cast(val=3.0, typ=int) # TC006
234+
235+
TC006.py:99:23: TC006 [*] Add quotes to type expression in `typing.cast()`
236+
|
237+
98 | cast(typ=int, val=3.0) # TC006
238+
99 | cast(val=3.0, typ=int) # TC006
239+
| ^^^ TC006
240+
|
241+
= help: Add quotes
242+
243+
Safe fix
244+
96 96 | from typing import cast
245+
97 97 |
246+
98 98 | cast(typ=int, val=3.0) # TC006
247+
99 |- cast(val=3.0, typ=int) # TC006
248+
99 |+ cast(val=3.0, typ="int") # TC006

0 commit comments

Comments
 (0)