Skip to content

Commit 8db9ecf

Browse files
committed
New lint: precedence_bits, with recent additions to precedence
Commit 2550530 has extended the `precedence` lint to include bitmasking and shift operations. The lint is warn by default, and this generates many hits, especially in embedded or system code, where it is very idiomatic to use expressions such as `1 << 3 | 1 << 5` without parentheses. This commit splits the recent addition into a new lint, which is put into the "restriction" category, while the original one stays in "complexity", because mixing bitmasking and arithmetic operations is less typical.
1 parent ad05bc0 commit 8db9ecf

8 files changed

+153
-50
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5954,6 +5954,7 @@ Released 2018-09-13
59545954
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
59555955
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
59565956
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
5957+
[`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits
59575958
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
59585959
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
59595960
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
626626
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
627627
crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
628628
crate::precedence::PRECEDENCE_INFO,
629+
crate::precedence::PRECEDENCE_BITS_INFO,
629630
crate::ptr::CMP_NULL_INFO,
630631
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
631632
crate::ptr::MUT_FROM_REF_INFO,

clippy_lints/src/precedence.rs

+47-21
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,47 @@ use clippy_utils::source::snippet_with_applicability;
33
use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
44
use rustc_ast::ast::{BinOpKind, Expr, ExprKind};
55
use rustc_errors::Applicability;
6-
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
77
use rustc_session::declare_lint_pass;
88
use rustc_span::source_map::Spanned;
99

1010
declare_clippy_lint! {
1111
/// ### What it does
12-
/// Checks for operations where precedence may be unclear
13-
/// and suggests to add parentheses. Currently it catches the following:
14-
/// * mixed usage of arithmetic and bit shifting/combining operators without
15-
/// parentheses
16-
/// * mixed usage of bitmasking and bit shifting operators without parentheses
12+
/// Checks for operations where precedence may be unclear and suggests to add parentheses.
13+
/// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses
1714
///
1815
/// ### Why is this bad?
1916
/// Not everyone knows the precedence of those operators by
2017
/// heart, so expressions like these may trip others trying to reason about the
2118
/// code.
2219
///
2320
/// ### Example
24-
/// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
25-
/// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
21+
/// `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
2622
#[clippy::version = "pre 1.29.0"]
2723
pub PRECEDENCE,
2824
complexity,
2925
"operations where precedence may be unclear"
3026
}
3127

32-
declare_lint_pass!(Precedence => [PRECEDENCE]);
28+
declare_clippy_lint! {
29+
/// ### What it does
30+
/// Checks for bit shifting operations combined with bit masking/combining operators
31+
/// and suggest using parentheses.
32+
///
33+
/// ### Why restrict this?
34+
/// Not everyone knows the precedence of those operators by
35+
/// heart, so expressions like these may trip others trying to reason about the
36+
/// code.
37+
///
38+
/// ### Example
39+
/// `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
40+
#[clippy::version = "1.86.0"]
41+
pub PRECEDENCE_BITS,
42+
restriction,
43+
"operations mixing bit shifting with bit combining/masking"
44+
}
45+
46+
declare_lint_pass!(Precedence => [PRECEDENCE, PRECEDENCE_BITS]);
3347

3448
impl EarlyLintPass for Precedence {
3549
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
@@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence {
3852
}
3953

4054
if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
41-
let span_sugg = |expr: &Expr, sugg, appl| {
55+
let span_sugg = |lint: &'static Lint, expr: &Expr, sugg, appl| {
4256
span_lint_and_sugg(
4357
cx,
44-
PRECEDENCE,
58+
lint,
4559
expr.span,
4660
"operator precedence might not be obvious",
4761
"consider parenthesizing your expression",
@@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence {
5771
match (op, get_bin_opt(left), get_bin_opt(right)) {
5872
(
5973
BitAnd | BitOr | BitXor,
60-
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
61-
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
74+
Some(left_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
75+
Some(right_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
6276
)
63-
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => {
77+
| (
78+
Shl | Shr,
79+
Some(left_op @ (Add | Div | Mul | Rem | Sub)),
80+
Some(right_op @ (Add | Div | Mul | Rem | Sub)),
81+
) => {
6482
let sugg = format!(
6583
"({}) {} ({})",
6684
snippet_with_applicability(cx, left.span, "..", &mut applicability),
6785
op.as_str(),
6886
snippet_with_applicability(cx, right.span, "..", &mut applicability)
6987
);
70-
span_sugg(expr, sugg, applicability);
88+
span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability);
7189
},
72-
(BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _)
73-
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => {
90+
(BitAnd | BitOr | BitXor, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), _)
91+
| (Shl | Shr, Some(side_op @ (Add | Div | Mul | Rem | Sub)), _) => {
7492
let sugg = format!(
7593
"({}) {} {}",
7694
snippet_with_applicability(cx, left.span, "..", &mut applicability),
7795
op.as_str(),
7896
snippet_with_applicability(cx, right.span, "..", &mut applicability)
7997
);
80-
span_sugg(expr, sugg, applicability);
98+
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
8199
},
82-
(BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub))
83-
| (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => {
100+
(BitAnd | BitOr | BitXor, _, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)))
101+
| (Shl | Shr, _, Some(side_op @ (Add | Div | Mul | Rem | Sub))) => {
84102
let sugg = format!(
85103
"{} {} ({})",
86104
snippet_with_applicability(cx, left.span, "..", &mut applicability),
87105
op.as_str(),
88106
snippet_with_applicability(cx, right.span, "..", &mut applicability)
89107
);
90-
span_sugg(expr, sugg, applicability);
108+
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
91109
},
92110
_ => (),
93111
}
@@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option<BinOpKind> {
106124
fn is_bit_op(op: BinOpKind) -> bool {
107125
matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
108126
}
127+
128+
fn lint_for(ops: &[BinOpKind]) -> &'static Lint {
129+
if ops.iter().all(|op| is_bit_op(*op)) {
130+
PRECEDENCE_BITS
131+
} else {
132+
PRECEDENCE
133+
}
134+
}

tests/ui/precedence.fixed

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ fn main() {
2020
1 ^ (1 - 1);
2121
3 | (2 - 1);
2222
3 & (5 - 2);
23-
0x0F00 & (0x00F0 << 4);
24-
0x0F00 & (0xF000 >> 4);
25-
(0x0F00 << 1) ^ 3;
26-
(0x0F00 << 1) | 2;
23+
0x0F00 & 0x00F0 << 4;
24+
0x0F00 & 0xF000 >> 4;
25+
0x0F00 << 1 ^ 3;
26+
0x0F00 << 1 | 2;
2727

2828
let b = 3;
2929
trip!(b * 8);

tests/ui/precedence.stderr

+1-25
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,5 @@ error: operator precedence might not be obvious
4343
LL | 3 & 5 - 2;
4444
| ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
4545

46-
error: operator precedence might not be obvious
47-
--> tests/ui/precedence.rs:23:5
48-
|
49-
LL | 0x0F00 & 0x00F0 << 4;
50-
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
51-
52-
error: operator precedence might not be obvious
53-
--> tests/ui/precedence.rs:24:5
54-
|
55-
LL | 0x0F00 & 0xF000 >> 4;
56-
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
57-
58-
error: operator precedence might not be obvious
59-
--> tests/ui/precedence.rs:25:5
60-
|
61-
LL | 0x0F00 << 1 ^ 3;
62-
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
63-
64-
error: operator precedence might not be obvious
65-
--> tests/ui/precedence.rs:26:5
66-
|
67-
LL | 0x0F00 << 1 | 2;
68-
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
69-
70-
error: aborting due to 11 previous errors
46+
error: aborting due to 7 previous errors
7147

tests/ui/precedence_bits.fixed

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![warn(clippy::precedence_bits)]
2+
#![allow(
3+
unused_must_use,
4+
clippy::no_effect,
5+
clippy::unnecessary_operation,
6+
clippy::precedence
7+
)]
8+
#![allow(clippy::identity_op)]
9+
#![allow(clippy::eq_op)]
10+
11+
macro_rules! trip {
12+
($a:expr) => {
13+
match $a & 0b1111_1111u8 {
14+
0 => println!("a is zero ({})", $a),
15+
_ => println!("a is {}", $a),
16+
}
17+
};
18+
}
19+
20+
fn main() {
21+
1 << 2 + 3;
22+
1 + 2 << 3;
23+
4 >> 1 + 1;
24+
1 + 3 >> 2;
25+
1 ^ 1 - 1;
26+
3 | 2 - 1;
27+
3 & 5 - 2;
28+
0x0F00 & (0x00F0 << 4);
29+
0x0F00 & (0xF000 >> 4);
30+
(0x0F00 << 1) ^ 3;
31+
(0x0F00 << 1) | 2;
32+
33+
let b = 3;
34+
trip!(b * 8);
35+
}

tests/ui/precedence_bits.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![warn(clippy::precedence_bits)]
2+
#![allow(
3+
unused_must_use,
4+
clippy::no_effect,
5+
clippy::unnecessary_operation,
6+
clippy::precedence
7+
)]
8+
#![allow(clippy::identity_op)]
9+
#![allow(clippy::eq_op)]
10+
11+
macro_rules! trip {
12+
($a:expr) => {
13+
match $a & 0b1111_1111u8 {
14+
0 => println!("a is zero ({})", $a),
15+
_ => println!("a is {}", $a),
16+
}
17+
};
18+
}
19+
20+
fn main() {
21+
1 << 2 + 3;
22+
1 + 2 << 3;
23+
4 >> 1 + 1;
24+
1 + 3 >> 2;
25+
1 ^ 1 - 1;
26+
3 | 2 - 1;
27+
3 & 5 - 2;
28+
0x0F00 & 0x00F0 << 4;
29+
0x0F00 & 0xF000 >> 4;
30+
0x0F00 << 1 ^ 3;
31+
0x0F00 << 1 | 2;
32+
33+
let b = 3;
34+
trip!(b * 8);
35+
}

tests/ui/precedence_bits.stderr

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error: operator precedence might not be obvious
2+
--> tests/ui/precedence_bits.rs:28:5
3+
|
4+
LL | 0x0F00 & 0x00F0 << 4;
5+
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
6+
|
7+
= note: `-D clippy::precedence-bits` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::precedence_bits)]`
9+
10+
error: operator precedence might not be obvious
11+
--> tests/ui/precedence_bits.rs:29:5
12+
|
13+
LL | 0x0F00 & 0xF000 >> 4;
14+
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
15+
16+
error: operator precedence might not be obvious
17+
--> tests/ui/precedence_bits.rs:30:5
18+
|
19+
LL | 0x0F00 << 1 ^ 3;
20+
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
21+
22+
error: operator precedence might not be obvious
23+
--> tests/ui/precedence_bits.rs:31:5
24+
|
25+
LL | 0x0F00 << 1 | 2;
26+
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
27+
28+
error: aborting due to 4 previous errors
29+

0 commit comments

Comments
 (0)