Skip to content

Commit

Permalink
[RUF021]: Add an autofix
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Jan 9, 2024
1 parent 20af5a7 commit 795d053
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 48 deletions.
5 changes: 4 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,8 @@

a, b, c = 1, 0, 2
x = a or b and c # RUF021: => `a or (b and c)`
x = a or b and c # looooooooooooong comment but not long enough to prevent an autofix
x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)

a, b, c = 0, 1, 2
y = a and b or c # RUF021: => `(a and b) or c`
Expand All @@ -30,7 +32,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 Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_python_ast::parenthesize::parenthesized_range;
Expand Down Expand Up @@ -37,12 +37,20 @@ use crate::checkers::ast::Checker;
pub struct ParenthesizeChainedOperators;

impl Violation for ParenthesizeChainedOperators {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

#[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) -> Option<String> {
Some(
"Put parentheses around the `and` subexpression inside the `or` expression".to_string(),
)
}
}

/// RUF021
Expand Down Expand Up @@ -75,18 +83,30 @@ 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 mut diagnostic =
Diagnostic::new(ParenthesizeChainedOperators, source_range);
let line_length: u16 = checker.settings.line_length.into();
if !locator.contains_line_break(source_range)
&& locator.full_line(source_range.start()).trim_end().len()
<= ((line_length as usize) - 2)
{
let new_source = format!("({})", locator.slice(source_range));
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(new_source, source_range),
Applicability::Safe,
));
}
checker.diagnostics.push(diagnostic);
}
}
_ => continue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,214 @@
---
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 # looooooooooooong comment but not long enough to prevent an autofix
14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

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 # looooooooooooong comment but not long enough to prevent an autofix
14 14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
15 15 |

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 # looooooooooooong comment but not long enough to prevent an autofix
| ^^^^^^^ RUF021
14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

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 # looooooooooooong comment but not long enough to prevent an autofix
13 |+x = a or (b and c) # looooooooooooong comment but not long enough to prevent an autofix
14 14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
15 15 |
16 16 | a, b, c = 0, 1, 2

RUF021.py:14:10: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
|
12 | x = a or b and c # RUF021: => `a or (b and c)`
13 | x = a or b and c # looooooooooooong comment but not long enough to prevent an autofix
14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
| ^^^^^^^ RUF021
15 |
16 | a, b, c = 0, 1, 2
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

RUF021.py:17: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`
16 | a, b, c = 0, 1, 2
17 | y = a and b or c # RUF021: => `(a and b) or c`
| ^^^^^^^ RUF021
16 |
17 | a, b, c, d = 1, 2, 0, 3
18 |
19 | a, b, c, d = 1, 2, 0, 3
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

RUF021.py:18:14: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
Safe fix
14 14 | x = a or b and c # looooooooong comment to prevent an autofix (line would be too long)
15 15 |
16 16 | a, b, c = 0, 1, 2
17 |-y = a and b or c # RUF021: => `(a and b) or c`
17 |+y = (a and b) or c # RUF021: => `(a and b) or c`
18 18 |
19 19 | a, b, c, d = 1, 2, 0, 3
20 20 | if a or b or c and d: # RUF021: => `a or b or (c and d)`

RUF021.py:20: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)`
19 | a, b, c, d = 1, 2, 0, 3
20 | if a or b or c and d: # RUF021: => `a or b or (c and d)`
| ^^^^^^^ RUF021
19 | pass
21 | pass
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

Safe fix
17 17 | y = a and b or c # RUF021: => `(a and b) or c`
18 18 |
19 19 | a, b, c, d = 1, 2, 0, 3
20 |-if a or b or c and d: # RUF021: => `a or b or (c and d)`
20 |+if a or b or (c and d): # RUF021: => `a or b or (c and d)`
21 21 | pass
22 22 |
23 23 | a, b, c, d = 0, 0, 2, 3

RUF021.py:25:11: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:27: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`
| ^^^^^^^ RUF021
25 | if bool():
26 | pass
27 | elif a or b and c or d: # RUF021: => `a or (b and c) or d`
| ^^^^^^^ RUF021
28 | pass
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

RUF021.py:29:7: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
Safe fix
24 24 |
25 25 | if bool():
26 26 | pass
27 |-elif a or b and c or d: # RUF021: => `a or (b and c) or d`
27 |+elif a or (b and c) or d: # RUF021: => `a or (b and c) or d`
28 28 | pass
29 29 |
30 30 | a, b, c, d = 0, 1, 0, 2

RUF021.py:31: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)`
30 | a, b, c, d = 0, 1, 0, 2
31 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
| ^^^^^^^ RUF021
30 | pass
32 | pass
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

Safe fix
28 28 | pass
29 29 |
30 30 | a, b, c, d = 0, 1, 0, 2
31 |-while a and b or c and d: # RUF021: => `(and b) or (c and d)`
31 |+while (a and b) or c and d: # RUF021: => `(and b) or (c and d)`
32 32 | pass
33 33 |
34 34 | 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:31: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)`
30 | a, b, c, d = 0, 1, 0, 2
31 | while a and b or c and d: # RUF021: => `(and b) or (c and d)`
| ^^^^^^^ RUF021
30 | pass
32 | pass
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

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

RUF021.py:33:44: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
RUF021.py:36: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)`
34 | b, c, d, e = 2, 3, 0, 4
35 | # RUF021: => `a or b or c or (d and e)`:
36 | 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
37 |
38 | a, b, c, d = 0, 1, 3, 0
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

Safe fix
33 33 |
34 34 | b, c, d, e = 2, 3, 0, 4
35 35 | # RUF021: => `a or b or c or (d and e)`:
36 |-z = [a for a in range(5) if a or b or c or d and e]
36 |+z = [a for a in range(5) if a or b or c or (d and e)]
37 37 |
38 38 | a, b, c, d = 0, 1, 3, 0
39 39 | 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:39: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`
38 | a, b, c, d = 0, 1, 3, 0
39 | 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`
40 |
41 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

RUF021.py:38:4: RUF021 Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear
Safe fix
36 36 | z = [a for a in range(5) if a or b or c or d and e]
37 37 |
38 38 | a, b, c, d = 0, 1, 3, 0
39 |-assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
39 |+assert (not a and b) or c or d # RUF021: => `(not a and b) or c or d`
40 40 |
41 41 | if (not a) and b or c or d: # RUF021: => `((not a) and b) or c or d`
42 42 | if (not a and b) or c or d: # OK

RUF021.py:41: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`
39 | assert not a and b or c or d # RUF021: => `(not a and b) or c or d`
40 |
41 | 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
42 | if (not a and b) or c or d: # OK
43 | pass
|
= help: Put parentheses around the `and` subexpression inside the `or` expression

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


0 comments on commit 795d053

Please sign in to comment.