mirror of https://github.com/astral-sh/ruff
[flake8-pyi] Add autofix for unused-private-type-var (PYI018) (#15999)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
18b497a913
commit
ba2f0e998d
|
|
@ -2218,13 +2218,11 @@ def func(t: _T) -> _T:
|
|||
return x
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
@r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
from typing import TypeVar
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class OldStyle[T]:
|
||||
var: T
|
||||
|
|
@ -2234,8 +2232,7 @@ def func(t: _T) -> _T:
|
|||
return x
|
||||
|
||||
----- stderr -----
|
||||
test.py:3:1: PYI018 Private TypeVar `_T` is never used
|
||||
Found 6 errors (5 fixed, 1 remaining).
|
||||
"#
|
||||
Found 7 errors (7 fixed, 0 remaining).
|
||||
"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,8 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
|
||||
#[test_case(Rule::UnusedPrivateTypeVar, Path::new("PYI018.py"))]
|
||||
#[test_case(Rule::UnusedPrivateTypeVar, Path::new("PYI018.pyi"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::helpers::map_subscript;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
|
|
@ -6,6 +6,7 @@ use ruff_python_semantic::{Scope, SemanticModel};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of unused private `TypeVar`, `ParamSpec` or
|
||||
|
|
@ -13,7 +14,8 @@ use crate::checkers::ast::Checker;
|
|||
///
|
||||
/// ## Why is this bad?
|
||||
/// 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.
|
||||
/// should either be used, made public, or removed to avoid confusion. A type
|
||||
/// variable is considered "private" if its name starts with an underscore.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```pyi
|
||||
|
|
@ -23,6 +25,11 @@ use crate::checkers::ast::Checker;
|
|||
/// _T = typing.TypeVar("_T")
|
||||
/// _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety and availability
|
||||
/// This rule's fix is available when [`preview`] mode is enabled.
|
||||
/// It is always marked as unsafe, as it would break your code if the type
|
||||
/// variable is imported by another module.
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct UnusedPrivateTypeVar {
|
||||
type_var_like_name: String,
|
||||
|
|
@ -30,6 +37,8 @@ pub(crate) struct UnusedPrivateTypeVar {
|
|||
}
|
||||
|
||||
impl Violation for UnusedPrivateTypeVar {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedPrivateTypeVar {
|
||||
|
|
@ -38,6 +47,16 @@ impl Violation for UnusedPrivateTypeVar {
|
|||
} = self;
|
||||
format!("Private {type_var_like_kind} `{type_var_like_name}` is never used")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let UnusedPrivateTypeVar {
|
||||
type_var_like_name,
|
||||
type_var_like_kind,
|
||||
} = self;
|
||||
Some(format!(
|
||||
"Remove unused private {type_var_like_kind} `{type_var_like_name}`"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
|
|
@ -178,7 +197,7 @@ pub(crate) fn unused_private_type_var(
|
|||
let Some(source) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) =
|
||||
let stmt @ Stmt::Assign(ast::StmtAssign { targets, value, .. }) =
|
||||
checker.semantic().statement(source)
|
||||
else {
|
||||
continue;
|
||||
|
|
@ -210,13 +229,20 @@ pub(crate) fn unused_private_type_var(
|
|||
continue;
|
||||
};
|
||||
|
||||
diagnostics.push(Diagnostic::new(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnusedPrivateTypeVar {
|
||||
type_var_like_name: id.to_string(),
|
||||
type_var_like_kind: type_var_like_kind.to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
));
|
||||
);
|
||||
|
||||
if checker.settings.preview.is_enabled() {
|
||||
let edit = fix::edits::delete_stmt(stmt, None, checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ PYI018.py:6:1: PYI018 Private TypeVar `_T` is never used
|
|||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 | _P = ParamSpec("_P")
|
||||
|
|
||||
= help: Remove unused private TypeVar `_T`
|
||||
|
||||
PYI018.py:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
||||
|
|
||||
|
|
@ -19,6 +20,7 @@ PYI018.py:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
|||
8 | _P = ParamSpec("_P")
|
||||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts`
|
||||
|
||||
PYI018.py:8:1: PYI018 Private ParamSpec `_P` is never used
|
||||
|
|
||||
|
|
@ -29,6 +31,7 @@ PYI018.py:8:1: PYI018 Private ParamSpec `_P` is never used
|
|||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
10 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P`
|
||||
|
||||
PYI018.py:9:1: PYI018 Private ParamSpec `_P2` is never used
|
||||
|
|
||||
|
|
@ -38,6 +41,7 @@ PYI018.py:9:1: PYI018 Private ParamSpec `_P2` is never used
|
|||
| ^^^ PYI018
|
||||
10 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P2`
|
||||
|
||||
PYI018.py:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
||||
|
|
||||
|
|
@ -48,3 +52,4 @@ PYI018.py:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
|||
11 |
|
||||
12 | # OK
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts2`
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ PYI018.pyi:6:1: PYI018 Private TypeVar `_T` is never used
|
|||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 | _P = ParamSpec("_P")
|
||||
|
|
||||
= help: Remove unused private TypeVar `_T`
|
||||
|
||||
PYI018.pyi:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
||||
|
|
||||
|
|
@ -19,6 +20,7 @@ PYI018.pyi:7:1: PYI018 Private TypeVarTuple `_Ts` is never used
|
|||
8 | _P = ParamSpec("_P")
|
||||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts`
|
||||
|
||||
PYI018.pyi:8:1: PYI018 Private ParamSpec `_P` is never used
|
||||
|
|
||||
|
|
@ -29,6 +31,7 @@ PYI018.pyi:8:1: PYI018 Private ParamSpec `_P` is never used
|
|||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
10 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P`
|
||||
|
||||
PYI018.pyi:9:1: PYI018 Private ParamSpec `_P2` is never used
|
||||
|
|
||||
|
|
@ -38,6 +41,7 @@ PYI018.pyi:9:1: PYI018 Private ParamSpec `_P2` is never used
|
|||
| ^^^ PYI018
|
||||
10 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P2`
|
||||
|
||||
PYI018.pyi:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
||||
|
|
||||
|
|
@ -48,3 +52,4 @@ PYI018.pyi:10:1: PYI018 Private TypeVarTuple `_Ts2` is never used
|
|||
11 |
|
||||
12 | # OK
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts2`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI018.py:6:1: PYI018 [*] Private TypeVar `_T` is never used
|
||||
|
|
||||
4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 |
|
||||
6 | _T = typing.TypeVar("_T")
|
||||
| ^^ PYI018
|
||||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 | _P = ParamSpec("_P")
|
||||
|
|
||||
= help: Remove unused private TypeVar `_T`
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | from typing import TypeVar
|
||||
4 4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 5 |
|
||||
6 |-_T = typing.TypeVar("_T")
|
||||
7 6 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 7 | _P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
|
||||
PYI018.py:7:1: PYI018 [*] Private TypeVarTuple `_Ts` is never used
|
||||
|
|
||||
6 | _T = typing.TypeVar("_T")
|
||||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
| ^^^ PYI018
|
||||
8 | _P = ParamSpec("_P")
|
||||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts`
|
||||
|
||||
ℹ Unsafe fix
|
||||
4 4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 5 |
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 |-_Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 7 | _P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
||||
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")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P`
|
||||
|
||||
ℹ Unsafe fix
|
||||
5 5 |
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 |-_P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
|
||||
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")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 8 | _P = ParamSpec("_P")
|
||||
9 |-_P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
12 11 | # OK
|
||||
|
||||
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
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 8 | _P = ParamSpec("_P")
|
||||
9 9 | _P2 = typing.ParamSpec("_P2")
|
||||
10 |-_Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
12 11 | # OK
|
||||
13 12 | _UsedTypeVar = TypeVar("_UsedTypeVar")
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI018.pyi:6:1: PYI018 [*] Private TypeVar `_T` is never used
|
||||
|
|
||||
4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 |
|
||||
6 | _T = typing.TypeVar("_T")
|
||||
| ^^ PYI018
|
||||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 | _P = ParamSpec("_P")
|
||||
|
|
||||
= help: Remove unused private TypeVar `_T`
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | from typing import TypeVar
|
||||
4 4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 5 |
|
||||
6 |-_T = typing.TypeVar("_T")
|
||||
7 6 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 7 | _P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
|
||||
PYI018.pyi:7:1: PYI018 [*] Private TypeVarTuple `_Ts` is never used
|
||||
|
|
||||
6 | _T = typing.TypeVar("_T")
|
||||
7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
| ^^^ PYI018
|
||||
8 | _P = ParamSpec("_P")
|
||||
9 | _P2 = typing.ParamSpec("_P2")
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts`
|
||||
|
||||
ℹ Unsafe fix
|
||||
4 4 | from typing_extensions import ParamSpec, TypeVarTuple
|
||||
5 5 |
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 |-_Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 7 | _P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
|
||||
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")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P`
|
||||
|
||||
ℹ Unsafe fix
|
||||
5 5 |
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 |-_P = ParamSpec("_P")
|
||||
9 8 | _P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
|
||||
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")
|
||||
|
|
||||
= help: Remove unused private ParamSpec `_P2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
6 6 | _T = typing.TypeVar("_T")
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 8 | _P = ParamSpec("_P")
|
||||
9 |-_P2 = typing.ParamSpec("_P2")
|
||||
10 9 | _Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
12 11 | # OK
|
||||
|
||||
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
|
||||
|
|
||||
= help: Remove unused private TypeVarTuple `_Ts2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 | _Ts = typing_extensions.TypeVarTuple("_Ts")
|
||||
8 8 | _P = ParamSpec("_P")
|
||||
9 9 | _P2 = typing.ParamSpec("_P2")
|
||||
10 |-_Ts2 = TypeVarTuple("_Ts2")
|
||||
11 10 |
|
||||
12 11 | # OK
|
||||
13 12 | _UsedTypeVar = TypeVar("_UsedTypeVar")
|
||||
Loading…
Reference in New Issue