diff --git a/crates/ruff/resources/test/fixtures/pylint/type_name_incorrect_variance.py b/crates/ruff/resources/test/fixtures/pylint/type_name_incorrect_variance.py new file mode 100644 index 0000000000000..33f68d2a04625 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/pylint/type_name_incorrect_variance.py @@ -0,0 +1,59 @@ +from typing import ParamSpec, TypeVar + +# Errors. + +T = TypeVar("T", covariant=True) +T = TypeVar("T", covariant=True, contravariant=False) +T = TypeVar("T", contravariant=True) +T = TypeVar("T", covariant=False, contravariant=True) +P = ParamSpec("P", covariant=True) +P = ParamSpec("P", covariant=True, contravariant=False) +P = ParamSpec("P", contravariant=True) +P = ParamSpec("P", covariant=False, contravariant=True) + +T_co = TypeVar("T_co") +T_co = TypeVar("T_co", covariant=False) +T_co = TypeVar("T_co", contravariant=False) +T_co = TypeVar("T_co", covariant=False, contravariant=False) +T_co = TypeVar("T_co", contravariant=True) +T_co = TypeVar("T_co", covariant=False, contravariant=True) +P_co = ParamSpec("P_co") +P_co = ParamSpec("P_co", covariant=False) +P_co = ParamSpec("P_co", contravariant=False) +P_co = ParamSpec("P_co", covariant=False, contravariant=False) +P_co = ParamSpec("P_co", contravariant=True) +P_co = ParamSpec("P_co", covariant=False, contravariant=True) + +T_contra = TypeVar("T_contra") +T_contra = TypeVar("T_contra", covariant=False) +T_contra = TypeVar("T_contra", contravariant=False) +T_contra = TypeVar("T_contra", covariant=False, contravariant=False) +T_contra = TypeVar("T_contra", covariant=True) +T_contra = TypeVar("T_contra", covariant=True, contravariant=False) +P_contra = ParamSpec("P_contra") +P_contra = ParamSpec("P_contra", covariant=False) +P_contra = ParamSpec("P_contra", contravariant=False) +P_contra = ParamSpec("P_contra", covariant=False, contravariant=False) +P_contra = ParamSpec("P_contra", covariant=True) +P_contra = ParamSpec("P_contra", covariant=True, contravariant=False) + +# Non-errors. + +T = TypeVar("T") +T = TypeVar("T", covariant=False) +T = TypeVar("T", contravariant=False) +T = TypeVar("T", covariant=False, contravariant=False) +P = ParamSpec("P") +P = ParamSpec("P", covariant=False) +P = ParamSpec("P", contravariant=False) +P = ParamSpec("P", covariant=False, contravariant=False) + +T_co = TypeVar("T_co", covariant=True) +T_co = TypeVar("T_co", covariant=True, contravariant=False) +P_co = ParamSpec("P_co", covariant=True) +P_co = ParamSpec("P_co", covariant=True, contravariant=False) + +T_contra = TypeVar("T_contra", contravariant=True) +T_contra = TypeVar("T_contra", covariant=False, contravariant=True) +P_contra = ParamSpec("P_contra", contravariant=True) +P_contra = ParamSpec("P_contra", covariant=False, contravariant=True) diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 8393ccf963e31..df808a2ef153c 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -1658,6 +1658,9 @@ where if self.settings.rules.enabled(Rule::TypeParamNameMismatch) { pylint::rules::type_param_name_mismatch(self, value, targets); } + if self.settings.rules.enabled(Rule::TypeNameIncorrectVariance) { + pylint::rules::type_name_incorrect_variance(self, value); + } if self.settings.rules.enabled(Rule::TypeBivariance) { pylint::rules::type_bivariance(self, value); } diff --git a/crates/ruff/src/codes.rs b/crates/ruff/src/codes.rs index b73d2caa44619..31b80fcff4007 100644 --- a/crates/ruff/src/codes.rs +++ b/crates/ruff/src/codes.rs @@ -168,6 +168,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pyflakes, "901") => (RuleGroup::Unspecified, rules::pyflakes::rules::RaiseNotImplemented), // pylint + (Pylint, "C0105") => (RuleGroup::Unspecified, rules::pylint::rules::TypeNameIncorrectVariance), (Pylint, "C0131") => (RuleGroup::Unspecified, rules::pylint::rules::TypeBivariance), (Pylint, "C0132") => (RuleGroup::Unspecified, rules::pylint::rules::TypeParamNameMismatch), (Pylint, "C0205") => (RuleGroup::Unspecified, rules::pylint::rules::SingleStringSlots), diff --git a/crates/ruff/src/rules/pylint/mod.rs b/crates/ruff/src/rules/pylint/mod.rs index 9129aeb506fd9..f6f5330d06e81 100644 --- a/crates/ruff/src/rules/pylint/mod.rs +++ b/crates/ruff/src/rules/pylint/mod.rs @@ -86,6 +86,10 @@ mod tests { )] #[test_case(Rule::TooManyStatements, Path::new("too_many_statements.py"))] #[test_case(Rule::TypeBivariance, Path::new("type_bivariance.py"))] + #[test_case( + Rule::TypeNameIncorrectVariance, + Path::new("type_name_incorrect_variance.py") + )] #[test_case(Rule::TypeParamNameMismatch, Path::new("type_param_name_mismatch.py"))] #[test_case( Rule::UnexpectedSpecialMethodSignature, diff --git a/crates/ruff/src/rules/pylint/rules/mod.rs b/crates/ruff/src/rules/pylint/rules/mod.rs index ad2b6d0d6cfa7..551abb1adab46 100644 --- a/crates/ruff/src/rules/pylint/rules/mod.rs +++ b/crates/ruff/src/rules/pylint/rules/mod.rs @@ -38,6 +38,7 @@ pub(crate) use too_many_branches::*; pub(crate) use too_many_return_statements::*; pub(crate) use too_many_statements::*; pub(crate) use type_bivariance::*; +pub(crate) use type_name_incorrect_variance::*; pub(crate) use type_param_name_mismatch::*; pub(crate) use unexpected_special_method_signature::*; pub(crate) use unnecessary_direct_lambda_call::*; @@ -87,6 +88,7 @@ mod too_many_branches; mod too_many_return_statements; mod too_many_statements; mod type_bivariance; +mod type_name_incorrect_variance; mod type_param_name_mismatch; mod unexpected_special_method_signature; mod unnecessary_direct_lambda_call; diff --git a/crates/ruff/src/rules/pylint/rules/type_name_incorrect_variance.rs b/crates/ruff/src/rules/pylint/rules/type_name_incorrect_variance.rs new file mode 100644 index 0000000000000..541f5e7f8f2c8 --- /dev/null +++ b/crates/ruff/src/rules/pylint/rules/type_name_incorrect_variance.rs @@ -0,0 +1,154 @@ +use std::fmt; + +use rustpython_parser::ast::{self, Expr, Ranged}; + +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::helpers::is_const_true; + +use crate::checkers::ast::Checker; +use crate::rules::pylint::helpers::type_param_name; + +/// ## What it does +/// Checks for type names that do not match the variance of their associated +/// type parameter. +/// +/// ## Why is this bad? +/// [PEP 484] recommends the use of the `_co` and `_contra` suffixes for +/// covariant and contravariant type parameters, respectively (while invariant +/// type parameters should not have any such suffix). +/// +/// ## Example +/// ```python +/// from typing import TypeVar +/// +/// T = TypeVar("T", covariant=True) +/// U = TypeVar("U", contravariant=True) +/// V_co = TypeVar("V_co") +/// ``` +/// +/// Use instead: +/// ```python +/// from typing import TypeVar +/// +/// T_co = TypeVar("T_co", covariant=True) +/// U_contra = TypeVar("U_contra", contravariant=True) +/// V = TypeVar("V") +/// ``` +/// +/// ## References +/// - [Python documentation: `typing` — Support for type hints](https://docs.python.org/3/library/typing.html) +/// - [PEP 483 – The Theory of Type Hints: Covariance and Contravariance](https://peps.python.org/pep-0483/#covariance-and-contravariance) +/// - [PEP 484 – Type Hints: Covariance and contravariance](https://peps.python.org/pep-0484/#covariance-and-contravariance) +/// +/// [PEP 484]: https://www.python.org/dev/peps/pep-0484/ +#[violation] +pub struct TypeNameIncorrectVariance { + kind: VarKind, + param_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") + } +} + +/// PLC0105 +pub(crate) fn type_name_incorrect_variance(checker: &mut Checker, value: &Expr) { + let Expr::Call(ast::ExprCall { + func, + args, + keywords, + .. + }) = value + else { + return; + }; + + let Some(param_name) = type_param_name(args, keywords) else { + return; + }; + + let covariant = keywords + .iter() + .find(|keyword| { + keyword + .arg + .as_ref() + .map_or(false, |keyword| keyword.as_str() == "covariant") + }) + .map(|keyword| &keyword.value); + + let contravariant = keywords + .iter() + .find(|keyword| { + keyword + .arg + .as_ref() + .map_or(false, |keyword| keyword.as_str() == "contravariant") + }) + .map(|keyword| &keyword.value); + + if !mismatch(param_name, covariant, contravariant) { + return; + } + + let Some(kind) = checker + .semantic() + .resolve_call_path(func) + .and_then(|call_path| { + if checker + .semantic() + .match_typing_call_path(&call_path, "ParamSpec") + { + Some(VarKind::ParamSpec) + } else if checker + .semantic() + .match_typing_call_path(&call_path, "TypeVar") + { + Some(VarKind::TypeVar) + } else { + None + } + }) + else { + return; + }; + + checker.diagnostics.push(Diagnostic::new( + TypeNameIncorrectVariance { + kind, + param_name: param_name.to_string(), + }, + func.range(), + )); +} + +/// Returns `true` if the parameter name does not match its type variance. +fn mismatch(param_name: &str, covariant: Option<&Expr>, contravariant: Option<&Expr>) -> bool { + if param_name.ends_with("_co") { + covariant.map_or(true, |covariant| !is_const_true(covariant)) + } else if param_name.ends_with("_contra") { + contravariant.map_or(true, |contravariant| !is_const_true(contravariant)) + } else { + covariant.map_or(false, is_const_true) || contravariant.map_or(false, is_const_true) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum VarKind { + TypeVar, + ParamSpec, +} + +impl fmt::Display for VarKind { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + VarKind::TypeVar => fmt.write_str("TypeVar"), + VarKind::ParamSpec => fmt.write_str("ParamSpec"), + } + } +} diff --git a/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLC0105_type_name_incorrect_variance.py.snap b/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLC0105_type_name_incorrect_variance.py.snap new file mode 100644 index 0000000000000..8e54cce037f96 --- /dev/null +++ b/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLC0105_type_name_incorrect_variance.py.snap @@ -0,0 +1,318 @@ +--- +source: crates/ruff/src/rules/pylint/mod.rs +--- +type_name_incorrect_variance.py:5:5: PLC0105 `TypeVar` name "T" does not match variance + | +3 | # Errors. +4 | +5 | T = TypeVar("T", covariant=True) + | ^^^^^^^ PLC0105 +6 | T = TypeVar("T", covariant=True, contravariant=False) +7 | T = TypeVar("T", contravariant=True) + | + +type_name_incorrect_variance.py:6:5: PLC0105 `TypeVar` name "T" does not match variance + | +5 | T = TypeVar("T", covariant=True) +6 | T = TypeVar("T", covariant=True, contravariant=False) + | ^^^^^^^ PLC0105 +7 | T = TypeVar("T", contravariant=True) +8 | T = TypeVar("T", covariant=False, contravariant=True) + | + +type_name_incorrect_variance.py:7:5: PLC0105 `TypeVar` name "T" does not match variance + | +5 | T = TypeVar("T", covariant=True) +6 | T = TypeVar("T", covariant=True, contravariant=False) +7 | T = TypeVar("T", contravariant=True) + | ^^^^^^^ PLC0105 +8 | T = TypeVar("T", covariant=False, contravariant=True) +9 | P = ParamSpec("P", covariant=True) + | + +type_name_incorrect_variance.py:8:5: PLC0105 `TypeVar` name "T" does not match variance + | + 6 | T = TypeVar("T", covariant=True, contravariant=False) + 7 | T = TypeVar("T", contravariant=True) + 8 | T = TypeVar("T", covariant=False, contravariant=True) + | ^^^^^^^ PLC0105 + 9 | P = ParamSpec("P", covariant=True) +10 | P = ParamSpec("P", covariant=True, contravariant=False) + | + +type_name_incorrect_variance.py:9:5: PLC0105 `ParamSpec` name "P" does not match variance + | + 7 | T = TypeVar("T", contravariant=True) + 8 | T = TypeVar("T", covariant=False, contravariant=True) + 9 | P = ParamSpec("P", covariant=True) + | ^^^^^^^^^ PLC0105 +10 | P = ParamSpec("P", covariant=True, contravariant=False) +11 | P = ParamSpec("P", contravariant=True) + | + +type_name_incorrect_variance.py:10:5: PLC0105 `ParamSpec` name "P" does not match variance + | + 8 | T = TypeVar("T", covariant=False, contravariant=True) + 9 | P = ParamSpec("P", covariant=True) +10 | P = ParamSpec("P", covariant=True, contravariant=False) + | ^^^^^^^^^ PLC0105 +11 | P = ParamSpec("P", contravariant=True) +12 | P = ParamSpec("P", covariant=False, contravariant=True) + | + +type_name_incorrect_variance.py:11:5: PLC0105 `ParamSpec` name "P" does not match variance + | + 9 | P = ParamSpec("P", covariant=True) +10 | P = ParamSpec("P", covariant=True, contravariant=False) +11 | P = ParamSpec("P", contravariant=True) + | ^^^^^^^^^ PLC0105 +12 | P = ParamSpec("P", covariant=False, contravariant=True) + | + +type_name_incorrect_variance.py:12:5: PLC0105 `ParamSpec` name "P" does not match variance + | +10 | P = ParamSpec("P", covariant=True, contravariant=False) +11 | P = ParamSpec("P", contravariant=True) +12 | P = ParamSpec("P", covariant=False, contravariant=True) + | ^^^^^^^^^ PLC0105 +13 | +14 | T_co = TypeVar("T_co") + | + +type_name_incorrect_variance.py:14:8: PLC0105 `TypeVar` name "T_co" does not match variance + | +12 | P = ParamSpec("P", covariant=False, contravariant=True) +13 | +14 | T_co = TypeVar("T_co") + | ^^^^^^^ PLC0105 +15 | T_co = TypeVar("T_co", covariant=False) +16 | T_co = TypeVar("T_co", contravariant=False) + | + +type_name_incorrect_variance.py:15:8: PLC0105 `TypeVar` name "T_co" does not match variance + | +14 | T_co = TypeVar("T_co") +15 | T_co = TypeVar("T_co", covariant=False) + | ^^^^^^^ PLC0105 +16 | T_co = TypeVar("T_co", contravariant=False) +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 + | +14 | T_co = TypeVar("T_co") +15 | T_co = TypeVar("T_co", covariant=False) +16 | T_co = TypeVar("T_co", contravariant=False) + | ^^^^^^^ PLC0105 +17 | T_co = TypeVar("T_co", covariant=False, contravariant=False) +18 | T_co = TypeVar("T_co", contravariant=True) + | + +type_name_incorrect_variance.py:17:8: PLC0105 `TypeVar` name "T_co" does not match variance + | +15 | T_co = TypeVar("T_co", covariant=False) +16 | T_co = TypeVar("T_co", contravariant=False) +17 | T_co = TypeVar("T_co", covariant=False, contravariant=False) + | ^^^^^^^ PLC0105 +18 | T_co = TypeVar("T_co", contravariant=True) +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 + | +16 | T_co = TypeVar("T_co", contravariant=False) +17 | T_co = TypeVar("T_co", covariant=False, contravariant=False) +18 | T_co = TypeVar("T_co", contravariant=True) + | ^^^^^^^ PLC0105 +19 | T_co = TypeVar("T_co", covariant=False, contravariant=True) +20 | P_co = ParamSpec("P_co") + | + +type_name_incorrect_variance.py:19:8: PLC0105 `TypeVar` name "T_co" does not match variance + | +17 | T_co = TypeVar("T_co", covariant=False, contravariant=False) +18 | T_co = TypeVar("T_co", contravariant=True) +19 | T_co = TypeVar("T_co", covariant=False, contravariant=True) + | ^^^^^^^ PLC0105 +20 | P_co = ParamSpec("P_co") +21 | P_co = ParamSpec("P_co", covariant=False) + | + +type_name_incorrect_variance.py:20:8: PLC0105 `ParamSpec` name "P_co" does not match variance + | +18 | T_co = TypeVar("T_co", contravariant=True) +19 | T_co = TypeVar("T_co", covariant=False, contravariant=True) +20 | P_co = ParamSpec("P_co") + | ^^^^^^^^^ PLC0105 +21 | P_co = ParamSpec("P_co", covariant=False) +22 | P_co = ParamSpec("P_co", contravariant=False) + | + +type_name_incorrect_variance.py:21:8: PLC0105 `ParamSpec` name "P_co" does not match variance + | +19 | T_co = TypeVar("T_co", covariant=False, contravariant=True) +20 | P_co = ParamSpec("P_co") +21 | P_co = ParamSpec("P_co", covariant=False) + | ^^^^^^^^^ PLC0105 +22 | P_co = ParamSpec("P_co", contravariant=False) +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 + | +20 | P_co = ParamSpec("P_co") +21 | P_co = ParamSpec("P_co", covariant=False) +22 | P_co = ParamSpec("P_co", contravariant=False) + | ^^^^^^^^^ PLC0105 +23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False) +24 | P_co = ParamSpec("P_co", contravariant=True) + | + +type_name_incorrect_variance.py:23:8: PLC0105 `ParamSpec` name "P_co" does not match variance + | +21 | P_co = ParamSpec("P_co", covariant=False) +22 | P_co = ParamSpec("P_co", contravariant=False) +23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False) + | ^^^^^^^^^ PLC0105 +24 | P_co = ParamSpec("P_co", contravariant=True) +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 + | +22 | P_co = ParamSpec("P_co", contravariant=False) +23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False) +24 | P_co = ParamSpec("P_co", contravariant=True) + | ^^^^^^^^^ PLC0105 +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 + | +23 | P_co = ParamSpec("P_co", covariant=False, contravariant=False) +24 | P_co = ParamSpec("P_co", contravariant=True) +25 | P_co = ParamSpec("P_co", covariant=False, contravariant=True) + | ^^^^^^^^^ PLC0105 +26 | +27 | T_contra = TypeVar("T_contra") + | + +type_name_incorrect_variance.py:27:12: PLC0105 `TypeVar` name "T_contra" does not match variance + | +25 | P_co = ParamSpec("P_co", covariant=False, contravariant=True) +26 | +27 | T_contra = TypeVar("T_contra") + | ^^^^^^^ PLC0105 +28 | T_contra = TypeVar("T_contra", covariant=False) +29 | T_contra = TypeVar("T_contra", contravariant=False) + | + +type_name_incorrect_variance.py:28:12: PLC0105 `TypeVar` name "T_contra" does not match variance + | +27 | T_contra = TypeVar("T_contra") +28 | T_contra = TypeVar("T_contra", covariant=False) + | ^^^^^^^ PLC0105 +29 | T_contra = TypeVar("T_contra", contravariant=False) +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 + | +27 | T_contra = TypeVar("T_contra") +28 | T_contra = TypeVar("T_contra", covariant=False) +29 | T_contra = TypeVar("T_contra", contravariant=False) + | ^^^^^^^ PLC0105 +30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False) +31 | T_contra = TypeVar("T_contra", covariant=True) + | + +type_name_incorrect_variance.py:30:12: PLC0105 `TypeVar` name "T_contra" does not match variance + | +28 | T_contra = TypeVar("T_contra", covariant=False) +29 | T_contra = TypeVar("T_contra", contravariant=False) +30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False) + | ^^^^^^^ PLC0105 +31 | T_contra = TypeVar("T_contra", covariant=True) +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 + | +29 | T_contra = TypeVar("T_contra", contravariant=False) +30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False) +31 | T_contra = TypeVar("T_contra", covariant=True) + | ^^^^^^^ PLC0105 +32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False) +33 | P_contra = ParamSpec("P_contra") + | + +type_name_incorrect_variance.py:32:12: PLC0105 `TypeVar` name "T_contra" does not match variance + | +30 | T_contra = TypeVar("T_contra", covariant=False, contravariant=False) +31 | T_contra = TypeVar("T_contra", covariant=True) +32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False) + | ^^^^^^^ PLC0105 +33 | P_contra = ParamSpec("P_contra") +34 | P_contra = ParamSpec("P_contra", covariant=False) + | + +type_name_incorrect_variance.py:33:12: PLC0105 `ParamSpec` name "P_contra" does not match variance + | +31 | T_contra = TypeVar("T_contra", covariant=True) +32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False) +33 | P_contra = ParamSpec("P_contra") + | ^^^^^^^^^ PLC0105 +34 | P_contra = ParamSpec("P_contra", covariant=False) +35 | P_contra = ParamSpec("P_contra", contravariant=False) + | + +type_name_incorrect_variance.py:34:12: PLC0105 `ParamSpec` name "P_contra" does not match variance + | +32 | T_contra = TypeVar("T_contra", covariant=True, contravariant=False) +33 | P_contra = ParamSpec("P_contra") +34 | P_contra = ParamSpec("P_contra", covariant=False) + | ^^^^^^^^^ PLC0105 +35 | P_contra = ParamSpec("P_contra", contravariant=False) +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 + | +33 | P_contra = ParamSpec("P_contra") +34 | P_contra = ParamSpec("P_contra", covariant=False) +35 | P_contra = ParamSpec("P_contra", contravariant=False) + | ^^^^^^^^^ PLC0105 +36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False) +37 | P_contra = ParamSpec("P_contra", covariant=True) + | + +type_name_incorrect_variance.py:36:12: PLC0105 `ParamSpec` name "P_contra" does not match variance + | +34 | P_contra = ParamSpec("P_contra", covariant=False) +35 | P_contra = ParamSpec("P_contra", contravariant=False) +36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False) + | ^^^^^^^^^ PLC0105 +37 | P_contra = ParamSpec("P_contra", covariant=True) +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 + | +35 | P_contra = ParamSpec("P_contra", contravariant=False) +36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False) +37 | P_contra = ParamSpec("P_contra", covariant=True) + | ^^^^^^^^^ PLC0105 +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 + | +36 | P_contra = ParamSpec("P_contra", covariant=False, contravariant=False) +37 | P_contra = ParamSpec("P_contra", covariant=True) +38 | P_contra = ParamSpec("P_contra", covariant=True, contravariant=False) + | ^^^^^^^^^ PLC0105 +39 | +40 | # Non-errors. + | + + diff --git a/ruff.schema.json b/ruff.schema.json index f283f253b8865..15cc68f75279d 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2129,6 +2129,8 @@ "PLC", "PLC0", "PLC01", + "PLC010", + "PLC0105", "PLC013", "PLC0131", "PLC0132",