1+ use clippy_utils:: get_parent_expr;
12use clippy_utils:: source:: snippet;
23use rustc_hir:: { BinOp , BinOpKind , Expr , ExprKind } ;
34use rustc_lint:: { LateContext , LateLintPass } ;
@@ -22,6 +23,11 @@ declare_clippy_lint! {
2223 /// # let x = 1;
2324 /// x / 1 + 0 * 1 - 0 | 0;
2425 /// ```
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)
2531 #[ clippy:: version = "pre 1.29.0" ]
2632 pub IDENTITY_OP ,
2733 complexity,
@@ -31,36 +37,66 @@ declare_clippy_lint! {
3137declare_lint_pass ! ( IdentityOp => [ IDENTITY_OP ] ) ;
3238
3339impl < ' 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 ( ) {
3642 return ;
3743 }
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+ }
5975 }
6076 }
6177 }
6278}
6379
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+
64100fn is_allowed ( cx : & LateContext < ' _ > , cmp : BinOp , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
65101 // This lint applies to integers
66102 !cx. typeck_results ( ) . expr_ty ( left) . peel_refs ( ) . is_integral ( )
0 commit comments