1
+ use clippy_utils:: get_parent_expr;
1
2
use clippy_utils:: source:: snippet;
2
3
use rustc_hir:: { BinOp , BinOpKind , Expr , ExprKind } ;
3
4
use rustc_lint:: { LateContext , LateLintPass } ;
@@ -22,6 +23,11 @@ declare_clippy_lint! {
22
23
/// # let x = 1;
23
24
/// x / 1 + 0 * 1 - 0 | 0;
24
25
/// ```
26
+ ///
27
+ /// ### Known problems
28
+ /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
29
+ /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
30
+ /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
25
31
#[ clippy:: version = "pre 1.29.0" ]
26
32
pub IDENTITY_OP ,
27
33
complexity,
@@ -31,36 +37,66 @@ declare_clippy_lint! {
31
37
declare_lint_pass ! ( IdentityOp => [ IDENTITY_OP ] ) ;
32
38
33
39
impl < ' tcx > LateLintPass < ' tcx > for IdentityOp {
34
- fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
35
- if e . span . from_expansion ( ) {
40
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
41
+ if expr . span . from_expansion ( ) {
36
42
return ;
37
43
}
38
- if let ExprKind :: Binary ( cmp, left, right) = e. kind {
39
- if is_allowed ( cx, cmp, left, right) {
40
- return ;
41
- }
42
- match cmp. node {
43
- BinOpKind :: Add | BinOpKind :: BitOr | BinOpKind :: BitXor => {
44
- check ( cx, left, 0 , e. span , right. span ) ;
45
- check ( cx, right, 0 , e. span , left. span ) ;
46
- } ,
47
- BinOpKind :: Shl | BinOpKind :: Shr | BinOpKind :: Sub => check ( cx, right, 0 , e. span , left. span ) ,
48
- BinOpKind :: Mul => {
49
- check ( cx, left, 1 , e. span , right. span ) ;
50
- check ( cx, right, 1 , e. span , left. span ) ;
51
- } ,
52
- BinOpKind :: Div => check ( cx, right, 1 , e. span , left. span ) ,
53
- BinOpKind :: BitAnd => {
54
- check ( cx, left, -1 , e. span , right. span ) ;
55
- check ( cx, right, -1 , e. span , left. span ) ;
56
- } ,
57
- BinOpKind :: Rem => check_remainder ( cx, left, right, e. span , left. span ) ,
58
- _ => ( ) ,
44
+ if let ExprKind :: Binary ( cmp, left, right) = & expr. kind {
45
+ if !is_allowed ( cx, * cmp, left, right) {
46
+ match cmp. node {
47
+ BinOpKind :: Add | BinOpKind :: BitOr | BinOpKind :: BitXor => {
48
+ if reducible_to_right ( cx, expr, right) {
49
+ check ( cx, left, 0 , expr. span , right. span ) ;
50
+ }
51
+ check ( cx, right, 0 , expr. span , left. span ) ;
52
+ } ,
53
+ BinOpKind :: Shl | BinOpKind :: Shr | BinOpKind :: Sub => {
54
+ check ( cx, right, 0 , expr. span , left. span ) ;
55
+ } ,
56
+ BinOpKind :: Mul => {
57
+ if reducible_to_right ( cx, expr, right) {
58
+ check ( cx, left, 1 , expr. span , right. span ) ;
59
+ }
60
+ check ( cx, right, 1 , expr. span , left. span ) ;
61
+ } ,
62
+ BinOpKind :: Div => check ( cx, right, 1 , expr. span , left. span ) ,
63
+ BinOpKind :: BitAnd => {
64
+ if reducible_to_right ( cx, expr, right) {
65
+ check ( cx, left, -1 , expr. span , right. span ) ;
66
+ }
67
+ check ( cx, right, -1 , expr. span , left. span ) ;
68
+ } ,
69
+ BinOpKind :: Rem => {
70
+ // Don't call reducible_to_right because N % N is always reducible to 1
71
+ check_remainder ( cx, left, right, expr. span , left. span ) ;
72
+ } ,
73
+ _ => ( ) ,
74
+ }
59
75
}
60
76
}
61
77
}
62
78
}
63
79
80
+ /// Checks if `left op ..right` can be actually reduced to `right`
81
+ /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
82
+ /// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }`
83
+ /// See #8724
84
+ fn reducible_to_right ( cx : & LateContext < ' _ > , binary : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
85
+ if let ExprKind :: If ( ..) | ExprKind :: Match ( ..) | ExprKind :: Block ( ..) | ExprKind :: Loop ( ..) = right. kind {
86
+ is_toplevel_binary ( cx, binary)
87
+ } else {
88
+ true
89
+ }
90
+ }
91
+
92
+ fn is_toplevel_binary ( cx : & LateContext < ' _ > , must_be_binary : & Expr < ' _ > ) -> bool {
93
+ if let Some ( parent) = get_parent_expr ( cx, must_be_binary) && let ExprKind :: Binary ( ..) = & parent. kind {
94
+ false
95
+ } else {
96
+ true
97
+ }
98
+ }
99
+
64
100
fn is_allowed ( cx : & LateContext < ' _ > , cmp : BinOp , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
65
101
// This lint applies to integers
66
102
!cx. typeck_results ( ) . expr_ty ( left) . peel_refs ( ) . is_integral ( )
0 commit comments