Improve type-name-incorrect-variance message (#5658)

## Summary

Change the `type-name-incorrect-variance` diagnostic message to include
the detected variance and a name change recommendation. For example,

```
`TypeVar` name "T_co" does not reflect its contravariance; consider renaming it to "T_contra"
```

Related to #5651.

## Test Plan

`cargo test`
This commit is contained in:
Tom Kuson
2023-07-10 18:33:37 +01:00
committed by GitHub
parent d19839fe0f
commit 5ab9538573
2 changed files with 92 additions and 44 deletions

View File

@@ -46,27 +46,34 @@ use crate::rules::pylint::helpers::type_param_name;
pub struct TypeNameIncorrectVariance {
kind: VarKind,
param_name: String,
variance: VarVariance,
replacement_name: String,
}
impl Violation for TypeNameIncorrectVariance {
#[derive_message_formats]
fn message(&self) -> String {
let TypeNameIncorrectVariance { kind, param_name } = self;
format!("`{kind}` name \"{param_name}\" does not match variance")
let TypeNameIncorrectVariance {
kind,
param_name,
variance,
replacement_name,
} = self;
format!("`{kind}` name \"{param_name}\" does not reflect its {variance}; consider renaming it to \"{replacement_name}\"")
}
}
/// PLC0105
pub(crate) fn type_name_incorrect_variance(checker: &mut Checker, value: &Expr) {
let Expr::Call(ast::ExprCall {
func,
args,
keywords,
..
}) = value
else {
return;
};
func,
args,
keywords,
..
}) = value
else {
return;
};
let Some(param_name) = type_param_name(args, keywords) else {
return;
@@ -114,14 +121,26 @@ pub(crate) fn type_name_incorrect_variance(checker: &mut Checker, value: &Expr)
None
}
})
else {
return;
else {
return;
};
let variance = variance(covariant, contravariant);
let name_root = param_name
.trim_end_matches("_co")
.trim_end_matches("_contra");
let replacement_name: String = match variance {
VarVariance::Covariance => format!("{name_root}_co"),
VarVariance::Contravariance => format!("{name_root}_contra"),
VarVariance::Invariance => name_root.to_string(),
};
checker.diagnostics.push(Diagnostic::new(
TypeNameIncorrectVariance {
kind,
param_name: param_name.to_string(),
variance,
replacement_name,
},
func.range(),
));
@@ -138,6 +157,18 @@ fn mismatch(param_name: &str, covariant: Option<&Expr>, contravariant: Option<&E
}
}
/// Return the variance of the type parameter.
fn variance(covariant: Option<&Expr>, contravariant: Option<&Expr>) -> VarVariance {
match (
covariant.map(is_const_true),
contravariant.map(is_const_true),
) {
(Some(true), _) => VarVariance::Covariance,
(_, Some(true)) => VarVariance::Contravariance,
_ => VarVariance::Invariance,
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum VarKind {
TypeVar,
@@ -152,3 +183,20 @@ impl fmt::Display for VarKind {
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum VarVariance {
Covariance,
Contravariance,
Invariance,
}
impl fmt::Display for VarVariance {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
VarVariance::Covariance => fmt.write_str("covariance"),
VarVariance::Contravariance => fmt.write_str("contravariance"),
VarVariance::Invariance => fmt.write_str("invariance"),
}
}
}

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff/src/rules/pylint/mod.rs
---
type_name_incorrect_variance.py:5:5: PLC0105 `TypeVar` name "T" does not match variance
type_name_incorrect_variance.py:5:5: PLC0105 `TypeVar` name "T" does not reflect its covariance; consider renaming it to "T_co"
|
3 | # Errors.
4 |
@@ -11,7 +11,7 @@ type_name_incorrect_variance.py:5:5: PLC0105 `TypeVar` name "T" does not match v
7 | T = TypeVar("T", contravariant=True)
|
type_name_incorrect_variance.py:6:5: PLC0105 `TypeVar` name "T" does not match variance
type_name_incorrect_variance.py:6:5: PLC0105 `TypeVar` name "T" does not reflect its covariance; consider renaming it to "T_co"
|
5 | T = TypeVar("T", covariant=True)
6 | T = TypeVar("T", covariant=True, contravariant=False)
@@ -20,7 +20,7 @@ type_name_incorrect_variance.py:6:5: PLC0105 `TypeVar` name "T" does not match v
8 | T = TypeVar("T", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:7:5: PLC0105 `TypeVar` name "T" does not match variance
type_name_incorrect_variance.py:7:5: PLC0105 `TypeVar` name "T" does not reflect its contravariance; consider renaming it to "T_contra"
|
5 | T = TypeVar("T", covariant=True)
6 | T = TypeVar("T", covariant=True, contravariant=False)
@@ -30,7 +30,7 @@ type_name_incorrect_variance.py:7:5: PLC0105 `TypeVar` name "T" does not match v
9 | P = ParamSpec("P", covariant=True)
|
type_name_incorrect_variance.py:8:5: PLC0105 `TypeVar` name "T" does not match variance
type_name_incorrect_variance.py:8:5: PLC0105 `TypeVar` name "T" does not reflect its contravariance; consider renaming it to "T_contra"
|
6 | T = TypeVar("T", covariant=True, contravariant=False)
7 | T = TypeVar("T", contravariant=True)
@@ -40,7 +40,7 @@ type_name_incorrect_variance.py:8:5: PLC0105 `TypeVar` name "T" does not match v
10 | P = ParamSpec("P", covariant=True, contravariant=False)
|
type_name_incorrect_variance.py:9:5: PLC0105 `ParamSpec` name "P" does not match variance
type_name_incorrect_variance.py:9:5: PLC0105 `ParamSpec` name "P" does not reflect its covariance; consider renaming it to "P_co"
|
7 | T = TypeVar("T", contravariant=True)
8 | T = TypeVar("T", covariant=False, contravariant=True)
@@ -50,7 +50,7 @@ type_name_incorrect_variance.py:9:5: PLC0105 `ParamSpec` name "P" does not match
11 | P = ParamSpec("P", contravariant=True)
|
type_name_incorrect_variance.py:10:5: PLC0105 `ParamSpec` name "P" does not match variance
type_name_incorrect_variance.py:10:5: PLC0105 `ParamSpec` name "P" does not reflect its covariance; consider renaming it to "P_co"
|
8 | T = TypeVar("T", covariant=False, contravariant=True)
9 | P = ParamSpec("P", covariant=True)
@@ -60,7 +60,7 @@ type_name_incorrect_variance.py:10:5: PLC0105 `ParamSpec` name "P" does not matc
12 | P = ParamSpec("P", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:11:5: PLC0105 `ParamSpec` name "P" does not match variance
type_name_incorrect_variance.py:11:5: PLC0105 `ParamSpec` name "P" does not reflect its contravariance; consider renaming it to "P_contra"
|
9 | P = ParamSpec("P", covariant=True)
10 | P = ParamSpec("P", covariant=True, contravariant=False)
@@ -69,7 +69,7 @@ type_name_incorrect_variance.py:11:5: PLC0105 `ParamSpec` name "P" does not matc
12 | P = ParamSpec("P", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:12:5: PLC0105 `ParamSpec` name "P" does not match variance
type_name_incorrect_variance.py:12:5: PLC0105 `ParamSpec` name "P" does not reflect its contravariance; consider renaming it to "P_contra"
|
10 | P = ParamSpec("P", covariant=True, contravariant=False)
11 | P = ParamSpec("P", contravariant=True)
@@ -79,7 +79,7 @@ type_name_incorrect_variance.py:12:5: PLC0105 `ParamSpec` name "P" does not matc
14 | T_co = TypeVar("T_co")
|
type_name_incorrect_variance.py:14:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:14:8: PLC0105 `TypeVar` name "T_co" does not reflect its invariance; consider renaming it to "T"
|
12 | P = ParamSpec("P", covariant=False, contravariant=True)
13 |
@@ -89,7 +89,7 @@ type_name_incorrect_variance.py:14:8: PLC0105 `TypeVar` name "T_co" does not mat
16 | T_co = TypeVar("T_co", contravariant=False)
|
type_name_incorrect_variance.py:15:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:15:8: PLC0105 `TypeVar` name "T_co" does not reflect its invariance; consider renaming it to "T"
|
14 | T_co = TypeVar("T_co")
15 | T_co = TypeVar("T_co", covariant=False)
@@ -98,7 +98,7 @@ type_name_incorrect_variance.py:15:8: PLC0105 `TypeVar` name "T_co" does not mat
17 | T_co = TypeVar("T_co", covariant=False, contravariant=False)
|
type_name_incorrect_variance.py:16:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:16:8: PLC0105 `TypeVar` name "T_co" does not reflect its invariance; consider renaming it to "T"
|
14 | T_co = TypeVar("T_co")
15 | T_co = TypeVar("T_co", covariant=False)
@@ -108,7 +108,7 @@ type_name_incorrect_variance.py:16:8: PLC0105 `TypeVar` name "T_co" does not mat
18 | T_co = TypeVar("T_co", contravariant=True)
|
type_name_incorrect_variance.py:17:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:17:8: PLC0105 `TypeVar` name "T_co" does not reflect its invariance; consider renaming it to "T"
|
15 | T_co = TypeVar("T_co", covariant=False)
16 | T_co = TypeVar("T_co", contravariant=False)
@@ -118,7 +118,7 @@ type_name_incorrect_variance.py:17:8: PLC0105 `TypeVar` name "T_co" does not mat
19 | T_co = TypeVar("T_co", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:18:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:18:8: PLC0105 `TypeVar` name "T_co" does not reflect its contravariance; consider renaming it to "T_contra"
|
16 | T_co = TypeVar("T_co", contravariant=False)
17 | T_co = TypeVar("T_co", covariant=False, contravariant=False)
@@ -128,7 +128,7 @@ type_name_incorrect_variance.py:18:8: PLC0105 `TypeVar` name "T_co" does not mat
20 | P_co = ParamSpec("P_co")
|
type_name_incorrect_variance.py:19:8: PLC0105 `TypeVar` name "T_co" does not match variance
type_name_incorrect_variance.py:19:8: PLC0105 `TypeVar` name "T_co" does not reflect its contravariance; consider renaming it to "T_contra"
|
17 | T_co = TypeVar("T_co", covariant=False, contravariant=False)
18 | T_co = TypeVar("T_co", contravariant=True)
@@ -138,7 +138,7 @@ type_name_incorrect_variance.py:19:8: PLC0105 `TypeVar` name "T_co" does not mat
21 | P_co = ParamSpec("P_co", covariant=False)
|
type_name_incorrect_variance.py:20:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:20:8: PLC0105 `ParamSpec` name "P_co" does not reflect its invariance; consider renaming it to "P"
|
18 | T_co = TypeVar("T_co", contravariant=True)
19 | T_co = TypeVar("T_co", covariant=False, contravariant=True)
@@ -148,7 +148,7 @@ type_name_incorrect_variance.py:20:8: PLC0105 `ParamSpec` name "P_co" does not m
22 | P_co = ParamSpec("P_co", contravariant=False)
|
type_name_incorrect_variance.py:21:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:21:8: PLC0105 `ParamSpec` name "P_co" does not reflect its invariance; consider renaming it to "P"
|
19 | T_co = TypeVar("T_co", covariant=False, contravariant=True)
20 | P_co = ParamSpec("P_co")
@@ -158,7 +158,7 @@ type_name_incorrect_variance.py:21:8: PLC0105 `ParamSpec` name "P_co" does not m
23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False)
|
type_name_incorrect_variance.py:22:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:22:8: PLC0105 `ParamSpec` name "P_co" does not reflect its invariance; consider renaming it to "P"
|
20 | P_co = ParamSpec("P_co")
21 | P_co = ParamSpec("P_co", covariant=False)
@@ -168,7 +168,7 @@ type_name_incorrect_variance.py:22:8: PLC0105 `ParamSpec` name "P_co" does not m
24 | P_co = ParamSpec("P_co", contravariant=True)
|
type_name_incorrect_variance.py:23:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:23:8: PLC0105 `ParamSpec` name "P_co" does not reflect its invariance; consider renaming it to "P"
|
21 | P_co = ParamSpec("P_co", covariant=False)
22 | P_co = ParamSpec("P_co", contravariant=False)
@@ -178,7 +178,7 @@ type_name_incorrect_variance.py:23:8: PLC0105 `ParamSpec` name "P_co" does not m
25 | P_co = ParamSpec("P_co", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:24:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:24:8: PLC0105 `ParamSpec` name "P_co" does not reflect its contravariance; consider renaming it to "P_contra"
|
22 | P_co = ParamSpec("P_co", contravariant=False)
23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False)
@@ -187,7 +187,7 @@ type_name_incorrect_variance.py:24:8: PLC0105 `ParamSpec` name "P_co" does not m
25 | P_co = ParamSpec("P_co", covariant=False, contravariant=True)
|
type_name_incorrect_variance.py:25:8: PLC0105 `ParamSpec` name "P_co" does not match variance
type_name_incorrect_variance.py:25:8: PLC0105 `ParamSpec` name "P_co" does not reflect its contravariance; consider renaming it to "P_contra"
|
23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False)
24 | P_co = ParamSpec("P_co", contravariant=True)
@@ -197,7 +197,7 @@ type_name_incorrect_variance.py:25:8: PLC0105 `ParamSpec` name "P_co" does not m
27 | T_contra = TypeVar("T_contra")
|
type_name_incorrect_variance.py:27:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:27:12: PLC0105 `TypeVar` name "T_contra" does not reflect its invariance; consider renaming it to "T"
|
25 | P_co = ParamSpec("P_co", covariant=False, contravariant=True)
26 |
@@ -207,7 +207,7 @@ type_name_incorrect_variance.py:27:12: PLC0105 `TypeVar` name "T_contra" does no
29 | T_contra = TypeVar("T_contra", contravariant=False)
|
type_name_incorrect_variance.py:28:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:28:12: PLC0105 `TypeVar` name "T_contra" does not reflect its invariance; consider renaming it to "T"
|
27 | T_contra = TypeVar("T_contra")
28 | T_contra = TypeVar("T_contra", covariant=False)
@@ -216,7 +216,7 @@ type_name_incorrect_variance.py:28:12: PLC0105 `TypeVar` name "T_contra" does no
30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False)
|
type_name_incorrect_variance.py:29:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:29:12: PLC0105 `TypeVar` name "T_contra" does not reflect its invariance; consider renaming it to "T"
|
27 | T_contra = TypeVar("T_contra")
28 | T_contra = TypeVar("T_contra", covariant=False)
@@ -226,7 +226,7 @@ type_name_incorrect_variance.py:29:12: PLC0105 `TypeVar` name "T_contra" does no
31 | T_contra = TypeVar("T_contra", covariant=True)
|
type_name_incorrect_variance.py:30:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:30:12: PLC0105 `TypeVar` name "T_contra" does not reflect its invariance; consider renaming it to "T"
|
28 | T_contra = TypeVar("T_contra", covariant=False)
29 | T_contra = TypeVar("T_contra", contravariant=False)
@@ -236,7 +236,7 @@ type_name_incorrect_variance.py:30:12: PLC0105 `TypeVar` name "T_contra" does no
32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False)
|
type_name_incorrect_variance.py:31:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:31:12: PLC0105 `TypeVar` name "T_contra" does not reflect its covariance; consider renaming it to "T_co"
|
29 | T_contra = TypeVar("T_contra", contravariant=False)
30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False)
@@ -246,7 +246,7 @@ type_name_incorrect_variance.py:31:12: PLC0105 `TypeVar` name "T_contra" does no
33 | P_contra = ParamSpec("P_contra")
|
type_name_incorrect_variance.py:32:12: PLC0105 `TypeVar` name "T_contra" does not match variance
type_name_incorrect_variance.py:32:12: PLC0105 `TypeVar` name "T_contra" does not reflect its covariance; consider renaming it to "T_co"
|
30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False)
31 | T_contra = TypeVar("T_contra", covariant=True)
@@ -256,7 +256,7 @@ type_name_incorrect_variance.py:32:12: PLC0105 `TypeVar` name "T_contra" does no
34 | P_contra = ParamSpec("P_contra", covariant=False)
|
type_name_incorrect_variance.py:33:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:33:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its invariance; consider renaming it to "P"
|
31 | T_contra = TypeVar("T_contra", covariant=True)
32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False)
@@ -266,7 +266,7 @@ type_name_incorrect_variance.py:33:12: PLC0105 `ParamSpec` name "P_contra" does
35 | P_contra = ParamSpec("P_contra", contravariant=False)
|
type_name_incorrect_variance.py:34:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:34:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its invariance; consider renaming it to "P"
|
32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False)
33 | P_contra = ParamSpec("P_contra")
@@ -276,7 +276,7 @@ type_name_incorrect_variance.py:34:12: PLC0105 `ParamSpec` name "P_contra" does
36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False)
|
type_name_incorrect_variance.py:35:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:35:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its invariance; consider renaming it to "P"
|
33 | P_contra = ParamSpec("P_contra")
34 | P_contra = ParamSpec("P_contra", covariant=False)
@@ -286,7 +286,7 @@ type_name_incorrect_variance.py:35:12: PLC0105 `ParamSpec` name "P_contra" does
37 | P_contra = ParamSpec("P_contra", covariant=True)
|
type_name_incorrect_variance.py:36:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:36:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its invariance; consider renaming it to "P"
|
34 | P_contra = ParamSpec("P_contra", covariant=False)
35 | P_contra = ParamSpec("P_contra", contravariant=False)
@@ -296,7 +296,7 @@ type_name_incorrect_variance.py:36:12: PLC0105 `ParamSpec` name "P_contra" does
38 | P_contra = ParamSpec("P_contra", covariant=True, contravariant=False)
|
type_name_incorrect_variance.py:37:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:37:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its covariance; consider renaming it to "P_co"
|
35 | P_contra = ParamSpec("P_contra", contravariant=False)
36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False)
@@ -305,7 +305,7 @@ type_name_incorrect_variance.py:37:12: PLC0105 `ParamSpec` name "P_contra" does
38 | P_contra = ParamSpec("P_contra", covariant=True, contravariant=False)
|
type_name_incorrect_variance.py:38:12: PLC0105 `ParamSpec` name "P_contra" does not match variance
type_name_incorrect_variance.py:38:12: PLC0105 `ParamSpec` name "P_contra" does not reflect its covariance; consider renaming it to "P_co"
|
36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False)
37 | P_contra = ParamSpec("P_contra", covariant=True)