From 77c2f4c6cb1ef92b8f9659026e5544e7e52ced63 Mon Sep 17 00:00:00 2001 From: ValdonVitijaa <76228057+ValdonVitija@users.noreply.github.com> Date: Wed, 31 Dec 2025 17:07:56 +0100 Subject: [PATCH] [`flake8-unused-arguments`] Mark `**kwargs` in `TypeVar` as used (`ARG001`) (#22214) ## Summary Fixes false positive in ARG001 when `**kwargs` is passed to `typing.TypeVar` Closes #22178 When `**kwargs` is used in a `typing.TypeVar` call, the checker was not recognizing it as a usage, leading to false positive "unused function argument" warnings. ### Root Cause In the AST, keyword arguments are represented by the `Keyword` struct with an `arg` field of type `Option`: - Named keywords like `bound=int` have `arg = Some("bound")` - Dictionary unpacking like `**kwargs` has `arg = None` The existing code only handled the `Some(id)` case, never visiting the expression when `arg` was `None`, so `**kwargs` was never marked as used. ### Changes Added an `else` branch to handle `**kwargs` unpacking by calling `visit_non_type_definition(value)` when `arg` is `None`. This ensures the `kwargs` variable is properly visited and marked as used by the semantic model. ## Test Plan Tested with the following code: ```python import typing def f( *args: object, default: object = None, **kwargs: object, ) -> None: typing.TypeVar(*args, **kwargs) ``` Before : `ARG001 Unused function argument: kwargs ` After : `All checks passed!` Run the example with the following command(from the root of ruff and please change the path to the module that contains the code example): `cargo run -p ruff -- check /path/to/file.py --isolated --select=ARG --no-cache` --- .../test/fixtures/flake8_unused_arguments/ARG.py | 14 +++++++++++++- crates/ruff_linter/src/checkers/ast/mod.rs | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) 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 311409e6ff..e1725a4351 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 @@ class C: """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 be8bc4b275..d4aa25d126 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1878,6 +1878,9 @@ impl<'a> Visitor<'a> for Checker<'a> { } else { self.visit_non_type_definition(value); } + } else { + // Ex: typing.TypeVar(**kwargs) + self.visit_non_type_definition(value); } } }