@@ -3,33 +3,47 @@ use clippy_utils::source::snippet_with_applicability;
33use rustc_ast:: ast:: BinOpKind :: { Add , BitAnd , BitOr , BitXor , Div , Mul , Rem , Shl , Shr , Sub } ;
44use rustc_ast:: ast:: { BinOpKind , Expr , ExprKind } ;
55use rustc_errors:: Applicability ;
6- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
6+ use rustc_lint:: { EarlyContext , EarlyLintPass , Lint } ;
77use rustc_session:: declare_lint_pass;
88use rustc_span:: source_map:: Spanned ;
99
1010declare_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
3448impl 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> {
106124fn 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+ }
0 commit comments