diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM118.py b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM118.py index 27331ee559b4a..945d6968a3fd2 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM118.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM118.py @@ -1,3 +1,5 @@ +obj = {} + key in obj.keys() # SIM118 key not in obj.keys() # SIM118 diff --git a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs index fe3eaecb8ddd3..ec71d7001d718 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs @@ -55,6 +55,7 @@ mod tests { Ok(()) } + #[test_case(Rule::InDictKeys, Path::new("SIM118.py"))] #[test_case(Rule::IfElseBlockInsteadOfDictGet, Path::new("SIM401.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs index 36592a3874cdf..0811226c5c04b 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs @@ -1,9 +1,10 @@ -use ruff_diagnostics::Edit; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix}; +use ruff_diagnostics::{Applicability, Edit}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::AnyNodeRef; use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr}; +use ruff_python_semantic::analyze::typing; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; use ruff_text_size::{Ranged, TextRange}; @@ -27,8 +28,19 @@ use crate::checkers::ast::Checker; /// key in foo /// ``` /// +/// ## Fix safety +/// Given `key in obj.keys()`, `obj` _could_ be a dictionary, or it could be +/// another type that defines a `.keys()` method. In the latter case, removing +/// the `.keys()` attribute could lead to a runtime error. +/// +/// As such, this rule's fixes are marked as unsafe. In [preview], though, +/// fixes are marked as safe when Ruff can determine that `obj` is a +/// dictionary. +/// /// ## References /// - [Python documentation: Mapping Types](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) +/// +/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct InDictKeys { operator: String, @@ -113,6 +125,28 @@ fn key_in_dict( .skip_trivia() .find(|token| token.kind == SimpleTokenKind::Dot) { + // The fix is only safe if we know the expression is a dictionary, since other types + // can define a `.keys()` method. + let applicability = if checker.settings.preview.is_enabled() { + let is_dict = value.as_name_expr().is_some_and(|name| { + let Some(binding) = checker + .semantic() + .only_binding(name) + .map(|id| checker.semantic().binding(id)) + else { + return false; + }; + typing::is_dict(binding, checker.semantic()) + }); + if is_dict { + Applicability::Safe + } else { + Applicability::Unsafe + } + } else { + Applicability::Unsafe + }; + // If the `.keys()` is followed by (e.g.) a keyword, we need to insert a space, // since we're removing parentheses, which could lead to invalid syntax, as in: // ```python @@ -126,12 +160,15 @@ fn key_in_dict( .next() .is_some_and(|char| char.is_ascii_alphabetic()) { - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( - " ".to_string(), - range, - ))); + diagnostic.set_fix(Fix::applicable_edit( + Edit::range_replacement(" ".to_string(), range), + applicability, + )); } else { - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(range))); + diagnostic.set_fix(Fix::applicable_edit( + Edit::range_deletion(range), + applicability, + )); } } checker.diagnostics.push(diagnostic); diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap index a95f802966289..98a7b274bc9b4 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap @@ -1,396 +1,401 @@ --- source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs --- -SIM118.py:1:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:3:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -1 | key in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^ SIM118 -2 | -3 | key not in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Suggested fix -1 |-key in obj.keys() # SIM118 - 1 |+key in obj # SIM118 -2 2 | -3 3 | key not in obj.keys() # SIM118 -4 4 | - -SIM118.py:3:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` - | -1 | key in obj.keys() # SIM118 +1 | obj = {} 2 | -3 | key not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^ SIM118 +3 | key in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^ SIM118 4 | -5 | foo["bar"] in obj.keys() # SIM118 +5 | key not in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix -1 1 | key in obj.keys() # SIM118 +1 1 | obj = {} 2 2 | -3 |-key not in obj.keys() # SIM118 - 3 |+key not in obj # SIM118 +3 |-key in obj.keys() # SIM118 + 3 |+key in obj # SIM118 4 4 | -5 5 | foo["bar"] in obj.keys() # SIM118 +5 5 | key not in obj.keys() # SIM118 6 6 | -SIM118.py:5:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:5:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` | -3 | key not in obj.keys() # SIM118 +3 | key in obj.keys() # SIM118 4 | -5 | foo["bar"] in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +5 | key not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^ SIM118 6 | -7 | foo["bar"] not in obj.keys() # SIM118 +7 | foo["bar"] in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 2 2 | -3 3 | key not in obj.keys() # SIM118 +3 3 | key in obj.keys() # SIM118 4 4 | -5 |-foo["bar"] in obj.keys() # SIM118 - 5 |+foo["bar"] in obj # SIM118 +5 |-key not in obj.keys() # SIM118 + 5 |+key not in obj # SIM118 6 6 | -7 7 | foo["bar"] not in obj.keys() # SIM118 +7 7 | foo["bar"] in obj.keys() # SIM118 8 8 | -SIM118.py:7:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` +SIM118.py:7:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -5 | foo["bar"] in obj.keys() # SIM118 +5 | key not in obj.keys() # SIM118 6 | -7 | foo["bar"] not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +7 | foo["bar"] in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 8 | -9 | foo['bar'] in obj.keys() # SIM118 +9 | foo["bar"] not in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 4 4 | -5 5 | foo["bar"] in obj.keys() # SIM118 +5 5 | key not in obj.keys() # SIM118 6 6 | -7 |-foo["bar"] not in obj.keys() # SIM118 - 7 |+foo["bar"] not in obj # SIM118 +7 |-foo["bar"] in obj.keys() # SIM118 + 7 |+foo["bar"] in obj # SIM118 8 8 | -9 9 | foo['bar'] in obj.keys() # SIM118 +9 9 | foo["bar"] not in obj.keys() # SIM118 10 10 | -SIM118.py:9:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:9:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` | - 7 | foo["bar"] not in obj.keys() # SIM118 + 7 | foo["bar"] in obj.keys() # SIM118 8 | - 9 | foo['bar'] in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 + 9 | foo["bar"] not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 10 | -11 | foo['bar'] not in obj.keys() # SIM118 +11 | foo['bar'] in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 6 6 | -7 7 | foo["bar"] not in obj.keys() # SIM118 +7 7 | foo["bar"] in obj.keys() # SIM118 8 8 | -9 |-foo['bar'] in obj.keys() # SIM118 - 9 |+foo['bar'] in obj # SIM118 +9 |-foo["bar"] not in obj.keys() # SIM118 + 9 |+foo["bar"] not in obj # SIM118 10 10 | -11 11 | foo['bar'] not in obj.keys() # SIM118 +11 11 | foo['bar'] in obj.keys() # SIM118 12 12 | -SIM118.py:11:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` +SIM118.py:11:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | - 9 | foo['bar'] in obj.keys() # SIM118 + 9 | foo["bar"] not in obj.keys() # SIM118 10 | -11 | foo['bar'] not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +11 | foo['bar'] in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 12 | -13 | foo() in obj.keys() # SIM118 +13 | foo['bar'] not in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 8 8 | -9 9 | foo['bar'] in obj.keys() # SIM118 +9 9 | foo["bar"] not in obj.keys() # SIM118 10 10 | -11 |-foo['bar'] not in obj.keys() # SIM118 - 11 |+foo['bar'] not in obj # SIM118 +11 |-foo['bar'] in obj.keys() # SIM118 + 11 |+foo['bar'] in obj # SIM118 12 12 | -13 13 | foo() in obj.keys() # SIM118 +13 13 | foo['bar'] not in obj.keys() # SIM118 14 14 | -SIM118.py:13:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:13:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` | -11 | foo['bar'] not in obj.keys() # SIM118 +11 | foo['bar'] in obj.keys() # SIM118 12 | -13 | foo() in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^ SIM118 +13 | foo['bar'] not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 14 | -15 | foo() not in obj.keys() # SIM118 +15 | foo() in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 10 10 | -11 11 | foo['bar'] not in obj.keys() # SIM118 +11 11 | foo['bar'] in obj.keys() # SIM118 12 12 | -13 |-foo() in obj.keys() # SIM118 - 13 |+foo() in obj # SIM118 +13 |-foo['bar'] not in obj.keys() # SIM118 + 13 |+foo['bar'] not in obj # SIM118 14 14 | -15 15 | foo() not in obj.keys() # SIM118 +15 15 | foo() in obj.keys() # SIM118 16 16 | -SIM118.py:15:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` +SIM118.py:15:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -13 | foo() in obj.keys() # SIM118 +13 | foo['bar'] not in obj.keys() # SIM118 14 | -15 | foo() not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +15 | foo() in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^ SIM118 16 | -17 | for key in obj.keys(): # SIM118 +17 | foo() not in obj.keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 12 12 | -13 13 | foo() in obj.keys() # SIM118 +13 13 | foo['bar'] not in obj.keys() # SIM118 14 14 | -15 |-foo() not in obj.keys() # SIM118 - 15 |+foo() not in obj # SIM118 +15 |-foo() in obj.keys() # SIM118 + 15 |+foo() in obj # SIM118 16 16 | -17 17 | for key in obj.keys(): # SIM118 -18 18 | pass +17 17 | foo() not in obj.keys() # SIM118 +18 18 | -SIM118.py:17:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:17:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` | -15 | foo() not in obj.keys() # SIM118 +15 | foo() in obj.keys() # SIM118 16 | -17 | for key in obj.keys(): # SIM118 - | ^^^^^^^^^^^^^^^^^ SIM118 -18 | pass +17 | foo() not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +18 | +19 | for key in obj.keys(): # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 14 14 | -15 15 | foo() not in obj.keys() # SIM118 +15 15 | foo() in obj.keys() # SIM118 16 16 | -17 |-for key in obj.keys(): # SIM118 - 17 |+for key in obj: # SIM118 -18 18 | pass -19 19 | -20 20 | for key in list(obj.keys()): +17 |-foo() not in obj.keys() # SIM118 + 17 |+foo() not in obj # SIM118 +18 18 | +19 19 | for key in obj.keys(): # SIM118 +20 20 | pass -SIM118.py:24:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:19:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -22 | del obj[key] -23 | -24 | [k for k in obj.keys()] # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 -25 | -26 | {k for k in obj.keys()} # SIM118 +17 | foo() not in obj.keys() # SIM118 +18 | +19 | for key in obj.keys(): # SIM118 + | ^^^^^^^^^^^^^^^^^ SIM118 +20 | pass | = help: Remove `.keys()` ℹ Suggested fix -21 21 | if some_property(key): -22 22 | del obj[key] -23 23 | -24 |-[k for k in obj.keys()] # SIM118 - 24 |+[k for k in obj] # SIM118 -25 25 | -26 26 | {k for k in obj.keys()} # SIM118 -27 27 | +16 16 | +17 17 | foo() not in obj.keys() # SIM118 +18 18 | +19 |-for key in obj.keys(): # SIM118 + 19 |+for key in obj: # SIM118 +20 20 | pass +21 21 | +22 22 | for key in list(obj.keys()): SIM118.py:26:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -24 | [k for k in obj.keys()] # SIM118 +24 | del obj[key] 25 | -26 | {k for k in obj.keys()} # SIM118 +26 | [k for k in obj.keys()] # SIM118 | ^^^^^^^^^^^^^^^ SIM118 27 | -28 | {k: k for k in obj.keys()} # SIM118 +28 | {k for k in obj.keys()} # SIM118 | = help: Remove `.keys()` ℹ Suggested fix -23 23 | -24 24 | [k for k in obj.keys()] # SIM118 +23 23 | if some_property(key): +24 24 | del obj[key] 25 25 | -26 |-{k for k in obj.keys()} # SIM118 - 26 |+{k for k in obj} # SIM118 +26 |-[k for k in obj.keys()] # SIM118 + 26 |+[k for k in obj] # SIM118 27 27 | -28 28 | {k: k for k in obj.keys()} # SIM118 +28 28 | {k for k in obj.keys()} # SIM118 29 29 | -SIM118.py:28:11: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:28:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -26 | {k for k in obj.keys()} # SIM118 +26 | [k for k in obj.keys()] # SIM118 27 | -28 | {k: k for k in obj.keys()} # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 +28 | {k for k in obj.keys()} # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 29 | -30 | (k for k in obj.keys()) # SIM118 +30 | {k: k for k in obj.keys()} # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 25 25 | -26 26 | {k for k in obj.keys()} # SIM118 +26 26 | [k for k in obj.keys()] # SIM118 27 27 | -28 |-{k: k for k in obj.keys()} # SIM118 - 28 |+{k: k for k in obj} # SIM118 +28 |-{k for k in obj.keys()} # SIM118 + 28 |+{k for k in obj} # SIM118 29 29 | -30 30 | (k for k in obj.keys()) # SIM118 +30 30 | {k: k for k in obj.keys()} # SIM118 31 31 | -SIM118.py:30:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:30:11: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -28 | {k: k for k in obj.keys()} # SIM118 +28 | {k for k in obj.keys()} # SIM118 29 | -30 | (k for k in obj.keys()) # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 +30 | {k: k for k in obj.keys()} # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 31 | -32 | key in (obj or {}).keys() # SIM118 +32 | (k for k in obj.keys()) # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 27 27 | -28 28 | {k: k for k in obj.keys()} # SIM118 +28 28 | {k for k in obj.keys()} # SIM118 29 29 | -30 |-(k for k in obj.keys()) # SIM118 - 30 |+(k for k in obj) # SIM118 +30 |-{k: k for k in obj.keys()} # SIM118 + 30 |+{k: k for k in obj} # SIM118 31 31 | -32 32 | key in (obj or {}).keys() # SIM118 +32 32 | (k for k in obj.keys()) # SIM118 33 33 | -SIM118.py:32:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:32:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -30 | (k for k in obj.keys()) # SIM118 +30 | {k: k for k in obj.keys()} # SIM118 31 | -32 | key in (obj or {}).keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +32 | (k for k in obj.keys()) # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 33 | -34 | (key) in (obj or {}).keys() # SIM118 +34 | key in (obj or {}).keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 29 29 | -30 30 | (k for k in obj.keys()) # SIM118 +30 30 | {k: k for k in obj.keys()} # SIM118 31 31 | -32 |-key in (obj or {}).keys() # SIM118 - 32 |+key in (obj or {}) # SIM118 +32 |-(k for k in obj.keys()) # SIM118 + 32 |+(k for k in obj) # SIM118 33 33 | -34 34 | (key) in (obj or {}).keys() # SIM118 +34 34 | key in (obj or {}).keys() # SIM118 35 35 | SIM118.py:34:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -32 | key in (obj or {}).keys() # SIM118 +32 | (k for k in obj.keys()) # SIM118 33 | -34 | (key) in (obj or {}).keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +34 | key in (obj or {}).keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 35 | -36 | from typing import KeysView +36 | (key) in (obj or {}).keys() # SIM118 | = help: Remove `.keys()` ℹ Suggested fix 31 31 | -32 32 | key in (obj or {}).keys() # SIM118 +32 32 | (k for k in obj.keys()) # SIM118 33 33 | -34 |-(key) in (obj or {}).keys() # SIM118 - 34 |+(key) in (obj or {}) # SIM118 +34 |-key in (obj or {}).keys() # SIM118 + 34 |+key in (obj or {}) # SIM118 35 35 | -36 36 | from typing import KeysView +36 36 | (key) in (obj or {}).keys() # SIM118 37 37 | -SIM118.py:48:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +SIM118.py:36:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +34 | key in (obj or {}).keys() # SIM118 +35 | +36 | (key) in (obj or {}).keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +37 | +38 | from typing import KeysView | -47 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -48 | key in obj.keys()and foo + = help: Remove `.keys()` + +ℹ Suggested fix +33 33 | +34 34 | key in (obj or {}).keys() # SIM118 +35 35 | +36 |-(key) in (obj or {}).keys() # SIM118 + 36 |+(key) in (obj or {}) # SIM118 +37 37 | +38 38 | from typing import KeysView +39 39 | + +SIM118.py:50:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 | key in obj.keys()and foo | ^^^^^^^^^^^^^^^^^ SIM118 -49 | (key in obj.keys())and foo -50 | key in (obj.keys())and foo +51 | (key in obj.keys())and foo +52 | key in (obj.keys())and foo | = help: Remove `.keys()` ℹ Suggested fix -45 45 | -46 46 | -47 47 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -48 |-key in obj.keys()and foo - 48 |+key in obj and foo -49 49 | (key in obj.keys())and foo -50 50 | key in (obj.keys())and foo -51 51 | - -SIM118.py:49:2: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -47 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -48 | key in obj.keys()and foo -49 | (key in obj.keys())and foo +47 47 | +48 48 | +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 |-key in obj.keys()and foo + 50 |+key in obj and foo +51 51 | (key in obj.keys())and foo +52 52 | key in (obj.keys())and foo +53 53 | + +SIM118.py:51:2: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 | key in obj.keys()and foo +51 | (key in obj.keys())and foo | ^^^^^^^^^^^^^^^^^ SIM118 -50 | key in (obj.keys())and foo +52 | key in (obj.keys())and foo | = help: Remove `.keys()` ℹ Suggested fix -46 46 | -47 47 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -48 48 | key in obj.keys()and foo -49 |-(key in obj.keys())and foo - 49 |+(key in obj)and foo -50 50 | key in (obj.keys())and foo -51 51 | -52 52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 - -SIM118.py:50:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` +48 48 | +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 50 | key in obj.keys()and foo +51 |-(key in obj.keys())and foo + 51 |+(key in obj)and foo +52 52 | key in (obj.keys())and foo +53 53 | +54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 + +SIM118.py:52:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | -48 | key in obj.keys()and foo -49 | (key in obj.keys())and foo -50 | key in (obj.keys())and foo +50 | key in obj.keys()and foo +51 | (key in obj.keys())and foo +52 | key in (obj.keys())and foo | ^^^^^^^^^^^^^^^^^^^ SIM118 -51 | -52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 +53 | +54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 | = help: Remove `.keys()` ℹ Suggested fix -47 47 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -48 48 | key in obj.keys()and foo -49 49 | (key in obj.keys())and foo -50 |-key in (obj.keys())and foo - 50 |+key in (obj)and foo -51 51 | -52 52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 -53 53 | for key in ( - -SIM118.py:53:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 -53 | for key in ( +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 50 | key in obj.keys()and foo +51 51 | (key in obj.keys())and foo +52 |-key in (obj.keys())and foo + 52 |+key in (obj)and foo +53 53 | +54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 +55 55 | for key in ( + +SIM118.py:55:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 +55 | for key in ( | _____^ -54 | | self.experiment.surveys[0] -55 | | .stations[0] -56 | | .keys() -57 | | ): +56 | | self.experiment.surveys[0] +57 | | .stations[0] +58 | | .keys() +59 | | ): | |_^ SIM118 -58 | continue +60 | continue | = help: Remove `.keys()` ℹ Suggested fix -53 53 | for key in ( -54 54 | self.experiment.surveys[0] -55 55 | .stations[0] -56 |- .keys() - 56 |+ -57 57 | ): -58 58 | continue +55 55 | for key in ( +56 56 | self.experiment.surveys[0] +57 57 | .stations[0] +58 |- .keys() + 58 |+ +59 59 | ): +60 60 | continue diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap new file mode 100644 index 0000000000000..eb112d410c084 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap @@ -0,0 +1,401 @@ +--- +source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs +--- +SIM118.py:3:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +1 | obj = {} +2 | +3 | key in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^ SIM118 +4 | +5 | key not in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +1 1 | obj = {} +2 2 | +3 |-key in obj.keys() # SIM118 + 3 |+key in obj # SIM118 +4 4 | +5 5 | key not in obj.keys() # SIM118 +6 6 | + +SIM118.py:5:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` + | +3 | key in obj.keys() # SIM118 +4 | +5 | key not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^ SIM118 +6 | +7 | foo["bar"] in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +2 2 | +3 3 | key in obj.keys() # SIM118 +4 4 | +5 |-key not in obj.keys() # SIM118 + 5 |+key not in obj # SIM118 +6 6 | +7 7 | foo["bar"] in obj.keys() # SIM118 +8 8 | + +SIM118.py:7:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +5 | key not in obj.keys() # SIM118 +6 | +7 | foo["bar"] in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +8 | +9 | foo["bar"] not in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +4 4 | +5 5 | key not in obj.keys() # SIM118 +6 6 | +7 |-foo["bar"] in obj.keys() # SIM118 + 7 |+foo["bar"] in obj # SIM118 +8 8 | +9 9 | foo["bar"] not in obj.keys() # SIM118 +10 10 | + +SIM118.py:9:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` + | + 7 | foo["bar"] in obj.keys() # SIM118 + 8 | + 9 | foo["bar"] not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +10 | +11 | foo['bar'] in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +6 6 | +7 7 | foo["bar"] in obj.keys() # SIM118 +8 8 | +9 |-foo["bar"] not in obj.keys() # SIM118 + 9 |+foo["bar"] not in obj # SIM118 +10 10 | +11 11 | foo['bar'] in obj.keys() # SIM118 +12 12 | + +SIM118.py:11:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | + 9 | foo["bar"] not in obj.keys() # SIM118 +10 | +11 | foo['bar'] in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +12 | +13 | foo['bar'] not in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +8 8 | +9 9 | foo["bar"] not in obj.keys() # SIM118 +10 10 | +11 |-foo['bar'] in obj.keys() # SIM118 + 11 |+foo['bar'] in obj # SIM118 +12 12 | +13 13 | foo['bar'] not in obj.keys() # SIM118 +14 14 | + +SIM118.py:13:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` + | +11 | foo['bar'] in obj.keys() # SIM118 +12 | +13 | foo['bar'] not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +14 | +15 | foo() in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +10 10 | +11 11 | foo['bar'] in obj.keys() # SIM118 +12 12 | +13 |-foo['bar'] not in obj.keys() # SIM118 + 13 |+foo['bar'] not in obj # SIM118 +14 14 | +15 15 | foo() in obj.keys() # SIM118 +16 16 | + +SIM118.py:15:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +13 | foo['bar'] not in obj.keys() # SIM118 +14 | +15 | foo() in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^ SIM118 +16 | +17 | foo() not in obj.keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +12 12 | +13 13 | foo['bar'] not in obj.keys() # SIM118 +14 14 | +15 |-foo() in obj.keys() # SIM118 + 15 |+foo() in obj # SIM118 +16 16 | +17 17 | foo() not in obj.keys() # SIM118 +18 18 | + +SIM118.py:17:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` + | +15 | foo() in obj.keys() # SIM118 +16 | +17 | foo() not in obj.keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +18 | +19 | for key in obj.keys(): # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +14 14 | +15 15 | foo() in obj.keys() # SIM118 +16 16 | +17 |-foo() not in obj.keys() # SIM118 + 17 |+foo() not in obj # SIM118 +18 18 | +19 19 | for key in obj.keys(): # SIM118 +20 20 | pass + +SIM118.py:19:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +17 | foo() not in obj.keys() # SIM118 +18 | +19 | for key in obj.keys(): # SIM118 + | ^^^^^^^^^^^^^^^^^ SIM118 +20 | pass + | + = help: Remove `.keys()` + +ℹ Fix +16 16 | +17 17 | foo() not in obj.keys() # SIM118 +18 18 | +19 |-for key in obj.keys(): # SIM118 + 19 |+for key in obj: # SIM118 +20 20 | pass +21 21 | +22 22 | for key in list(obj.keys()): + +SIM118.py:26:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +24 | del obj[key] +25 | +26 | [k for k in obj.keys()] # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 +27 | +28 | {k for k in obj.keys()} # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +23 23 | if some_property(key): +24 24 | del obj[key] +25 25 | +26 |-[k for k in obj.keys()] # SIM118 + 26 |+[k for k in obj] # SIM118 +27 27 | +28 28 | {k for k in obj.keys()} # SIM118 +29 29 | + +SIM118.py:28:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +26 | [k for k in obj.keys()] # SIM118 +27 | +28 | {k for k in obj.keys()} # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 +29 | +30 | {k: k for k in obj.keys()} # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +25 25 | +26 26 | [k for k in obj.keys()] # SIM118 +27 27 | +28 |-{k for k in obj.keys()} # SIM118 + 28 |+{k for k in obj} # SIM118 +29 29 | +30 30 | {k: k for k in obj.keys()} # SIM118 +31 31 | + +SIM118.py:30:11: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +28 | {k for k in obj.keys()} # SIM118 +29 | +30 | {k: k for k in obj.keys()} # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 +31 | +32 | (k for k in obj.keys()) # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +27 27 | +28 28 | {k for k in obj.keys()} # SIM118 +29 29 | +30 |-{k: k for k in obj.keys()} # SIM118 + 30 |+{k: k for k in obj} # SIM118 +31 31 | +32 32 | (k for k in obj.keys()) # SIM118 +33 33 | + +SIM118.py:32:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +30 | {k: k for k in obj.keys()} # SIM118 +31 | +32 | (k for k in obj.keys()) # SIM118 + | ^^^^^^^^^^^^^^^ SIM118 +33 | +34 | key in (obj or {}).keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Fix +29 29 | +30 30 | {k: k for k in obj.keys()} # SIM118 +31 31 | +32 |-(k for k in obj.keys()) # SIM118 + 32 |+(k for k in obj) # SIM118 +33 33 | +34 34 | key in (obj or {}).keys() # SIM118 +35 35 | + +SIM118.py:34:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +32 | (k for k in obj.keys()) # SIM118 +33 | +34 | key in (obj or {}).keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +35 | +36 | (key) in (obj or {}).keys() # SIM118 + | + = help: Remove `.keys()` + +ℹ Suggested fix +31 31 | +32 32 | (k for k in obj.keys()) # SIM118 +33 33 | +34 |-key in (obj or {}).keys() # SIM118 + 34 |+key in (obj or {}) # SIM118 +35 35 | +36 36 | (key) in (obj or {}).keys() # SIM118 +37 37 | + +SIM118.py:36:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +34 | key in (obj or {}).keys() # SIM118 +35 | +36 | (key) in (obj or {}).keys() # SIM118 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 +37 | +38 | from typing import KeysView + | + = help: Remove `.keys()` + +ℹ Suggested fix +33 33 | +34 34 | key in (obj or {}).keys() # SIM118 +35 35 | +36 |-(key) in (obj or {}).keys() # SIM118 + 36 |+(key) in (obj or {}) # SIM118 +37 37 | +38 38 | from typing import KeysView +39 39 | + +SIM118.py:50:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 | key in obj.keys()and foo + | ^^^^^^^^^^^^^^^^^ SIM118 +51 | (key in obj.keys())and foo +52 | key in (obj.keys())and foo + | + = help: Remove `.keys()` + +ℹ Fix +47 47 | +48 48 | +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 |-key in obj.keys()and foo + 50 |+key in obj and foo +51 51 | (key in obj.keys())and foo +52 52 | key in (obj.keys())and foo +53 53 | + +SIM118.py:51:2: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 | key in obj.keys()and foo +51 | (key in obj.keys())and foo + | ^^^^^^^^^^^^^^^^^ SIM118 +52 | key in (obj.keys())and foo + | + = help: Remove `.keys()` + +ℹ Fix +48 48 | +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 50 | key in obj.keys()and foo +51 |-(key in obj.keys())and foo + 51 |+(key in obj)and foo +52 52 | key in (obj.keys())and foo +53 53 | +54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 + +SIM118.py:52:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +50 | key in obj.keys()and foo +51 | (key in obj.keys())and foo +52 | key in (obj.keys())and foo + | ^^^^^^^^^^^^^^^^^^^ SIM118 +53 | +54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 + | + = help: Remove `.keys()` + +ℹ Fix +49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 +50 50 | key in obj.keys()and foo +51 51 | (key in obj.keys())and foo +52 |-key in (obj.keys())and foo + 52 |+key in (obj)and foo +53 53 | +54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 +55 55 | for key in ( + +SIM118.py:55:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` + | +54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 +55 | for key in ( + | _____^ +56 | | self.experiment.surveys[0] +57 | | .stations[0] +58 | | .keys() +59 | | ): + | |_^ SIM118 +60 | continue + | + = help: Remove `.keys()` + +ℹ Suggested fix +55 55 | for key in ( +56 56 | self.experiment.surveys[0] +57 57 | .stations[0] +58 |- .keys() + 58 |+ +59 59 | ): +60 60 | continue + +