diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/iteration_over_set.py b/crates/ruff_linter/resources/test/fixtures/pylint/iteration_over_set.py index 38a20dcc1e..f8d32e59a5 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/iteration_over_set.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/iteration_over_set.py @@ -50,3 +50,13 @@ for number in {i for i in range(10)}: # set comprehensions are fine for item in {*numbers_set, 4, 5, 6}: # set unpacking is fine print(f"I like {item}.") + +for item in {1, 2, 3, 4, 5, 6, 2 // 1}: # operations in set literals are fine + print(f"I like {item}.") + +for item in {1, 2, 3, 4, 5, 6, int("7")}: # calls in set literals are fine + print(f"I like {item}.") + +for item in {1, 2, 2}: # duplicate literals will be ignored + # B033 catches this + print(f"I like {item}.") diff --git a/crates/ruff_linter/src/rules/pylint/rules/iteration_over_set.rs b/crates/ruff_linter/src/rules/pylint/rules/iteration_over_set.rs index 82023c5ad8..0abc2d89b0 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/iteration_over_set.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/iteration_over_set.rs @@ -1,12 +1,14 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::Expr; +use ruff_python_ast::{comparable::ComparableExpr, Expr}; use ruff_text_size::Ranged; +use rustc_hash::{FxBuildHasher, FxHashSet}; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for iterations over `set` literals. +/// Checks for iteration over a `set` literal where each element in the set is +/// itself a literal value. /// /// ## Why is this bad? /// Iterating over a `set` is less efficient than iterating over a sequence @@ -46,10 +48,20 @@ pub(crate) fn iteration_over_set(checker: &mut Checker, expr: &Expr) { return; }; - if set.iter().any(Expr::is_starred_expr) { + if set.iter().any(|value| !value.is_literal_expr()) { return; } + let mut seen_values = FxHashSet::with_capacity_and_hasher(set.len(), FxBuildHasher); + for value in set { + let comparable_value = ComparableExpr::from(value); + if !seen_values.insert(comparable_value) { + // if the set contains a duplicate literal value, early exit. + // rule `B033` can catch that. + return; + } + } + let mut diagnostic = Diagnostic::new(IterationOverSet, expr.range()); let tuple = if let [elt] = set.elts.as_slice() {