diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.py index cf10553c49..a321f2a30e 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.py @@ -89,3 +89,11 @@ def f(): int # TC006 , 6.0 ) + + +def f(): + # Keyword arguments + from typing import cast + + cast(typ=int, val=3.0) # TC006 + cast(val=3.0, typ=int) # TC006 diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index ebff124383..10784403e4 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1539,25 +1539,37 @@ impl<'a> Visitor<'a> for Checker<'a> { } } Some(typing::Callable::Cast) => { - let mut args = arguments.args.iter(); - if let Some(arg) = args.next() { - self.visit_type_definition(arg); - - if !self.source_type.is_stub() && self.enabled(Rule::RuntimeCastValue) { - flake8_type_checking::rules::runtime_cast_value(self, arg); + for (i, arg) in arguments.arguments_source_order().enumerate() { + match (i, arg) { + (0, ArgOrKeyword::Arg(arg)) => self.visit_cast_type_argument(arg), + (_, ArgOrKeyword::Arg(arg)) => self.visit_non_type_definition(arg), + (_, ArgOrKeyword::Keyword(Keyword { arg, value, .. })) => { + if let Some(id) = arg { + if id == "typ" { + self.visit_cast_type_argument(value); + } else { + self.visit_non_type_definition(value); + } + } + } } } - for arg in args { - self.visit_expr(arg); - } } Some(typing::Callable::NewType) => { - let mut args = arguments.args.iter(); - if let Some(arg) = args.next() { - self.visit_non_type_definition(arg); - } - for arg in args { - self.visit_type_definition(arg); + 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 id == "tp" { + self.visit_type_definition(value); + } else { + self.visit_non_type_definition(value); + } + } + } + } } } Some(typing::Callable::TypeVar) => { @@ -2209,6 +2221,15 @@ impl<'a> Checker<'a> { self.semantic.flags = snapshot; } + /// Visit an [`Expr`], and treat it as the `typ` argument to `typing.cast`. + fn visit_cast_type_argument(&mut self, arg: &'a Expr) { + self.visit_type_definition(arg); + + if !self.source_type.is_stub() && self.enabled(Rule::RuntimeCastValue) { + flake8_type_checking::rules::runtime_cast_value(self, arg); + } + } + /// Visit an [`Expr`], and treat it as a boolean test. This is useful for detecting whether an /// expressions return value is significant, or whether the calling context only relies on /// its truthiness. diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.py.snap index 25348b34ab..f117c364f7 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.py.snap @@ -212,3 +212,37 @@ TC006.py:89:9: TC006 [*] Add quotes to type expression in `typing.cast()` 89 |+ "int" # TC006 90 90 | , 6.0 91 91 | ) +92 92 | + +TC006.py:98:14: TC006 [*] Add quotes to type expression in `typing.cast()` + | +96 | from typing import cast +97 | +98 | cast(typ=int, val=3.0) # TC006 + | ^^^ TC006 +99 | cast(val=3.0, typ=int) # TC006 + | + = help: Add quotes + +ℹ Safe fix +95 95 | # Keyword arguments +96 96 | from typing import cast +97 97 | +98 |- cast(typ=int, val=3.0) # TC006 + 98 |+ cast(typ="int", val=3.0) # TC006 +99 99 | cast(val=3.0, typ=int) # TC006 + +TC006.py:99:23: TC006 [*] Add quotes to type expression in `typing.cast()` + | +98 | cast(typ=int, val=3.0) # TC006 +99 | cast(val=3.0, typ=int) # TC006 + | ^^^ TC006 + | + = help: Add quotes + +ℹ Safe fix +96 96 | from typing import cast +97 97 | +98 98 | cast(typ=int, val=3.0) # TC006 +99 |- cast(val=3.0, typ=int) # TC006 + 99 |+ cast(val=3.0, typ="int") # TC006