Rewrite `not not a` as `bool(a)` in boolean contexts (#4294)

This commit is contained in:
Charlie Marsh 2023-05-08 19:38:24 -04:00 committed by GitHub
parent 43d6aa9173
commit 61f21a6513
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 11 deletions

View File

@ -12,3 +12,10 @@ if not a == b: # OK
if not a != b: # OK
pass
a = not not b # SIM208
f(not not a) # SIM208
if 1 + (not (not a)): # SIM208
pass

View File

@ -6,6 +6,7 @@ a = True if b + c else False # SIM210
a = False if b else True # OK
def f():
# OK
def bool():

View File

@ -7,7 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_common::cformat::{CFormatError, CFormatErrorType};
use rustpython_parser::ast::{
Arg, Arguments, Comprehension, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext,
ExprKind, KeywordData, Located, Operator, Pattern, PatternKind, Stmt, StmtKind, Suite,
ExprKind, KeywordData, Located, Operator, Pattern, PatternKind, Stmt, StmtKind, Suite, Unaryop,
};
use ruff_diagnostics::{Diagnostic, Fix};
@ -2299,7 +2299,16 @@ where
let prev_in_type_definition = self.ctx.in_type_definition;
let prev_in_boolean_test = self.ctx.in_boolean_test;
if !matches!(expr.node, ExprKind::BoolOp { .. }) {
// If we're in a boolean test (e.g., the `test` of a `StmtKind::If`), but now within a
// subexpression (e.g., `a` in `f(a)`), then we're no longer in a boolean test.
if !matches!(
expr.node,
ExprKind::BoolOp { .. }
| ExprKind::UnaryOp {
op: Unaryop::Not,
..
}
) {
self.ctx.in_boolean_test = false;
}

View File

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop};
use rustpython_parser::ast::{Cmpop, Expr, ExprContext, ExprKind, Stmt, StmtKind, Unaryop};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
@ -191,10 +191,27 @@ pub fn double_negation(checker: &mut Checker, expr: &Expr, op: &Unaryop, operand
expr.range(),
);
if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
unparse_expr(operand, checker.stylist),
expr.range(),
)));
if checker.ctx.in_boolean_test {
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
unparse_expr(operand, checker.stylist),
expr.range(),
)));
} else if checker.ctx.is_builtin("bool") {
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
unparse_expr(
&create_expr(ExprKind::Call {
func: Box::new(create_expr(ExprKind::Name {
id: "bool".to_string(),
ctx: ExprContext::Load,
})),
args: vec![*operand.clone()],
keywords: vec![],
}),
checker.stylist,
),
expr.range(),
)));
};
}
checker.diagnostics.push(diagnostic);
}

View File

@ -36,4 +36,64 @@ SIM208.py:4:4: SIM208 [*] Use `a == b` instead of `not (not a == b)`
6 6 |
7 7 | if not a: # OK
SIM208.py:16:5: SIM208 [*] Use `b` instead of `not (not b)`
|
16 | pass
17 |
18 | a = not not b # SIM208
| ^^^^^^^^^ SIM208
19 |
20 | f(not not a) # SIM208
|
= help: Replace with `b`
Suggested fix
13 13 | if not a != b: # OK
14 14 | pass
15 15 |
16 |-a = not not b # SIM208
16 |+a = bool(b) # SIM208
17 17 |
18 18 | f(not not a) # SIM208
19 19 |
SIM208.py:18:3: SIM208 [*] Use `a` instead of `not (not a)`
|
18 | a = not not b # SIM208
19 |
20 | f(not not a) # SIM208
| ^^^^^^^^^ SIM208
21 |
22 | if 1 + (not (not a)): # SIM208
|
= help: Replace with `a`
Suggested fix
15 15 |
16 16 | a = not not b # SIM208
17 17 |
18 |-f(not not a) # SIM208
18 |+f(bool(a)) # SIM208
19 19 |
20 20 | if 1 + (not (not a)): # SIM208
21 21 | pass
SIM208.py:20:9: SIM208 [*] Use `a` instead of `not (not a)`
|
20 | f(not not a) # SIM208
21 |
22 | if 1 + (not (not a)): # SIM208
| ^^^^^^^^^^^ SIM208
23 | pass
|
= help: Replace with `a`
Suggested fix
17 17 |
18 18 | f(not not a) # SIM208
19 19 |
20 |-if 1 + (not (not a)): # SIM208
20 |+if 1 + (bool(a)): # SIM208
21 21 | pass

View File

@ -58,11 +58,11 @@ SIM210.py:5:5: SIM210 [*] Use `bool(b + c)` instead of `True if b + c else False
7 7 | a = False if b else True # OK
8 8 |
SIM210.py:14:9: SIM210 [*] Use `bool(b)` instead of `True if b else False`
SIM210.py:15:9: SIM210 [*] Use `bool(b)` instead of `True if b else False`
|
14 | return False
15 |
16 | a = True if b else False
15 | return False
16 |
17 | a = True if b else False
| ^^^^^^^^^^^^^^^^^^^^ SIM210
|
= help: Replace with `not b