From 1dcbdcde219d2c0dc31342592a36c7c5c13c1ca8 Mon Sep 17 00:00:00 2001 From: Steve C Date: Sun, 15 Oct 2023 23:49:56 -0400 Subject: [PATCH 1/4] implement PLR6201 with autofix --- .../test/fixtures/pylint/set_membership.py | 13 ++++ .../src/checkers/ast/analyze/expression.rs | 3 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/pylint/mod.rs | 1 + .../ruff_linter/src/rules/pylint/rules/mod.rs | 2 + .../src/rules/pylint/rules/set_membership.rs | 77 +++++++++++++++++++ ...int__tests__PLR6201_set_membership.py.snap | 63 +++++++++++++++ ruff.schema.json | 3 + 8 files changed, 163 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py create mode 100644 crates/ruff_linter/src/rules/pylint/rules/set_membership.rs create mode 100644 crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py b/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py new file mode 100644 index 0000000000000..41afe29f4d818 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py @@ -0,0 +1,13 @@ +# PLR6201 +1 in [1, 2, 3] + +# PLR6201 +1 in (1, 2, 3) + +# PLR6201 +def fruit_is_dangerous_for_cat(fruit: str) -> bool: + return fruit in ["cherry", "grapes"] + +# Ok +fruits = ["cherry", "grapes"] +"cherry" in fruits diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 58ec6f6e4bdcd..dd70eb6c1ec69 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -1194,6 +1194,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::ComparisonWithItself) { pylint::rules::comparison_with_itself(checker, left, ops, comparators); } + if checker.enabled(Rule::SetMembership) { + pylint::rules::set_membership(checker, compare); + } if checker.enabled(Rule::ComparisonOfConstant) { pylint::rules::comparison_of_constant(checker, left, ops, comparators); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index a0722c0741b9b..cc92eab7c8ae1 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -239,6 +239,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "E2514") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidCharacterNul), (Pylint, "E2515") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidCharacterZeroWidthSpace), (Pylint, "R0124") => (RuleGroup::Unspecified, rules::pylint::rules::ComparisonWithItself), + (Pylint, "R6201") => (RuleGroup::Unspecified, rules::pylint::rules::SetMembership), (Pylint, "R0133") => (RuleGroup::Unspecified, rules::pylint::rules::ComparisonOfConstant), (Pylint, "R0206") => (RuleGroup::Unspecified, rules::pylint::rules::PropertyWithParameters), (Pylint, "R0402") => (RuleGroup::Unspecified, rules::pylint::rules::ManualFromImport), diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index 21b0c0250f911..dca63cb6174cd 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -135,6 +135,7 @@ mod tests { )] #[test_case(Rule::BadDunderMethodName, Path::new("bad_dunder_method_name.py"))] #[test_case(Rule::NoSelfUse, Path::new("no_self_use.py"))] + #[test_case(Rule::SetMembership, Path::new("set_membership.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pylint/rules/mod.rs b/crates/ruff_linter/src/rules/pylint/rules/mod.rs index 317657266794b..4f8ff68f40ce3 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/mod.rs @@ -38,6 +38,7 @@ pub(crate) use repeated_equality_comparison::*; pub(crate) use repeated_isinstance_calls::*; pub(crate) use return_in_init::*; pub(crate) use self_assigning_variable::*; +pub(crate) use set_membership::*; pub(crate) use single_string_slots::*; pub(crate) use subprocess_popen_preexec_fn::*; pub(crate) use subprocess_run_without_check::*; @@ -98,6 +99,7 @@ mod repeated_equality_comparison; mod repeated_isinstance_calls; mod return_in_init; mod self_assigning_variable; +mod set_membership; mod single_string_slots; mod subprocess_popen_preexec_fn; mod subprocess_run_without_check; diff --git a/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs b/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs new file mode 100644 index 0000000000000..1951df9fdee0b --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs @@ -0,0 +1,77 @@ +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, CmpOp, Expr}; +use ruff_text_size::{Ranged, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for operations that checks a list or tuple for an element. +/// +/// ## Why is this bad? +/// Membership tests are more efficient when performed on a +/// lookup-optimized datatype like `set`. +/// +/// ## Example +/// ```python +/// 1 in [1, 2, 3] +/// ``` +/// +/// Use instead: +/// ```python +/// 1 in {1, 2, 3} +/// ``` +/// ## References +/// - [Python 3.2 release notes](https://docs.python.org/3/whatsnew/3.2.html#optimizations) +#[violation] +pub struct SetMembership; + +impl AlwaysFixableViolation for SetMembership { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use a `set` when checking for element membership") + } + + fn fix_title(&self) -> String { + format!("Use a `set` when checking for element membership") + } +} + +/// PLR6201 +pub(crate) fn set_membership(checker: &mut Checker, compare: &ast::ExprCompare) { + let [op] = compare.ops.as_slice() else { + return; + }; + + if !matches!(op, CmpOp::In | CmpOp::NotIn) { + return; + } + + let [right] = compare.comparators.as_slice() else { + return; + }; + + let (Expr::List(ast::ExprList { + elts: right_elements, + .. + }) + | Expr::Tuple(ast::ExprTuple { + elts: right_elements, + .. + })) = right + else { + return; + }; + + let mut diagnostic = Diagnostic::new(SetMembership, right.range()); + + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + checker.generator().expr(&Expr::Set(ast::ExprSet { + elts: right_elements.clone(), + range: TextRange::default(), + })), + right.range(), + ))); + + checker.diagnostics.push(diagnostic); +} diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap new file mode 100644 index 0000000000000..f7c702b5ba37d --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap @@ -0,0 +1,63 @@ +--- +source: crates/ruff_linter/src/rules/pylint/mod.rs +--- +set_membership.py:2:6: PLR6201 [*] Use a `set` when checking for element membership + | +1 | # PLR6201 +2 | 1 in [1, 2, 3] + | ^^^^^^^^^ PLR6201 +3 | +4 | # PLR6201 + | + = help: Use a `set` when checking for element membership + +ℹ Fix +1 1 | # PLR6201 +2 |-1 in [1, 2, 3] + 2 |+1 in {1, 2, 3} +3 3 | +4 4 | # PLR6201 +5 5 | 1 in (1, 2, 3) + +set_membership.py:5:6: PLR6201 [*] Use a `set` when checking for element membership + | +4 | # PLR6201 +5 | 1 in (1, 2, 3) + | ^^^^^^^^^ PLR6201 +6 | +7 | # PLR6201 + | + = help: Use a `set` when checking for element membership + +ℹ Fix +2 2 | 1 in [1, 2, 3] +3 3 | +4 4 | # PLR6201 +5 |-1 in (1, 2, 3) + 5 |+1 in {1, 2, 3} +6 6 | +7 7 | # PLR6201 +8 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: + +set_membership.py:9:21: PLR6201 [*] Use a `set` when checking for element membership + | + 7 | # PLR6201 + 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: + 9 | return fruit in ["cherry", "grapes"] + | ^^^^^^^^^^^^^^^^^^^^ PLR6201 +10 | +11 | # Ok + | + = help: Use a `set` when checking for element membership + +ℹ Fix +6 6 | +7 7 | # PLR6201 +8 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: +9 |- return fruit in ["cherry", "grapes"] + 9 |+ return fruit in {"cherry", "grapes"} +10 10 | +11 11 | # Ok +12 12 | fruits = ["cherry", "grapes"] + + diff --git a/ruff.schema.json b/ruff.schema.json index 7be275a4100e0..1da4ad7707355 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2975,6 +2975,9 @@ "PLR550", "PLR5501", "PLR6", + "PLR62", + "PLR620", + "PLR6201", "PLR63", "PLR630", "PLR6301", From 88b664df4a31eda3cb13d35c7ff7aae375b26568 Mon Sep 17 00:00:00 2001 From: Steve C Date: Mon, 16 Oct 2023 15:15:36 -0400 Subject: [PATCH 2/4] move to preview --- crates/ruff_linter/src/codes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index cc92eab7c8ae1..c399797d6b506 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -239,7 +239,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "E2514") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidCharacterNul), (Pylint, "E2515") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidCharacterZeroWidthSpace), (Pylint, "R0124") => (RuleGroup::Unspecified, rules::pylint::rules::ComparisonWithItself), - (Pylint, "R6201") => (RuleGroup::Unspecified, rules::pylint::rules::SetMembership), + (Pylint, "R6201") => (RuleGroup::Preview, rules::pylint::rules::SetMembership), (Pylint, "R0133") => (RuleGroup::Unspecified, rules::pylint::rules::ComparisonOfConstant), (Pylint, "R0206") => (RuleGroup::Unspecified, rules::pylint::rules::PropertyWithParameters), (Pylint, "R0402") => (RuleGroup::Unspecified, rules::pylint::rules::ManualFromImport), From f2e14690198aab169f9937e55ad5bee3af8c0e9d Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 17 Oct 2023 00:04:11 -0400 Subject: [PATCH 3/4] Tweak implementation --- .../test/fixtures/pylint/set_membership.py | 13 ++- .../src/rules/pylint/rules/set_membership.rs | 32 +++----- ...int__tests__PLR6201_set_membership.py.snap | 80 ++++++++++--------- 3 files changed, 58 insertions(+), 67 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py b/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py index 41afe29f4d818..84e0df55c1384 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py @@ -1,13 +1,10 @@ -# PLR6201 +# Errors 1 in [1, 2, 3] - -# PLR6201 1 in (1, 2, 3) +1 in ( + 1, 2, 3 +) -# PLR6201 -def fruit_is_dangerous_for_cat(fruit: str) -> bool: - return fruit in ["cherry", "grapes"] - -# Ok +# OK fruits = ["cherry", "grapes"] "cherry" in fruits diff --git a/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs b/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs index 1951df9fdee0b..be62b756a160f 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs @@ -1,16 +1,16 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::{self as ast, CmpOp, Expr}; -use ruff_text_size::{Ranged, TextRange}; +use ruff_text_size::Ranged; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for operations that checks a list or tuple for an element. +/// Checks for membership tests on `list` and `tuple` literals. /// /// ## Why is this bad? -/// Membership tests are more efficient when performed on a -/// lookup-optimized datatype like `set`. +/// When testing for membership in a static sequence, prefer a `set` literal +/// over a `list` or `tuple`, as Python optimizes `set` membership tests. /// /// ## Example /// ```python @@ -22,7 +22,7 @@ use crate::checkers::ast::Checker; /// 1 in {1, 2, 3} /// ``` /// ## References -/// - [Python 3.2 release notes](https://docs.python.org/3/whatsnew/3.2.html#optimizations) +/// - [What’s New In Python 3.2](https://docs.python.org/3/whatsnew/3.2.html#optimizations) #[violation] pub struct SetMembership; @@ -51,27 +51,15 @@ pub(crate) fn set_membership(checker: &mut Checker, compare: &ast::ExprCompare) return; }; - let (Expr::List(ast::ExprList { - elts: right_elements, - .. - }) - | Expr::Tuple(ast::ExprTuple { - elts: right_elements, - .. - })) = right - else { + if !matches!(right, Expr::List(_) | Expr::Tuple(_)) { return; - }; + } let mut diagnostic = Diagnostic::new(SetMembership, right.range()); - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - checker.generator().expr(&Expr::Set(ast::ExprSet { - elts: right_elements.clone(), - range: TextRange::default(), - })), - right.range(), - ))); + let literal = checker.locator().slice(right); + let set = format!("{{{}}}", &literal[1..literal.len() - 1]); + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(set, right.range()))); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap index f7c702b5ba37d..64b9b62a8dcbe 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap @@ -3,61 +3,67 @@ source: crates/ruff_linter/src/rules/pylint/mod.rs --- set_membership.py:2:6: PLR6201 [*] Use a `set` when checking for element membership | -1 | # PLR6201 +1 | # Errors 2 | 1 in [1, 2, 3] | ^^^^^^^^^ PLR6201 -3 | -4 | # PLR6201 +3 | 1 in (1, 2, 3) +4 | 1 in ( | = help: Use a `set` when checking for element membership ℹ Fix -1 1 | # PLR6201 +1 1 | # Errors 2 |-1 in [1, 2, 3] 2 |+1 in {1, 2, 3} -3 3 | -4 4 | # PLR6201 -5 5 | 1 in (1, 2, 3) +3 3 | 1 in (1, 2, 3) +4 4 | 1 in ( +5 5 | 1, 2, 3 -set_membership.py:5:6: PLR6201 [*] Use a `set` when checking for element membership +set_membership.py:3:6: PLR6201 [*] Use a `set` when checking for element membership | -4 | # PLR6201 -5 | 1 in (1, 2, 3) +1 | # Errors +2 | 1 in [1, 2, 3] +3 | 1 in (1, 2, 3) | ^^^^^^^^^ PLR6201 -6 | -7 | # PLR6201 +4 | 1 in ( +5 | 1, 2, 3 | = help: Use a `set` when checking for element membership ℹ Fix +1 1 | # Errors 2 2 | 1 in [1, 2, 3] -3 3 | -4 4 | # PLR6201 -5 |-1 in (1, 2, 3) - 5 |+1 in {1, 2, 3} -6 6 | -7 7 | # PLR6201 -8 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: +3 |-1 in (1, 2, 3) + 3 |+1 in {1, 2, 3} +4 4 | 1 in ( +5 5 | 1, 2, 3 +6 6 | ) -set_membership.py:9:21: PLR6201 [*] Use a `set` when checking for element membership - | - 7 | # PLR6201 - 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: - 9 | return fruit in ["cherry", "grapes"] - | ^^^^^^^^^^^^^^^^^^^^ PLR6201 -10 | -11 | # Ok - | - = help: Use a `set` when checking for element membership +set_membership.py:4:6: PLR6201 [*] Use a `set` when checking for element membership + | +2 | 1 in [1, 2, 3] +3 | 1 in (1, 2, 3) +4 | 1 in ( + | ______^ +5 | | 1, 2, 3 +6 | | ) + | |_^ PLR6201 +7 | +8 | # OK + | + = help: Use a `set` when checking for element membership ℹ Fix -6 6 | -7 7 | # PLR6201 -8 8 | def fruit_is_dangerous_for_cat(fruit: str) -> bool: -9 |- return fruit in ["cherry", "grapes"] - 9 |+ return fruit in {"cherry", "grapes"} -10 10 | -11 11 | # Ok -12 12 | fruits = ["cherry", "grapes"] +1 1 | # Errors +2 2 | 1 in [1, 2, 3] +3 3 | 1 in (1, 2, 3) +4 |-1 in ( + 4 |+1 in { +5 5 | 1, 2, 3 +6 |-) + 6 |+} +7 7 | +8 8 | # OK +9 9 | fruits = ["cherry", "grapes"] From f7fd510b5056ec4bc2b9fe10a8e3bb8effab20f7 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 17 Oct 2023 00:07:41 -0400 Subject: [PATCH 4/4] Rename rule --- .../{set_membership.py => literal_membership.py} | 0 .../src/checkers/ast/analyze/expression.rs | 4 ++-- crates/ruff_linter/src/codes.rs | 2 +- crates/ruff_linter/src/rules/pylint/mod.rs | 2 +- .../{set_membership.rs => literal_membership.rs} | 12 ++++++------ crates/ruff_linter/src/rules/pylint/rules/mod.rs | 4 ++-- ...ylint__tests__PLR6201_literal_membership.py.snap} | 12 ++++++------ 7 files changed, 18 insertions(+), 18 deletions(-) rename crates/ruff_linter/resources/test/fixtures/pylint/{set_membership.py => literal_membership.py} (100%) rename crates/ruff_linter/src/rules/pylint/rules/{set_membership.rs => literal_membership.rs} (79%) rename crates/ruff_linter/src/rules/pylint/snapshots/{ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap => ruff_linter__rules__pylint__tests__PLR6201_literal_membership.py.snap} (66%) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py b/crates/ruff_linter/resources/test/fixtures/pylint/literal_membership.py similarity index 100% rename from crates/ruff_linter/resources/test/fixtures/pylint/set_membership.py rename to crates/ruff_linter/resources/test/fixtures/pylint/literal_membership.py diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 94f5f29227f9c..a686c2c1b6678 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -1197,8 +1197,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::ComparisonWithItself) { pylint::rules::comparison_with_itself(checker, left, ops, comparators); } - if checker.enabled(Rule::SetMembership) { - pylint::rules::set_membership(checker, compare); + if checker.enabled(Rule::LiteralMembership) { + pylint::rules::literal_membership(checker, compare); } if checker.enabled(Rule::ComparisonOfConstant) { pylint::rules::comparison_of_constant(checker, left, ops, comparators); diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index b822aa7f35003..c67d4e9a6b3e4 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -254,7 +254,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias), (Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison), (Pylint, "R5501") => (RuleGroup::Stable, rules::pylint::rules::CollapsibleElseIf), - (Pylint, "R6201") => (RuleGroup::Preview, rules::pylint::rules::SetMembership), + (Pylint, "R6201") => (RuleGroup::Preview, rules::pylint::rules::LiteralMembership), #[allow(deprecated)] (Pylint, "R6301") => (RuleGroup::Nursery, rules::pylint::rules::NoSelfUse), (Pylint, "W0120") => (RuleGroup::Stable, rules::pylint::rules::UselessElseOnLoop), diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index 969b28f0a401f..cf7d903f7a86e 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -137,7 +137,7 @@ mod tests { #[test_case(Rule::BadDunderMethodName, Path::new("bad_dunder_method_name.py"))] #[test_case(Rule::NoSelfUse, Path::new("no_self_use.py"))] #[test_case(Rule::MisplacedBareRaise, Path::new("misplaced_bare_raise.py"))] - #[test_case(Rule::SetMembership, Path::new("set_membership.py"))] + #[test_case(Rule::LiteralMembership, Path::new("literal_membership.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs b/crates/ruff_linter/src/rules/pylint/rules/literal_membership.rs similarity index 79% rename from crates/ruff_linter/src/rules/pylint/rules/set_membership.rs rename to crates/ruff_linter/src/rules/pylint/rules/literal_membership.rs index be62b756a160f..e69a09c916067 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/set_membership.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/literal_membership.rs @@ -24,21 +24,21 @@ use crate::checkers::ast::Checker; /// ## References /// - [What’s New In Python 3.2](https://docs.python.org/3/whatsnew/3.2.html#optimizations) #[violation] -pub struct SetMembership; +pub struct LiteralMembership; -impl AlwaysFixableViolation for SetMembership { +impl AlwaysFixableViolation for LiteralMembership { #[derive_message_formats] fn message(&self) -> String { - format!("Use a `set` when checking for element membership") + format!("Use a `set` literal when testing for membership") } fn fix_title(&self) -> String { - format!("Use a `set` when checking for element membership") + format!("Convert to `set`") } } /// PLR6201 -pub(crate) fn set_membership(checker: &mut Checker, compare: &ast::ExprCompare) { +pub(crate) fn literal_membership(checker: &mut Checker, compare: &ast::ExprCompare) { let [op] = compare.ops.as_slice() else { return; }; @@ -55,7 +55,7 @@ pub(crate) fn set_membership(checker: &mut Checker, compare: &ast::ExprCompare) return; } - let mut diagnostic = Diagnostic::new(SetMembership, right.range()); + let mut diagnostic = Diagnostic::new(LiteralMembership, right.range()); let literal = checker.locator().slice(right); let set = format!("{{{}}}", &literal[1..literal.len() - 1]); diff --git a/crates/ruff_linter/src/rules/pylint/rules/mod.rs b/crates/ruff_linter/src/rules/pylint/rules/mod.rs index 740783c05b5f8..2d7495ac07909 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/mod.rs @@ -24,6 +24,7 @@ pub(crate) use invalid_envvar_value::*; pub(crate) use invalid_str_return::*; pub(crate) use invalid_string_characters::*; pub(crate) use iteration_over_set::*; +pub(crate) use literal_membership::*; pub(crate) use load_before_global_declaration::*; pub(crate) use logging::*; pub(crate) use magic_value_comparison::*; @@ -39,7 +40,6 @@ pub(crate) use repeated_equality_comparison::*; pub(crate) use repeated_isinstance_calls::*; pub(crate) use return_in_init::*; pub(crate) use self_assigning_variable::*; -pub(crate) use set_membership::*; pub(crate) use single_string_slots::*; pub(crate) use subprocess_popen_preexec_fn::*; pub(crate) use subprocess_run_without_check::*; @@ -87,6 +87,7 @@ mod invalid_envvar_value; mod invalid_str_return; mod invalid_string_characters; mod iteration_over_set; +mod literal_membership; mod load_before_global_declaration; mod logging; mod magic_value_comparison; @@ -102,7 +103,6 @@ mod repeated_equality_comparison; mod repeated_isinstance_calls; mod return_in_init; mod self_assigning_variable; -mod set_membership; mod single_string_slots; mod subprocess_popen_preexec_fn; mod subprocess_run_without_check; diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_literal_membership.py.snap similarity index 66% rename from crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap rename to crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_literal_membership.py.snap index 64b9b62a8dcbe..bb3c795519038 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_set_membership.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6201_literal_membership.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -set_membership.py:2:6: PLR6201 [*] Use a `set` when checking for element membership +literal_membership.py:2:6: PLR6201 [*] Use a `set` literal when testing for membership | 1 | # Errors 2 | 1 in [1, 2, 3] @@ -9,7 +9,7 @@ set_membership.py:2:6: PLR6201 [*] Use a `set` when checking for element members 3 | 1 in (1, 2, 3) 4 | 1 in ( | - = help: Use a `set` when checking for element membership + = help: Convert to `set` ℹ Fix 1 1 | # Errors @@ -19,7 +19,7 @@ set_membership.py:2:6: PLR6201 [*] Use a `set` when checking for element members 4 4 | 1 in ( 5 5 | 1, 2, 3 -set_membership.py:3:6: PLR6201 [*] Use a `set` when checking for element membership +literal_membership.py:3:6: PLR6201 [*] Use a `set` literal when testing for membership | 1 | # Errors 2 | 1 in [1, 2, 3] @@ -28,7 +28,7 @@ set_membership.py:3:6: PLR6201 [*] Use a `set` when checking for element members 4 | 1 in ( 5 | 1, 2, 3 | - = help: Use a `set` when checking for element membership + = help: Convert to `set` ℹ Fix 1 1 | # Errors @@ -39,7 +39,7 @@ set_membership.py:3:6: PLR6201 [*] Use a `set` when checking for element members 5 5 | 1, 2, 3 6 6 | ) -set_membership.py:4:6: PLR6201 [*] Use a `set` when checking for element membership +literal_membership.py:4:6: PLR6201 [*] Use a `set` literal when testing for membership | 2 | 1 in [1, 2, 3] 3 | 1 in (1, 2, 3) @@ -51,7 +51,7 @@ set_membership.py:4:6: PLR6201 [*] Use a `set` when checking for element members 7 | 8 | # OK | - = help: Use a `set` when checking for element membership + = help: Convert to `set` ℹ Fix 1 1 | # Errors