From edbaaec8b7d756c68d5f1c4f6d34d7ba04574386 Mon Sep 17 00:00:00 2001 From: ValdonVitijaa Date: Fri, 26 Dec 2025 22:42:37 +0100 Subject: [PATCH 1/3] Fix for - TypeVar special handling doesn't recognize ** for ARG001 --- crates/ruff_linter/src/checkers/ast/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 37422cbf186d7..a9e21e699d1ce 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1867,12 +1867,17 @@ impl<'a> Visitor<'a> for Checker<'a> { range: _, node_index: _, } = keyword; + dbg!(keyword); if let Some(id) = arg { if matches!(&**id, "bound" | "default") { self.visit_type_definition(value); } else { self.visit_non_type_definition(value); } + } else { + // The only case when a keyword argument is None is when using **kwargs, since there is no explicit 'arg' name for it. + // Ex: typing.TypeVar(*args, **kwargs) + self.visit_non_type_definition(value); } } } From 77bdad744d8992333573c9ce369eb5cacb939971 Mon Sep 17 00:00:00 2001 From: ValdonVitijaa Date: Fri, 26 Dec 2025 22:50:42 +0100 Subject: [PATCH 2/3] Fix for - TypeVar special handling doesn't recognize ** for ARG001 --- crates/ruff_linter/src/checkers/ast/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index a9e21e699d1ce..a666782bf073a 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1867,7 +1867,6 @@ impl<'a> Visitor<'a> for Checker<'a> { range: _, node_index: _, } = keyword; - dbg!(keyword); if let Some(id) = arg { if matches!(&**id, "bound" | "default") { self.visit_type_definition(value); From 6fa2f53b1db37839ecef0884f98f0a370f64136d Mon Sep 17 00:00:00 2001 From: ValdonVitijaa Date: Mon, 29 Dec 2025 21:25:20 +0100 Subject: [PATCH 3/3] Adding python test case to cover for unused **kwargs in typing.TypeVar(**kwargs) --- .../test/fixtures/flake8_unused_arguments/ARG.py | 14 +++++++++++++- crates/ruff_linter/src/checkers/ast/mod.rs | 3 +-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py b/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py index 311409e6ffb73..e1725a43511fd 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py @@ -1,5 +1,5 @@ from abc import abstractmethod -from typing import overload, cast +from typing import overload, cast, TypeVar from typing_extensions import override @@ -256,3 +256,15 @@ def f(self, x, y): """Docstring.""" msg = t"{x}..." raise NotImplementedError(msg) + + +### +# Unused arguments with `**kwargs`. +### + +def f( + default: object = None, # noqa: ARG001 + **kwargs: object, +) -> None: + TypeVar(**kwargs) + diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index a666782bf073a..dd755e236bb43 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1874,8 +1874,7 @@ impl<'a> Visitor<'a> for Checker<'a> { self.visit_non_type_definition(value); } } else { - // The only case when a keyword argument is None is when using **kwargs, since there is no explicit 'arg' name for it. - // Ex: typing.TypeVar(*args, **kwargs) + // Ex: typing.TypeVar(**kwargs) self.visit_non_type_definition(value); } }