[flake8-pyi] Fix false negative for PYI046 with unused generic protocols (#9405)

I just fixed this false negative in flake8-pyi
(https://github.com/PyCQA/flake8-pyi/pull/460), and then realised ruff
has the exact same bug! Luckily it's a very easy fix.

(The bug is that unused protocols go undetected if they're generic.)
This commit is contained in:
Alex Waygood 2024-01-05 18:56:04 +00:00 committed by GitHub
parent 62eca330a8
commit cde4a7d7bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 9 deletions

View file

@ -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: ...

View file

@ -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: ...

View file

@ -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;
}

View file

@ -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
|

View file

@ -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
|