Extend SIM118 with not in (#5995)

Closes https://github.com/astral-sh/ruff/issues/5989

Tracking issue https://github.com/astral-sh/ruff/issues/1348
This commit is contained in:
Simon Brugman
2023-07-23 03:46:21 +02:00
committed by GitHub
parent 6d58b773b1
commit 3914fcb7ca
3 changed files with 242 additions and 130 deletions

View File

@@ -1,11 +1,19 @@
key in obj.keys() # SIM118
key not in obj.keys() # SIM118
foo["bar"] in obj.keys() # SIM118
foo["bar"] not in obj.keys() # SIM118
foo['bar'] in obj.keys() # SIM118
foo['bar'] not in obj.keys() # SIM118
foo() in obj.keys() # SIM118
foo() not in obj.keys() # SIM118
for key in obj.keys(): # SIM118
pass

View File

@@ -37,36 +37,38 @@ use crate::registry::AsRule;
pub struct InDictKeys {
key: String,
dict: String,
operator: String,
}
impl AlwaysAutofixableViolation for InDictKeys {
#[derive_message_formats]
fn message(&self) -> String {
let InDictKeys { key, dict } = self;
format!("Use `{key} in {dict}` instead of `{key} in {dict}.keys()`")
let InDictKeys {
key,
dict,
operator,
} = self;
format!("Use `{key} {operator} {dict}` instead of `{key} {operator} {dict}.keys()`")
}
fn autofix_title(&self) -> String {
let InDictKeys { key, dict } = self;
format!("Convert to `{key} in {dict}`")
let InDictKeys {
key,
dict,
operator,
} = self;
format!("Convert to `{key} {operator} {dict}`")
}
}
fn get_value_content_for_key_in_dict(
locator: &Locator,
stylist: &Stylist,
expr: &Expr,
) -> Result<String> {
let content = locator.slice(expr.range());
let mut expression = match_expression(content)?;
let call = match_call_mut(&mut expression)?;
let attribute = match_attribute(&mut call.func)?;
Ok(attribute.value.codegen_stylist(stylist))
}
/// SIM118
fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: TextRange) {
fn key_in_dict(
checker: &mut Checker,
left: &Expr,
right: &Expr,
operator: CmpOp,
range: TextRange,
) {
let Expr::Call(ast::ExprCall {
func,
args,
@@ -101,7 +103,8 @@ fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: TextRang
let mut diagnostic = Diagnostic::new(
InDictKeys {
key: left_content.to_string(),
dict: value_content.clone(),
dict: value_content.to_string(),
operator: operator.as_str().to_string(),
},
range,
);
@@ -120,6 +123,7 @@ pub(crate) fn key_in_dict_for(checker: &mut Checker, target: &Expr, iter: &Expr)
checker,
target,
iter,
CmpOp::In,
TextRange::new(target.start(), iter.end()),
);
}
@@ -132,14 +136,30 @@ pub(crate) fn key_in_dict_compare(
ops: &[CmpOp],
comparators: &[Expr],
) {
if !matches!(ops[..], [CmpOp::In]) {
let [op] = ops else {
return;
};
if !matches!(op, CmpOp::In | CmpOp::NotIn) {
return;
}
if comparators.len() != 1 {
let [right] = comparators else {
return;
}
let right = comparators.first().unwrap();
};
key_in_dict(checker, left, right, expr.range());
key_in_dict(checker, left, right, *op, expr.range());
}
fn get_value_content_for_key_in_dict(
locator: &Locator,
stylist: &Stylist,
expr: &Expr,
) -> Result<String> {
let content = locator.slice(expr.range());
let mut expression = match_expression(content)?;
let call = match_call_mut(&mut expression)?;
let attribute = match_attribute(&mut call.func)?;
Ok(attribute.value.codegen_stylist(stylist))
}

View File

@@ -6,7 +6,7 @@ SIM118.py:1:1: SIM118 [*] Use `key in obj` instead of `key in obj.keys()`
1 | key in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^ SIM118
2 |
3 | foo["bar"] in obj.keys() # SIM118
3 | key not in obj.keys() # SIM118
|
= help: Convert to `key in obj`
@@ -14,188 +14,272 @@ SIM118.py:1:1: SIM118 [*] Use `key in obj` instead of `key in obj.keys()`
1 |-key in obj.keys() # SIM118
1 |+key in obj # SIM118
2 2 |
3 3 | foo["bar"] in obj.keys() # SIM118
3 3 | key not in obj.keys() # SIM118
4 4 |
SIM118.py:3:1: SIM118 [*] Use `foo["bar"] in obj` instead of `foo["bar"] in obj.keys()`
SIM118.py:3:1: SIM118 [*] Use `key not in obj` instead of `key not in obj.keys()`
|
1 | key in obj.keys() # SIM118
2 |
3 | foo["bar"] in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
3 | key not in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^ SIM118
4 |
5 | foo['bar'] in obj.keys() # SIM118
5 | foo["bar"] in obj.keys() # SIM118
|
= help: Convert to `foo["bar"] in obj`
= help: Convert to `key not in obj`
Suggested fix
1 1 | key in obj.keys() # SIM118
2 2 |
3 |-foo["bar"] in obj.keys() # SIM118
3 |+foo["bar"] in obj # SIM118
3 |-key not in obj.keys() # SIM118
3 |+key not in obj # SIM118
4 4 |
5 5 | foo['bar'] in obj.keys() # SIM118
5 5 | foo["bar"] in obj.keys() # SIM118
6 6 |
SIM118.py:5:1: SIM118 [*] Use `foo['bar'] in obj` instead of `foo['bar'] in obj.keys()`
SIM118.py:5:1: SIM118 [*] Use `foo["bar"] in obj` instead of `foo["bar"] in obj.keys()`
|
3 | foo["bar"] in obj.keys() # SIM118
3 | key not in obj.keys() # SIM118
4 |
5 | foo['bar'] in obj.keys() # SIM118
5 | foo["bar"] in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
6 |
7 | foo() in obj.keys() # SIM118
7 | foo["bar"] not in obj.keys() # SIM118
|
= help: Convert to `foo['bar'] in obj`
= help: Convert to `foo["bar"] in obj`
Suggested fix
2 2 |
3 3 | foo["bar"] in obj.keys() # SIM118
3 3 | key not in obj.keys() # SIM118
4 4 |
5 |-foo['bar'] in obj.keys() # SIM118
5 |+foo['bar'] in obj # SIM118
5 |-foo["bar"] in obj.keys() # SIM118
5 |+foo["bar"] in obj # SIM118
6 6 |
7 7 | foo() in obj.keys() # SIM118
7 7 | foo["bar"] not in obj.keys() # SIM118
8 8 |
SIM118.py:7:1: SIM118 [*] Use `foo() in obj` instead of `foo() in obj.keys()`
SIM118.py:7:1: SIM118 [*] Use `foo["bar"] not in obj` instead of `foo["bar"] not in obj.keys()`
|
5 | foo['bar'] in obj.keys() # SIM118
5 | foo["bar"] in obj.keys() # SIM118
6 |
7 | foo() in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^ SIM118
7 | foo["bar"] not in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
8 |
9 | for key in obj.keys(): # SIM118
9 | foo['bar'] in obj.keys() # SIM118
|
= help: Convert to `foo() in obj`
= help: Convert to `foo["bar"] not in obj`
Suggested fix
4 4 |
5 5 | foo['bar'] in obj.keys() # SIM118
5 5 | foo["bar"] in obj.keys() # SIM118
6 6 |
7 |-foo() in obj.keys() # SIM118
7 |+foo() in obj # SIM118
7 |-foo["bar"] not in obj.keys() # SIM118
7 |+foo["bar"] not in obj # SIM118
8 8 |
9 9 | for key in obj.keys(): # SIM118
10 10 | pass
9 9 | foo['bar'] in obj.keys() # SIM118
10 10 |
SIM118.py:9:5: SIM118 [*] Use `key in obj` instead of `key in obj.keys()`
SIM118.py:9:1: SIM118 [*] Use `foo['bar'] in obj` instead of `foo['bar'] in obj.keys()`
|
7 | foo() in obj.keys() # SIM118
7 | foo["bar"] not in obj.keys() # SIM118
8 |
9 | for key in obj.keys(): # SIM118
9 | foo['bar'] in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
10 |
11 | foo['bar'] not in obj.keys() # SIM118
|
= help: Convert to `foo['bar'] in obj`
Suggested fix
6 6 |
7 7 | foo["bar"] not in obj.keys() # SIM118
8 8 |
9 |-foo['bar'] in obj.keys() # SIM118
9 |+foo['bar'] in obj # SIM118
10 10 |
11 11 | foo['bar'] not in obj.keys() # SIM118
12 12 |
SIM118.py:11:1: SIM118 [*] Use `foo['bar'] not in obj` instead of `foo['bar'] not in obj.keys()`
|
9 | foo['bar'] in obj.keys() # SIM118
10 |
11 | foo['bar'] not in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
12 |
13 | foo() in obj.keys() # SIM118
|
= help: Convert to `foo['bar'] not in obj`
Suggested fix
8 8 |
9 9 | foo['bar'] in obj.keys() # SIM118
10 10 |
11 |-foo['bar'] not in obj.keys() # SIM118
11 |+foo['bar'] not in obj # SIM118
12 12 |
13 13 | foo() in obj.keys() # SIM118
14 14 |
SIM118.py:13:1: SIM118 [*] Use `foo() in obj` instead of `foo() in obj.keys()`
|
11 | foo['bar'] not in obj.keys() # SIM118
12 |
13 | foo() in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^ SIM118
14 |
15 | foo() not in obj.keys() # SIM118
|
= help: Convert to `foo() in obj`
Suggested fix
10 10 |
11 11 | foo['bar'] not in obj.keys() # SIM118
12 12 |
13 |-foo() in obj.keys() # SIM118
13 |+foo() in obj # SIM118
14 14 |
15 15 | foo() not in obj.keys() # SIM118
16 16 |
SIM118.py:15:1: SIM118 [*] Use `foo() not in obj` instead of `foo() not in obj.keys()`
|
13 | foo() in obj.keys() # SIM118
14 |
15 | foo() not in obj.keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM118
16 |
17 | for key in obj.keys(): # SIM118
|
= help: Convert to `foo() not in obj`
Suggested fix
12 12 |
13 13 | foo() in obj.keys() # SIM118
14 14 |
15 |-foo() not in obj.keys() # SIM118
15 |+foo() not in obj # SIM118
16 16 |
17 17 | for key in obj.keys(): # SIM118
18 18 | pass
SIM118.py:17:5: SIM118 [*] Use `key in obj` instead of `key in obj.keys()`
|
15 | foo() not in obj.keys() # SIM118
16 |
17 | for key in obj.keys(): # SIM118
| ^^^^^^^^^^^^^^^^^ SIM118
10 | pass
18 | pass
|
= help: Convert to `key in obj`
Suggested fix
6 6 |
7 7 | foo() in obj.keys() # SIM118
8 8 |
9 |-for key in obj.keys(): # SIM118
9 |+for key in obj: # SIM118
10 10 | pass
11 11 |
12 12 | for key in list(obj.keys()):
14 14 |
15 15 | foo() not 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()):
SIM118.py:16:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
SIM118.py:24:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
|
14 | del obj[key]
15 |
16 | [k for k in obj.keys()] # SIM118
22 | del obj[key]
23 |
24 | [k for k in obj.keys()] # SIM118
| ^^^^^^^^^^^^^^^ SIM118
17 |
18 | {k for k in obj.keys()} # SIM118
25 |
26 | {k for k in obj.keys()} # SIM118
|
= help: Convert to `k in obj`
Suggested fix
13 13 | if some_property(key):
14 14 | del obj[key]
15 15 |
16 |-[k for k in obj.keys()] # SIM118
16 |+[k for k in obj] # SIM118
17 17 |
18 18 | {k for k in obj.keys()} # SIM118
19 19 |
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 |
SIM118.py:18:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
SIM118.py:26:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
|
16 | [k for k in obj.keys()] # SIM118
17 |
18 | {k for k in obj.keys()} # SIM118
24 | [k for k in obj.keys()] # SIM118
25 |
26 | {k for k in obj.keys()} # SIM118
| ^^^^^^^^^^^^^^^ SIM118
19 |
20 | {k: k for k in obj.keys()} # SIM118
27 |
28 | {k: k for k in obj.keys()} # SIM118
|
= help: Convert to `k in obj`
Suggested fix
15 15 |
16 16 | [k for k in obj.keys()] # SIM118
17 17 |
18 |-{k for k in obj.keys()} # SIM118
18 |+{k for k in obj} # SIM118
19 19 |
20 20 | {k: k for k in obj.keys()} # SIM118
21 21 |
23 23 |
24 24 | [k for k in obj.keys()] # SIM118
25 25 |
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
29 29 |
SIM118.py:20:11: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
SIM118.py:28:11: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
|
18 | {k for k in obj.keys()} # SIM118
19 |
20 | {k: 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
21 |
22 | (k for k in obj.keys()) # SIM118
29 |
30 | (k for k in obj.keys()) # SIM118
|
= help: Convert to `k in obj`
Suggested fix
17 17 |
18 18 | {k for k in obj.keys()} # SIM118
19 19 |
20 |-{k: k for k in obj.keys()} # SIM118
20 |+{k: k for k in obj} # SIM118
21 21 |
22 22 | (k for k in obj.keys()) # SIM118
23 23 |
25 25 |
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
29 29 |
30 30 | (k for k in obj.keys()) # SIM118
31 31 |
SIM118.py:22:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
SIM118.py:30:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
|
20 | {k: k for k in obj.keys()} # SIM118
21 |
22 | (k for k in obj.keys()) # SIM118
28 | {k: k for k in obj.keys()} # SIM118
29 |
30 | (k for k in obj.keys()) # SIM118
| ^^^^^^^^^^^^^^^ SIM118
23 |
24 | key in (obj or {}).keys() # SIM118
31 |
32 | key in (obj or {}).keys() # SIM118
|
= help: Convert to `k in obj`
Suggested fix
19 19 |
20 20 | {k: k for k in obj.keys()} # SIM118
21 21 |
22 |-(k for k in obj.keys()) # SIM118
22 |+(k for k in obj) # SIM118
23 23 |
24 24 | key in (obj or {}).keys() # SIM118
27 27 |
28 28 | {k: k for k in obj.keys()} # SIM118
29 29 |
30 |-(k for k in obj.keys()) # SIM118
30 |+(k for k in obj) # SIM118
31 31 |
32 32 | key in (obj or {}).keys() # SIM118
SIM118.py:24:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {}).keys()`
SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {}).keys()`
|
22 | (k for k in obj.keys()) # SIM118
23 |
24 | key in (obj or {}).keys() # SIM118
30 | (k for k in obj.keys()) # SIM118
31 |
32 | key in (obj or {}).keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
|
= help: Convert to `key in (obj or {})`
Suggested fix
21 21 |
22 22 | (k for k in obj.keys()) # SIM118
23 23 |
24 |-key in (obj or {}).keys() # SIM118
24 |+key in (obj or {}) # SIM118
29 29 |
30 30 | (k for k in obj.keys()) # SIM118
31 31 |
32 |-key in (obj or {}).keys() # SIM118
32 |+key in (obj or {}) # SIM118