Skip to content

Commit 368554a

Browse files
committed
Detect items that hash to same value in duplicate sets
1 parent b37c449 commit 368554a

3 files changed

Lines changed: 38 additions & 9 deletions

File tree

crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B033.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
1,
2020
1,
2121
}
22+
incorrect_set = {False, 1, 0}
2223

2324
###
2425
# Non-errors.

crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_value.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use anyhow::{Context, Result};
2-
use rustc_hash::FxHashSet;
2+
use rustc_hash::{FxHashMap, FxHashSet};
33

44
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
55
use ruff_macros::{derive_message_formats, violation};
66
use ruff_python_ast as ast;
77
use ruff_python_ast::comparable::ComparableExpr;
8+
use ruff_python_ast::hashable::HashableExpr;
9+
use ruff_python_ast::Expr;
810
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
911
use ruff_text_size::Ranged;
1012

@@ -30,15 +32,22 @@ use crate::checkers::ast::Checker;
3032
#[violation]
3133
pub struct DuplicateValue {
3234
value: String,
35+
existing: String,
3336
}
3437

3538
impl Violation for DuplicateValue {
3639
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
3740

3841
#[derive_message_formats]
3942
fn message(&self) -> String {
40-
let DuplicateValue { value } = self;
41-
format!("Sets should not contain duplicate item `{value}`")
43+
let DuplicateValue { value, existing } = self;
44+
if value == existing {
45+
format!("Sets should not contain duplicate item `{value}`")
46+
} else {
47+
format!(
48+
"Sets should not contain duplicate items, but `{existing}` and `{value}` has the same value"
49+
)
50+
}
4251
}
4352

4453
fn fix_title(&self) -> Option<String> {
@@ -48,15 +57,14 @@ impl Violation for DuplicateValue {
4857

4958
/// B033
5059
pub(crate) fn duplicate_value(checker: &mut Checker, set: &ast::ExprSet) {
51-
let mut seen_values: FxHashSet<ComparableExpr> = FxHashSet::default();
60+
let mut seen_values: FxHashMap<HashableExpr, &Expr> = FxHashMap::default();
5261
for (index, value) in set.iter().enumerate() {
5362
if value.is_literal_expr() {
54-
let comparable_value = ComparableExpr::from(value);
55-
56-
if !seen_values.insert(comparable_value) {
63+
if let Some(existing) = seen_values.insert(HashableExpr::from(value), value) {
5764
let mut diagnostic = Diagnostic::new(
5865
DuplicateValue {
5966
value: checker.generator().expr(value),
67+
existing: checker.generator().expr(existing),
6068
},
6169
value.range(),
6270
);

crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B033_B033.py.snap

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ B033.py:20:5: B033 [*] Sets should not contain duplicate item `1`
154154
20 | 1,
155155
| ^ B033
156156
21 | }
157+
22 | incorrect_set = {False, 1, 0}
157158
|
158159
= help: Remove duplicate item
159160

@@ -163,7 +164,26 @@ B033.py:20:5: B033 [*] Sets should not contain duplicate item `1`
163164
19 19 | 1,
164165
20 |- 1,
165166
21 20 | }
166-
22 21 |
167-
23 22 | ###
167+
22 21 | incorrect_set = {False, 1, 0}
168+
23 22 |
168169

170+
B033.py:22:28: B033 [*] Sets should not contain duplicate items, but `False` and `0` has the same value
171+
|
172+
20 | 1,
173+
21 | }
174+
22 | incorrect_set = {False, 1, 0}
175+
| ^ B033
176+
23 |
177+
24 | ###
178+
|
179+
= help: Remove duplicate item
169180

181+
Safe fix
182+
19 19 | 1,
183+
20 20 | 1,
184+
21 21 | }
185+
22 |-incorrect_set = {False, 1, 0}
186+
22 |+incorrect_set = {False, 1}
187+
23 23 |
188+
24 24 | ###
189+
25 25 | # Non-errors.

0 commit comments

Comments
 (0)