Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RUF021]: Add an autofix #9449

Merged
merged 3 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion crates/ruff_linter/resources/test/fixtures/ruff/RUF021.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

a, b, c = 1, 0, 2
x = a or b and c # RUF021: => `a or (b and c)`
x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix

a, b, c = 0, 1, 2
y = a and b or c # RUF021: => `(a and b) or c`
Expand All @@ -30,7 +31,8 @@
pass

b, c, d, e = 2, 3, 0, 4
z = [a for a in range(5) if a or b or c or d and e] # RUF021: => `a or b or c or (d and e)`
# RUF021: => `a or b or c or (d and e)`:
z = [a for a in range(5) if a or b or c or d and e]

a, b, c, d = 0, 1, 3, 0
assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
Expand All @@ -39,6 +41,20 @@
if (not a and b) or c or d: # OK
pass

if (
some_reasonably_long_condition
or some_other_reasonably_long_condition
and some_third_reasonably_long_condition
or some_fourth_reasonably_long_condition
and some_fifth_reasonably_long_condition
# a commment
and some_sixth_reasonably_long_condition
and some_seventh_reasonably_long_condition
# another comment
or some_eighth_reasonably_long_condition
):
pass

#############################################
# If they're all the same operator, it's fine
#############################################
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_python_ast::parenthesize::parenthesized_range;
Expand Down Expand Up @@ -36,13 +36,17 @@ use crate::checkers::ast::Checker;
#[violation]
pub struct ParenthesizeChainedOperators;

impl Violation for ParenthesizeChainedOperators {
impl AlwaysFixableViolation for ParenthesizeChainedOperators {
#[derive_message_formats]
fn message(&self) -> String {
format!(
"Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear"
)
}

fn fix_title(&self) -> String {
"Parenthesize the `and` subexpression".to_string()
}
}

/// RUF021
Expand Down Expand Up @@ -75,18 +79,22 @@ pub(crate) fn parenthesize_chained_logical_operators(
..
},
) => {
let locator = checker.locator();
let source_range = bool_op.range();
if parenthesized_range(
bool_op.into(),
expr.into(),
checker.indexer().comment_ranges(),
checker.locator().contents(),
locator.contents(),
)
.is_none()
{
checker.diagnostics.push(Diagnostic::new(
ParenthesizeChainedOperators,
bool_op.range(),
));
let new_source = format!("({})", locator.slice(source_range));
let edit = Edit::range_replacement(new_source, source_range);
checker.diagnostics.push(
Diagnostic::new(ParenthesizeChainedOperators, source_range)
.with_fix(Fix::safe_edit(edit)),
);
}
}
_ => continue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,259 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---
RUF021.py:12:10: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:12:10: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
11 | a, b, c = 1, 0, 2
12 | x = a or b and c # RUF021: => `a or (b and c)`
| ^^^^^^^ RUF021
13 |
14 | a, b, c = 0, 1, 2
13 | x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
|
= help: Parenthesize the `and` subexpression

RUF021.py:15:5: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
ℹ Safe fix
9 9 | # as part of a chain.
10 10 |
11 11 | a, b, c = 1, 0, 2
12 |-x = a or b and c # RUF021: => `a or (b and c)`
12 |+x = a or (b and c) # RUF021: => `a or (b and c)`
13 13 | x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
14 14 |
15 15 | a, b, c = 0, 1, 2

RUF021.py:13:10: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
11 | a, b, c = 1, 0, 2
12 | x = a or b and c # RUF021: => `a or (b and c)`
13 | x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
| ^^^^^^^ RUF021
14 |
15 | a, b, c = 0, 1, 2
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
10 10 |
11 11 | a, b, c = 1, 0, 2
12 12 | x = a or b and c # RUF021: => `a or (b and c)`
13 |-x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
13 |+x = a or (b and c) # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
14 14 |
15 15 | a, b, c = 0, 1, 2
16 16 | y = a and b or c # RUF021: => `(a and b) or c`

RUF021.py:16:5: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
14 | a, b, c = 0, 1, 2
15 | y = a and b or c # RUF021: => `(a and b) or c`
15 | a, b, c = 0, 1, 2
16 | y = a and b or c # RUF021: => `(a and b) or c`
| ^^^^^^^ RUF021
16 |
17 | a, b, c, d = 1, 2, 0, 3
17 |
18 | a, b, c, d = 1, 2, 0, 3
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
13 13 | x = a or b and c # looooooooooooooooooooooooooooooong comment but it won't prevent an autofix
14 14 |
15 15 | a, b, c = 0, 1, 2
16 |-y = a and b or c # RUF021: => `(a and b) or c`
16 |+y = (a and b) or c # RUF021: => `(a and b) or c`
17 17 |
18 18 | a, b, c, d = 1, 2, 0, 3
19 19 | if a or b or c and d: # RUF021: => `a or b or (c and d)`

RUF021.py:18:14: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:19:14: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
17 | a, b, c, d = 1, 2, 0, 3
18 | if a or b or c and d: # RUF021: => `a or b or (c and d)`
18 | a, b, c, d = 1, 2, 0, 3
19 | if a or b or c and d: # RUF021: => `a or b or (c and d)`
| ^^^^^^^ RUF021
19 | pass
20 | pass
|
= help: Parenthesize the `and` subexpression

RUF021.py:25:11: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
ℹ Safe fix
16 16 | y = a and b or c # RUF021: => `(a and b) or c`
17 17 |
18 18 | a, b, c, d = 1, 2, 0, 3
19 |-if a or b or c and d: # RUF021: => `a or b or (c and d)`
19 |+if a or b or (c and d): # RUF021: => `a or b or (c and d)`
20 20 | pass
21 21 |
22 22 | a, b, c, d = 0, 0, 2, 3

RUF021.py:26:11: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
23 | if bool():
24 | pass
25 | elif a or b and c or d: # RUF021: => `a or (b and c) or d`
24 | if bool():
25 | pass
26 | elif a or b and c or d: # RUF021: => `a or (b and c) or d`
| ^^^^^^^ RUF021
26 | pass
27 | pass
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
23 23 |
24 24 | if bool():
25 25 | pass
26 |-elif a or b and c or d: # RUF021: => `a or (b and c) or d`
26 |+elif a or (b and c) or d: # RUF021: => `a or (b and c) or d`
27 27 | pass
28 28 |
29 29 | a, b, c, d = 0, 1, 0, 2

RUF021.py:29:7: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:30:7: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
28 | a, b, c, d = 0, 1, 0, 2
29 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
29 | a, b, c, d = 0, 1, 0, 2
30 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
| ^^^^^^^ RUF021
30 | pass
31 | pass
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
27 27 | pass
28 28 |
29 29 | a, b, c, d = 0, 1, 0, 2
30 |-while a and b or c and d: # RUF021: => `(and b) or (c and d)`
30 |+while (a and b) or c and d: # RUF021: => `(and b) or (c and d)`
31 31 | pass
32 32 |
33 33 | b, c, d, e = 2, 3, 0, 4

RUF021.py:29:18: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:30:18: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
28 | a, b, c, d = 0, 1, 0, 2
29 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
29 | a, b, c, d = 0, 1, 0, 2
30 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
| ^^^^^^^ RUF021
30 | pass
31 | pass
|
= help: Parenthesize the `and` subexpression

RUF021.py:33:44: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
ℹ Safe fix
27 27 | pass
28 28 |
29 29 | a, b, c, d = 0, 1, 0, 2
30 |-while a and b or c and d: # RUF021: => `(and b) or (c and d)`
30 |+while a and b or (c and d): # RUF021: => `(and b) or (c and d)`
31 31 | pass
32 32 |
33 33 | b, c, d, e = 2, 3, 0, 4

RUF021.py:35:44: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
32 | b, c, d, e = 2, 3, 0, 4
33 | z = [a for a in range(5) if a or b or c or d and e] # RUF021: => `a or b or c or (d and e)`
33 | b, c, d, e = 2, 3, 0, 4
34 | # RUF021: => `a or b or c or (d and e)`:
35 | z = [a for a in range(5) if a or b or c or d and e]
| ^^^^^^^ RUF021
34 |
35 | a, b, c, d = 0, 1, 3, 0
36 |
37 | a, b, c, d = 0, 1, 3, 0
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
32 32 |
33 33 | b, c, d, e = 2, 3, 0, 4
34 34 | # RUF021: => `a or b or c or (d and e)`:
35 |-z = [a for a in range(5) if a or b or c or d and e]
35 |+z = [a for a in range(5) if a or b or c or (d and e)]
36 36 |
37 37 | a, b, c, d = 0, 1, 3, 0
38 38 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`

RUF021.py:36:8: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:38:8: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
35 | a, b, c, d = 0, 1, 3, 0
36 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
37 | a, b, c, d = 0, 1, 3, 0
38 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
| ^^^^^^^^^^^ RUF021
37 |
38 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
39 |
40 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
35 35 | z = [a for a in range(5) if a or b or c or d and e]
36 36 |
37 37 | a, b, c, d = 0, 1, 3, 0
38 |-assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
38 |+assert (not a and b) or c or d # RUF021: => `(not a and b) or c or d`
39 39 |
40 40 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
41 41 | if (not a and b) or c or d: # OK

RUF021.py:38:4: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:40:4: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
36 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
37 |
38 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
38 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
39 |
40 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
| ^^^^^^^^^^^^^ RUF021
39 | if (not a and b) or c or d: # OK
40 | pass
41 | if (not a and b) or c or d: # OK
42 | pass
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
37 37 | a, b, c, d = 0, 1, 3, 0
38 38 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
39 39 |
40 |-if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
40 |+if ((not a) and b) or c or d: # RUF021: => `((not a) and b) or c or d`
41 41 | if (not a and b) or c or d: # OK
42 42 | pass
43 43 |

RUF021.py:46:8: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
44 | if (
45 | some_reasonably_long_condition
46 | or some_other_reasonably_long_condition
| ________^
47 | | and some_third_reasonably_long_condition
| |____________________________________________^ RUF021
48 | or some_fourth_reasonably_long_condition
49 | and some_fifth_reasonably_long_condition
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
43 43 |
44 44 | if (
45 45 | some_reasonably_long_condition
46 |- or some_other_reasonably_long_condition
47 |- and some_third_reasonably_long_condition
46 |+ or (some_other_reasonably_long_condition
47 |+ and some_third_reasonably_long_condition)
48 48 | or some_fourth_reasonably_long_condition
49 49 | and some_fifth_reasonably_long_condition
50 50 | # a commment

RUF021.py:48:8: RUF021 [*] Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
46 | or some_other_reasonably_long_condition
47 | and some_third_reasonably_long_condition
48 | or some_fourth_reasonably_long_condition
| ________^
49 | | and some_fifth_reasonably_long_condition
50 | | # a commment
51 | | and some_sixth_reasonably_long_condition
52 | | and some_seventh_reasonably_long_condition
| |______________________________________________^ RUF021
53 | # another comment
54 | or some_eighth_reasonably_long_condition
|
= help: Parenthesize the `and` subexpression

ℹ Safe fix
45 45 | some_reasonably_long_condition
46 46 | or some_other_reasonably_long_condition
47 47 | and some_third_reasonably_long_condition
48 |- or some_fourth_reasonably_long_condition
48 |+ or (some_fourth_reasonably_long_condition
49 49 | and some_fifth_reasonably_long_condition
50 50 | # a commment
51 51 | and some_sixth_reasonably_long_condition
52 |- and some_seventh_reasonably_long_condition
52 |+ and some_seventh_reasonably_long_condition)
53 53 | # another comment
54 54 | or some_eighth_reasonably_long_condition
55 55 | ):


Loading