diff --git a/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs b/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs index 2af5085d8991f..1fa2fe15d938b 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs @@ -33,7 +33,22 @@ use crate::checkers::ast::Checker; /// /// for city, population in data.items(): /// print(f"{city} has population {population}.") +/// +/// ## Known problems +/// If the dictionary key is a tuple, e.g.: +/// +/// ```python +/// d = {(1, 2): 3, (3, 4): 5} +/// for x, y in d: +/// print(x, y) /// ``` +/// +/// The tuple key is unpacked into `x` and `y` instead of the key and values. This means that +/// the suggested fix of using `d.items()` would result in different runtime behavior. Ruff +/// cannot consistently infer the type of a dictionary's keys. +/// +/// ## Fix safety +/// Due to the known problem with tuple keys, this fix is unsafe. #[violation] pub struct DictIterMissingItems; @@ -48,6 +63,7 @@ impl AlwaysFixableViolation for DictIterMissingItems { } } +/// PLE1141 pub(crate) fn dict_iter_missing_items(checker: &mut Checker, target: &Expr, iter: &Expr) { let Expr::Tuple(tuple) = target else { return; @@ -78,7 +94,7 @@ pub(crate) fn dict_iter_missing_items(checker: &mut Checker, target: &Expr, iter } let mut diagnostic = Diagnostic::new(DictIterMissingItems, iter.range()); - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( format!("{}.items()", name.id), iter.range(), ))); diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1141_dict_iter_missing_items.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1141_dict_iter_missing_items.py.snap index 34684ce92dac2..db90e1a686e01 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1141_dict_iter_missing_items.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1141_dict_iter_missing_items.py.snap @@ -10,7 +10,7 @@ dict_iter_missing_items.py:13:13: PLE1141 [*] Unpacking a dictionary in iteratio | = help: Add a call to `.items()` -ℹ Safe fix +ℹ Unsafe fix 10 10 | s2 = {1, 2, 3} 11 11 | 12 12 | # Errors @@ -30,7 +30,7 @@ dict_iter_missing_items.py:16:13: PLE1141 [*] Unpacking a dictionary in iteratio | = help: Add a call to `.items()` -ℹ Safe fix +ℹ Unsafe fix 13 13 | for k, v in d: 14 14 | pass 15 15 | @@ -38,6 +38,4 @@ dict_iter_missing_items.py:16:13: PLE1141 [*] Unpacking a dictionary in iteratio 16 |+for k, v in d_tuple_incorrect_tuple.items(): 17 17 | pass 18 18 | -19 19 | - - +19 19 |