Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flake8-pyi] Expand PYI018 to cover ParamSpecs and TypeVarTuples #9198

Merged
merged 4 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import typing
import typing_extensions
from typing import TypeVar
from typing_extensions import ParamSpec, TypeVarTuple

_T = typing.TypeVar("_T")
_P = TypeVar("_P")
_Ts = typing_extensions.TypeVarTuple("_Ts")
_P = ParamSpec("_P")
_P2 = typing.ParamSpec("_P2")
_Ts2 = TypeVarTuple("_Ts2")

# OK
_UsedTypeVar = TypeVar("_UsedTypeVar")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import typing
import typing_extensions
from typing import TypeVar
from typing_extensions import ParamSpec, TypeVarTuple

_T = typing.TypeVar("_T")
_P = TypeVar("_P")
_Ts = typing_extensions.TypeVarTuple("_Ts")
_P = ParamSpec("_P")
_P2 = typing.ParamSpec("_P2")
_Ts2 = TypeVarTuple("_Ts2")

# OK
_UsedTypeVar = TypeVar("_UsedTypeVar")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,35 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;

/// ## What it does
/// Checks for the presence of unused private `TypeVar` declarations.
/// Checks for the presence of unused private `TypeVar`, `ParamSpec` or
/// `TypeVarTuple` declarations.
///
/// ## Why is this bad?
/// A private `TypeVar` that is defined but not used is likely a mistake, and
/// A private `TypeVar` that is defined but not used is likely a mistake. It
/// should either be used, made public, or removed to avoid confusion.
///
/// ## Example
/// ```python
/// import typing
/// import typing_extensions
///
/// _T = typing.TypeVar("_T")
/// _Ts = typing_extensions.TypeVarTuple("_Ts")
/// ```
#[violation]
pub struct UnusedPrivateTypeVar {
name: String,
typevarlike_name: String,
typevarlike_kind: String,
}

impl Violation for UnusedPrivateTypeVar {
#[derive_message_formats]
fn message(&self) -> String {
let UnusedPrivateTypeVar { name } = self;
format!("Private TypeVar `{name}` is never used")
let UnusedPrivateTypeVar {
typevarlike_name,
typevarlike_kind,
} = self;
format!("Private {typevarlike_kind} `{typevarlike_name}` is never used")
}
}

Expand Down Expand Up @@ -185,13 +192,22 @@ pub(crate) fn unused_private_type_var(
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
continue;
};
if !checker.semantic().match_typing_expr(func, "TypeVar") {
continue;
}

let semantic = checker.semantic();

let typevarlike_kind = match func {
f if semantic.match_typing_expr(f, "TypeVar") => "TypeVar",
f if semantic.match_typing_expr(f, "ParamSpec") => "ParamSpec",
f if semantic.match_typing_expr(f, "TypeVarTuple") => "TypeVarTuple",
_ => {
continue;
}
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might write this like:

let typevarlike_kind = if semantic.match_typing_expr(f, "TypeVar")  {
   "TypeVar"
} else if ...

But I don't know that what I'm proposing is actually better, I just hadn't seen this pattern before :)


diagnostics.push(Diagnostic::new(
UnusedPrivateTypeVar {
name: id.to_string(),
typevarlike_name: id.to_string(),
typevarlike_kind: typevarlike_kind.to_string(),
},
binding.range(),
));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI018.py:4:1: PYI018 Private TypeVar `_T` is never used
PYI018.py:6:1: PYI018 Private TypeVar `_T` is never used
|
2 | from typing import TypeVar
3 |
4 | _T = typing.TypeVar("_T")
4 | from typing_extensions import ParamSpec, TypeVarTuple
5 |
6 | _T = typing.TypeVar("_T")
| ^^ PYI018
5 | _P = TypeVar("_P")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
|

PYI018.py:5:1: PYI018 Private TypeVar `_P` is never used
PYI018.py:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
4 | _T = typing.TypeVar("_T")
5 | _P = TypeVar("_P")
| ^^ PYI018
6 |
7 | # OK
6 | _T = typing.TypeVar("_T")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
| ^^^ PYI018
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
|

PYI018.py:8:1: PYI018 Private ParamSpec `_P` is never used
|
6 | _T = typing.TypeVar("_T")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
| ^^ PYI018
9 | _P2 = typing.ParamSpec("_P2")
10 | _Ts2 = TypeVarTuple("_Ts2")
|

PYI018.py:9:1: PYI018 Private ParamSpec `_P2` is never used
|
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
| ^^^ PYI018
10 | _Ts2 = TypeVarTuple("_Ts2")
|

PYI018.py:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
10 | _Ts2 = TypeVarTuple("_Ts2")
| ^^^^ PYI018
11 |
12 | # OK
|


Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI018.pyi:4:1: PYI018 Private TypeVar `_T` is never used
PYI018.pyi:6:1: PYI018 Private TypeVar `_T` is never used
|
2 | from typing import TypeVar
3 |
4 | _T = typing.TypeVar("_T")
4 | from typing_extensions import ParamSpec, TypeVarTuple
5 |
6 | _T = typing.TypeVar("_T")
| ^^ PYI018
5 | _P = TypeVar("_P")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
|

PYI018.pyi:5:1: PYI018 Private TypeVar `_P` is never used
PYI018.pyi:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
4 | _T = typing.TypeVar("_T")
5 | _P = TypeVar("_P")
| ^^ PYI018
6 |
7 | # OK
6 | _T = typing.TypeVar("_T")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
| ^^^ PYI018
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
|

PYI018.pyi:8:1: PYI018 Private ParamSpec `_P` is never used
|
6 | _T = typing.TypeVar("_T")
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
| ^^ PYI018
9 | _P2 = typing.ParamSpec("_P2")
10 | _Ts2 = TypeVarTuple("_Ts2")
|

PYI018.pyi:9:1: PYI018 Private ParamSpec `_P2` is never used
|
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
| ^^^ PYI018
10 | _Ts2 = TypeVarTuple("_Ts2")
|

PYI018.pyi:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
8 | _P = ParamSpec("_P")
9 | _P2 = typing.ParamSpec("_P2")
10 | _Ts2 = TypeVarTuple("_Ts2")
| ^^^^ PYI018
11 |
12 | # OK
|


Loading