@@ -3,33 +3,47 @@ use clippy_utils::source::snippet_with_applicability;
3
3
use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
4
4
use rustc_ast::ast::{BinOpKind, Expr, ExprKind};
5
5
use rustc_errors::Applicability;
6
- use rustc_lint::{EarlyContext, EarlyLintPass};
6
+ use rustc_lint::{EarlyContext, EarlyLintPass, Lint };
7
7
use rustc_session::declare_lint_pass;
8
8
use rustc_span::source_map::Spanned;
9
9
10
10
declare_clippy_lint! {
11
11
/// ### 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
17
14
///
18
15
/// ### Why is this bad?
19
16
/// Not everyone knows the precedence of those operators by
20
17
/// heart, so expressions like these may trip others trying to reason about the
21
18
/// code.
22
19
///
23
20
/// ### 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
26
22
#[clippy::version = "pre 1.29.0"]
27
23
pub PRECEDENCE,
28
24
complexity,
29
25
"operations where precedence may be unclear"
30
26
}
31
27
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]);
33
47
34
48
impl EarlyLintPass for Precedence {
35
49
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
@@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence {
38
52
}
39
53
40
54
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| {
42
56
span_lint_and_sugg(
43
57
cx,
44
- PRECEDENCE ,
58
+ lint ,
45
59
expr.span,
46
60
"operator precedence might not be obvious",
47
61
"consider parenthesizing your expression",
@@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence {
57
71
match (op, get_bin_opt(left), get_bin_opt(right)) {
58
72
(
59
73
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) ),
62
76
)
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
+ ) => {
64
82
let sugg = format!(
65
83
"({}) {} ({})",
66
84
snippet_with_applicability(cx, left.span, "..", &mut applicability),
67
85
op.as_str(),
68
86
snippet_with_applicability(cx, right.span, "..", &mut applicability)
69
87
);
70
- span_sugg(expr, sugg, applicability);
88
+ span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability);
71
89
},
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) ), _) => {
74
92
let sugg = format!(
75
93
"({}) {} {}",
76
94
snippet_with_applicability(cx, left.span, "..", &mut applicability),
77
95
op.as_str(),
78
96
snippet_with_applicability(cx, right.span, "..", &mut applicability)
79
97
);
80
- span_sugg(expr, sugg, applicability);
98
+ span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
81
99
},
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) )) => {
84
102
let sugg = format!(
85
103
"{} {} ({})",
86
104
snippet_with_applicability(cx, left.span, "..", &mut applicability),
87
105
op.as_str(),
88
106
snippet_with_applicability(cx, right.span, "..", &mut applicability)
89
107
);
90
- span_sugg(expr, sugg, applicability);
108
+ span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
91
109
},
92
110
_ => (),
93
111
}
@@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option<BinOpKind> {
106
124
fn is_bit_op(op: BinOpKind) -> bool {
107
125
matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
108
126
}
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
+ }
0 commit comments