@@ -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