[pylint] - restrict iteration-over-set to only work on sets of literals (PLC0208) (#13731)

This commit is contained in:
Steve C 2024-10-21 07:14:02 -04:00 committed by GitHub
parent c2dc502f3b
commit f3612c2717
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 3 deletions

View file

@ -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}.")

View file

@ -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() {