From d5cd85210733b2f6796e7ed57886cd3473214386 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Fri, 5 Jan 2024 18:33:03 +0000 Subject: [PATCH] [flake8-pyi] Fix false negative for PYI046 with unused generic protocols --- .../test/fixtures/flake8_pyi/PYI046.py | 18 ++++++++++++++++-- .../test/fixtures/flake8_pyi/PYI046.pyi | 18 ++++++++++++++++-- .../rules/unused_private_type_definition.rs | 11 ++++++----- ...s__flake8_pyi__tests__PYI046_PYI046.py.snap | 7 +++++++ ...__flake8_pyi__tests__PYI046_PYI046.pyi.snap | 7 +++++++ 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.py index 42fb031ebdb0d..6a96f532c5d91 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.py @@ -1,5 +1,5 @@ import typing -from typing import Protocol +from typing import Protocol, TypeVar class _Foo(Protocol): @@ -10,9 +10,23 @@ class _Bar(typing.Protocol): bar: int +_T = TypeVar("_T") + + +class _Baz(Protocol[_T]): + x: _T + + # OK class _UsedPrivateProtocol(Protocol): bar: int -def uses__UsedPrivateProtocol(arg: _UsedPrivateProtocol) -> None: ... +# Also OK +class _UsedGenericPrivateProtocol(Protocol[_T]): + x: _T + + +def uses_some_private_protocols( + arg: _UsedPrivateProtocol, arg2: _UsedGenericPrivateProtocol[int] +) -> None: ... diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.pyi index fb9292ed857fa..6ce6420951290 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI046.pyi @@ -1,5 +1,5 @@ import typing -from typing import Protocol +from typing import Protocol, TypeVar class _Foo(object, Protocol): @@ -10,9 +10,23 @@ class _Bar(typing.Protocol): bar: int +_T = TypeVar("_T") + + +class _Baz(Protocol[_T]): + x: _T + + # OK class _UsedPrivateProtocol(Protocol): bar: int -def uses__UsedPrivateProtocol(arg: _UsedPrivateProtocol) -> None: ... +# Also OK +class _UsedGenericPrivateProtocol(Protocol[_T]): + x: _T + + +def uses_some_private_protocols( + arg: _UsedPrivateProtocol, arg2: _UsedGenericPrivateProtocol[int] +) -> None: ... diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs index 5da722a904394..545b5f7f656d3 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs @@ -1,5 +1,6 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::helpers::map_subscript; use ruff_python_ast::{self as ast, Expr, Stmt}; use ruff_python_semantic::Scope; use ruff_text_size::Ranged; @@ -243,11 +244,11 @@ pub(crate) fn unused_private_protocol( continue; }; - if !class_def - .bases() - .iter() - .any(|base| checker.semantic().match_typing_expr(base, "Protocol")) - { + if !class_def.bases().iter().any(|base| { + checker + .semantic() + .match_typing_expr(map_subscript(base), "Protocol") + }) { continue; } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.py.snap index 89c8e24038423..3787d04a3fe52 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.py.snap @@ -15,4 +15,11 @@ PYI046.py:9:7: PYI046 Private protocol `_Bar` is never used 10 | bar: int | +PYI046.py:16:7: PYI046 Private protocol `_Baz` is never used + | +16 | class _Baz(Protocol[_T]): + | ^^^^ PYI046 +17 | x: _T + | + diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.pyi.snap index 83f52186ba935..ce2f2d7bc5897 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI046_PYI046.pyi.snap @@ -15,4 +15,11 @@ PYI046.pyi:9:7: PYI046 Private protocol `_Bar` is never used 10 | bar: int | +PYI046.pyi:16:7: PYI046 Private protocol `_Baz` is never used + | +16 | class _Baz(Protocol[_T]): + | ^^^^ PYI046 +17 | x: _T + | +