about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-03 09:02:02 +0000
committerbors <bors@rust-lang.org>2024-01-03 09:02:02 +0000
commit2eb13d3a7cb19372c40d3db1b45274e126a8dbf2 (patch)
tree065757311cb2e207a1f908a3eb293bd38be427ed
parent9eb2b225f8fe7c8066adb261635fbece9f356ea3 (diff)
parentc2b3f5c767e69a3788efb01633ae53f6adfcd6dd (diff)
downloadrust-2eb13d3a7cb19372c40d3db1b45274e126a8dbf2.tar.gz
rust-2eb13d3a7cb19372c40d3db1b45274e126a8dbf2.zip
Auto merge of #12056 - PartiallyTyped:12050, r=xFrednet
Fixes: #12050 - `identity_op` correctly suggests a deference for coerced references

When `identity_op` identifies a `no_op`, provides a suggestion, it also checks the type of the type of the variable. If the variable is a reference that's been coerced into a value, e.g.

```
let x = &0i32;
let _ = x + 0;
```

the suggestion will now use a derefence. This is done by identifying whether the variable is a reference to an integral value, and then whether it gets dereferenced.

changelog: false positive: [`identity_op`]: corrected suggestion for reference coerced to value.

fixes: #12050
-rw-r--r--clippy_lints/src/operators/identity_op.rs208
-rw-r--r--tests/ui/identity_op.fixed94
-rw-r--r--tests/ui/identity_op.rs94
-rw-r--r--tests/ui/identity_op.stderr154
4 files changed, 426 insertions, 124 deletions
diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
index 8ecb038627f..f671517c134 100644
--- a/clippy_lints/src/operators/identity_op.rs
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -18,82 +18,118 @@ pub(crate) fn check<'tcx>(
     right: &'tcx Expr<'_>,
 ) {
     if !is_allowed(cx, op, left, right) {
-        match op {
-            BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                check_op(
-                    cx,
-                    left,
-                    0,
-                    expr.span,
-                    peel_hir_expr_refs(right).0.span,
-                    needs_parenthesis(cx, expr, right),
-                );
-                check_op(
-                    cx,
-                    right,
-                    0,
-                    expr.span,
-                    peel_hir_expr_refs(left).0.span,
-                    Parens::Unneeded,
-                );
-            },
-            BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
-                check_op(
-                    cx,
-                    right,
-                    0,
-                    expr.span,
-                    peel_hir_expr_refs(left).0.span,
-                    Parens::Unneeded,
-                );
-            },
-            BinOpKind::Mul => {
-                check_op(
-                    cx,
-                    left,
-                    1,
-                    expr.span,
-                    peel_hir_expr_refs(right).0.span,
-                    needs_parenthesis(cx, expr, right),
-                );
-                check_op(
-                    cx,
-                    right,
-                    1,
-                    expr.span,
-                    peel_hir_expr_refs(left).0.span,
-                    Parens::Unneeded,
-                );
-            },
-            BinOpKind::Div => check_op(
+        return;
+    }
+
+    // we need to know whether a ref is coerced to a value
+    // if a ref is coerced, then the suggested lint must deref it
+    // e.g. `let _: i32 = x+0` with `x: &i32` should be replaced with `let _: i32 = *x`.
+    // we do this by checking the _kind_ of the type of the expression
+    // if it's a ref, we then check whether it is erased, and that's it.
+    let (peeled_left_span, left_is_coerced_to_value) = {
+        let expr = peel_hir_expr_refs(left).0;
+        let span = expr.span;
+        let is_coerced = expr_is_erased_ref(cx, expr);
+        (span, is_coerced)
+    };
+
+    let (peeled_right_span, right_is_coerced_to_value) = {
+        let expr = peel_hir_expr_refs(right).0;
+        let span = expr.span;
+        let is_coerced = expr_is_erased_ref(cx, expr);
+        (span, is_coerced)
+    };
+
+    match op {
+        BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+            check_op(
+                cx,
+                left,
+                0,
+                expr.span,
+                peeled_right_span,
+                needs_parenthesis(cx, expr, right),
+                right_is_coerced_to_value,
+            );
+            check_op(
+                cx,
+                right,
+                0,
+                expr.span,
+                peeled_left_span,
+                Parens::Unneeded,
+                left_is_coerced_to_value,
+            );
+        },
+        BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
+            check_op(
+                cx,
+                right,
+                0,
+                expr.span,
+                peeled_left_span,
+                Parens::Unneeded,
+                left_is_coerced_to_value,
+            );
+        },
+        BinOpKind::Mul => {
+            check_op(
+                cx,
+                left,
+                1,
+                expr.span,
+                peeled_right_span,
+                needs_parenthesis(cx, expr, right),
+                right_is_coerced_to_value,
+            );
+            check_op(
                 cx,
                 right,
                 1,
                 expr.span,
-                peel_hir_expr_refs(left).0.span,
+                peeled_left_span,
                 Parens::Unneeded,
-            ),
-            BinOpKind::BitAnd => {
-                check_op(
-                    cx,
-                    left,
-                    -1,
-                    expr.span,
-                    peel_hir_expr_refs(right).0.span,
-                    needs_parenthesis(cx, expr, right),
-                );
-                check_op(
-                    cx,
-                    right,
-                    -1,
-                    expr.span,
-                    peel_hir_expr_refs(left).0.span,
-                    Parens::Unneeded,
-                );
-            },
-            BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
-            _ => (),
-        }
+                left_is_coerced_to_value,
+            );
+        },
+        BinOpKind::Div => check_op(
+            cx,
+            right,
+            1,
+            expr.span,
+            peeled_left_span,
+            Parens::Unneeded,
+            left_is_coerced_to_value,
+        ),
+        BinOpKind::BitAnd => {
+            check_op(
+                cx,
+                left,
+                -1,
+                expr.span,
+                peeled_right_span,
+                needs_parenthesis(cx, expr, right),
+                right_is_coerced_to_value,
+            );
+            check_op(
+                cx,
+                right,
+                -1,
+                expr.span,
+                peeled_left_span,
+                Parens::Unneeded,
+                left_is_coerced_to_value,
+            );
+        },
+        BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
+        _ => (),
+    }
+}
+
+fn expr_is_erased_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match cx.typeck_results().expr_ty(expr).kind() {
+        ty::Ref(r, ..) => r.is_erased(),
+        _ => false,
     }
 }
 
@@ -144,11 +180,11 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
 }
 
 fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
-    // This lint applies to integers
-    !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
-        || !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
+    // This lint applies to integers and their references
+    cx.typeck_results().expr_ty(left).peel_refs().is_integral()
+        && cx.typeck_results().expr_ty(right).peel_refs().is_integral()
         // `1 << 0` is a common pattern in bit manipulation code
-        || (cmp == BinOpKind::Shl
+        && !(cmp == BinOpKind::Shl
             && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
             && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 }
@@ -161,11 +197,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
         (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
         _ => return,
     } {
-        span_ineffective_operation(cx, span, arg, Parens::Unneeded);
+        span_ineffective_operation(cx, span, arg, Parens::Unneeded, false);
     }
 }
 
-fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
+fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) {
     if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
             ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
@@ -178,18 +214,28 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
             1 => v == 1,
             _ => unreachable!(),
         } {
-            span_ineffective_operation(cx, span, arg, parens);
+            span_ineffective_operation(cx, span, arg, parens, is_erased);
         }
     }
 }
 
-fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
+fn span_ineffective_operation(
+    cx: &LateContext<'_>,
+    span: Span,
+    arg: Span,
+    parens: Parens,
+    is_ref_coerced_to_val: bool,
+) {
     let mut applicability = Applicability::MachineApplicable;
     let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
-
+    let expr_snippet = if is_ref_coerced_to_val {
+        format!("*{expr_snippet}")
+    } else {
+        expr_snippet.into_owned()
+    };
     let suggestion = match parens {
         Parens::Needed => format!("({expr_snippet})"),
-        Parens::Unneeded => expr_snippet.into_owned(),
+        Parens::Unneeded => expr_snippet,
     };
 
     span_lint_and_sugg(
diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed
index f3b4b1fffa0..660e9a21b18 100644
--- a/tests/ui/identity_op.fixed
+++ b/tests/ui/identity_op.fixed
@@ -6,7 +6,9 @@
     clippy::unnecessary_operation,
     clippy::op_ref,
     clippy::double_parens,
-    clippy::uninlined_format_args
+    clippy::uninlined_format_args,
+    clippy::borrow_deref_ref,
+    clippy::deref_addrof
 )]
 
 use std::fmt::Write as _;
@@ -40,32 +42,45 @@ fn main() {
     let x = 0;
 
     x;
+    //~^ ERROR: this operation has no effect
     x;
+    //~^ ERROR: this operation has no effect
     x + 1;
     x;
+    //~^ ERROR: this operation has no effect
     1 + x;
     x - ZERO; //no error, as we skip lookups (for now)
     x;
+    //~^ ERROR: this operation has no effect
     ((ZERO)) | x; //no error, as we skip lookups (for now)
 
     x;
+    //~^ ERROR: this operation has no effect
     x;
+    //~^ ERROR: this operation has no effect
     x / ONE; //no error, as we skip lookups (for now)
 
     x / 2; //no false positive
 
     x & NEG_ONE; //no error, as we skip lookups (for now)
     x;
+    //~^ ERROR: this operation has no effect
 
     let u: u8 = 0;
     u;
+    //~^ ERROR: this operation has no effect
 
     1 << 0; // no error, this case is allowed, see issue 3430
     42;
+    //~^ ERROR: this operation has no effect
     1;
+    //~^ ERROR: this operation has no effect
     42;
+    //~^ ERROR: this operation has no effect
     x;
+    //~^ ERROR: this operation has no effect
     x;
+    //~^ ERROR: this operation has no effect
 
     let mut a = A(String::new());
     let b = a << 0; // no error: non-integer
@@ -73,10 +88,15 @@ fn main() {
     1 * Meter; // no error: non-integer
 
     2;
+    //~^ ERROR: this operation has no effect
     -2;
+    //~^ ERROR: this operation has no effect
     2 + x;
+    //~^ ERROR: this operation has no effect
     -2 + x;
+    //~^ ERROR: this operation has no effect
     x + 1;
+    //~^ ERROR: this operation has no effect
     (x + 1) % 3; // no error
     4 % 3; // no error
     4 % -3; // no error
@@ -85,38 +105,110 @@ fn main() {
     let a = 0;
     let b = true;
     (if b { 1 } else { 2 });
+    //~^ ERROR: this operation has no effect
     (if b { 1 } else { 2 }) + if b { 3 } else { 4 };
+    //~^ ERROR: this operation has no effect
     (match a { 0 => 10, _ => 20 });
+    //~^ ERROR: this operation has no effect
     (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
+    //~^ ERROR: this operation has no effect
     (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
+    //~^ ERROR: this operation has no effect
     (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
+    //~^ ERROR: this operation has no effect
     (if b { 1 } else { 2 });
+    //~^ ERROR: this operation has no effect
 
     ({ a }) + 3;
+    //~^ ERROR: this operation has no effect
     ({ a } * 2);
+    //~^ ERROR: this operation has no effect
     (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
+    //~^ ERROR: this operation has no effect
 
     fn f(_: i32) {
         todo!();
     }
+
     f(a + { 8 * 5 });
+    //~^ ERROR: this operation has no effect
     f(if b { 1 } else { 2 } + 3);
+    //~^ ERROR: this operation has no effect
+    
     const _: i32 = { 2 * 4 } + 3;
+    //~^ ERROR: this operation has no effect
     const _: i32 = { 1 + 2 * 3 } + 3;
+    //~^ ERROR: this operation has no effect
 
     a as usize;
+    //~^ ERROR: this operation has no effect
     let _ = a as usize;
+    //~^ ERROR: this operation has no effect
     ({ a } as usize);
+    //~^ ERROR: this operation has no effect
 
     2 * { a };
+    //~^ ERROR: this operation has no effect
     (({ a } + 4));
+    //~^ ERROR: this operation has no effect
     1;
+    //~^ ERROR: this operation has no effect
 
     // Issue #9904
     let x = 0i32;
     let _: i32 = x;
+    //~^ ERROR: this operation has no effect
 }
 
 pub fn decide(a: bool, b: bool) -> u32 {
     (if a { 1 } else { 2 }) + if b { 3 } else { 5 }
 }
+
+/// The following tests are from / for issue #12050
+/// In short, the lint didn't work for coerced references,
+/// e.g. let x = &0; let y = x + 0;
+/// because the suggested fix was `let y = x;` but
+/// it should have been `let y = *x;`
+fn issue_12050() {
+    {
+        let x = &0i32;
+        let _: i32 = *x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = *x;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        let x = &&0i32;
+        let _: i32 = **x;
+        //~^ ERROR: this operation has no effect
+        let x = &&0i32;
+        let _: i32 = **x;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        // this is just silly
+        let x = &&&0i32;
+        let _: i32 = ***x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = ***x;
+        //~^ ERROR: this operation has no effect
+        let x = 0i32;
+        let _: i32 = *&x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **&&x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = *&*&x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **&&*&x;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        // this is getting ridiculous, but we should still see the same
+        // error message so let's just keep going
+        let x = &0i32;
+        let _: i32 = ***&&*&x;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = ***&&*&x;
+        //~^ ERROR: this operation has no effect
+    }
+}
diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs
index 631aa3b0215..bbef531e9dc 100644
--- a/tests/ui/identity_op.rs
+++ b/tests/ui/identity_op.rs
@@ -6,7 +6,9 @@
     clippy::unnecessary_operation,
     clippy::op_ref,
     clippy::double_parens,
-    clippy::uninlined_format_args
+    clippy::uninlined_format_args,
+    clippy::borrow_deref_ref,
+    clippy::deref_addrof
 )]
 
 use std::fmt::Write as _;
@@ -40,32 +42,45 @@ fn main() {
     let x = 0;
 
     x + 0;
+    //~^ ERROR: this operation has no effect
     x + (1 - 1);
+    //~^ ERROR: this operation has no effect
     x + 1;
     0 + x;
+    //~^ ERROR: this operation has no effect
     1 + x;
     x - ZERO; //no error, as we skip lookups (for now)
     x | (0);
+    //~^ ERROR: this operation has no effect
     ((ZERO)) | x; //no error, as we skip lookups (for now)
 
     x * 1;
+    //~^ ERROR: this operation has no effect
     1 * x;
+    //~^ ERROR: this operation has no effect
     x / ONE; //no error, as we skip lookups (for now)
 
     x / 2; //no false positive
 
     x & NEG_ONE; //no error, as we skip lookups (for now)
     -1 & x;
+    //~^ ERROR: this operation has no effect
 
     let u: u8 = 0;
     u & 255;
+    //~^ ERROR: this operation has no effect
 
     1 << 0; // no error, this case is allowed, see issue 3430
     42 << 0;
+    //~^ ERROR: this operation has no effect
     1 >> 0;
+    //~^ ERROR: this operation has no effect
     42 >> 0;
+    //~^ ERROR: this operation has no effect
     &x >> 0;
+    //~^ ERROR: this operation has no effect
     x >> &0;
+    //~^ ERROR: this operation has no effect
 
     let mut a = A(String::new());
     let b = a << 0; // no error: non-integer
@@ -73,10 +88,15 @@ fn main() {
     1 * Meter; // no error: non-integer
 
     2 % 3;
+    //~^ ERROR: this operation has no effect
     -2 % 3;
+    //~^ ERROR: this operation has no effect
     2 % -3 + x;
+    //~^ ERROR: this operation has no effect
     -2 % -3 + x;
+    //~^ ERROR: this operation has no effect
     x + 1 % 3;
+    //~^ ERROR: this operation has no effect
     (x + 1) % 3; // no error
     4 % 3; // no error
     4 % -3; // no error
@@ -85,38 +105,110 @@ fn main() {
     let a = 0;
     let b = true;
     0 + if b { 1 } else { 2 };
+    //~^ ERROR: this operation has no effect
     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
+    //~^ ERROR: this operation has no effect
     0 + match a { 0 => 10, _ => 20 };
+    //~^ ERROR: this operation has no effect
     0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
+    //~^ ERROR: this operation has no effect
     0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
+    //~^ ERROR: this operation has no effect
     0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
+    //~^ ERROR: this operation has no effect
     (if b { 1 } else { 2 }) + 0;
+    //~^ ERROR: this operation has no effect
 
     0 + { a } + 3;
+    //~^ ERROR: this operation has no effect
     0 + { a } * 2;
+    //~^ ERROR: this operation has no effect
     0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
+    //~^ ERROR: this operation has no effect
 
     fn f(_: i32) {
         todo!();
     }
+
     f(1 * a + { 8 * 5 });
+    //~^ ERROR: this operation has no effect
     f(0 + if b { 1 } else { 2 } + 3);
+    //~^ ERROR: this operation has no effect
+    
     const _: i32 = { 2 * 4 } + 0 + 3;
+    //~^ ERROR: this operation has no effect
     const _: i32 = 0 + { 1 + 2 * 3 } + 3;
+    //~^ ERROR: this operation has no effect
 
     0 + a as usize;
+    //~^ ERROR: this operation has no effect
     let _ = 0 + a as usize;
+    //~^ ERROR: this operation has no effect
     0 + { a } as usize;
+    //~^ ERROR: this operation has no effect
 
     2 * (0 + { a });
+    //~^ ERROR: this operation has no effect
     1 * ({ a } + 4);
+    //~^ ERROR: this operation has no effect
     1 * 1;
+    //~^ ERROR: this operation has no effect
 
     // Issue #9904
     let x = 0i32;
     let _: i32 = &x + 0;
+    //~^ ERROR: this operation has no effect
 }
 
 pub fn decide(a: bool, b: bool) -> u32 {
     0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
 }
+
+/// The following tests are from / for issue #12050
+/// In short, the lint didn't work for coerced references,
+/// e.g. let x = &0; let y = x + 0;
+/// because the suggested fix was `let y = x;` but
+/// it should have been `let y = *x;`
+fn issue_12050() {
+    {
+        let x = &0i32;
+        let _: i32 = *x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = x + 0;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        let x = &&0i32;
+        let _: i32 = **x + 0;
+        //~^ ERROR: this operation has no effect
+        let x = &&0i32;
+        let _: i32 = *x + 0;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        // this is just silly
+        let x = &&&0i32;
+        let _: i32 = ***x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **x + 0;
+        //~^ ERROR: this operation has no effect
+        let x = 0i32;
+        let _: i32 = *&x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **&&x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = *&*&x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **&&*&x + 0;
+        //~^ ERROR: this operation has no effect
+    }
+    {
+        // this is getting ridiculous, but we should still see the same
+        // error message so let's just keep going
+        let x = &0i32;
+        let _: i32 = **&&*&x + 0;
+        //~^ ERROR: this operation has no effect
+        let _: i32 = **&&*&x + 0;
+        //~^ ERROR: this operation has no effect
+    }
+}
diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr
index 2a61327f15f..6bb980035c1 100644
--- a/tests/ui/identity_op.stderr
+++ b/tests/ui/identity_op.stderr
@@ -1,5 +1,5 @@
 error: this operation has no effect
-  --> $DIR/identity_op.rs:42:5
+  --> $DIR/identity_op.rs:44:5
    |
 LL |     x + 0;
    |     ^^^^^ help: consider reducing it to: `x`
@@ -8,238 +8,310 @@ LL |     x + 0;
    = help: to override `-D warnings` add `#[allow(clippy::identity_op)]`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:43:5
+  --> $DIR/identity_op.rs:46:5
    |
 LL |     x + (1 - 1);
    |     ^^^^^^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:45:5
+  --> $DIR/identity_op.rs:49:5
    |
 LL |     0 + x;
    |     ^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:48:5
+  --> $DIR/identity_op.rs:53:5
    |
 LL |     x | (0);
    |     ^^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:51:5
+  --> $DIR/identity_op.rs:57:5
    |
 LL |     x * 1;
    |     ^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:52:5
+  --> $DIR/identity_op.rs:59:5
    |
 LL |     1 * x;
    |     ^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:58:5
+  --> $DIR/identity_op.rs:66:5
    |
 LL |     -1 & x;
    |     ^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:61:5
+  --> $DIR/identity_op.rs:70:5
    |
 LL |     u & 255;
    |     ^^^^^^^ help: consider reducing it to: `u`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:64:5
+  --> $DIR/identity_op.rs:74:5
    |
 LL |     42 << 0;
    |     ^^^^^^^ help: consider reducing it to: `42`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:65:5
+  --> $DIR/identity_op.rs:76:5
    |
 LL |     1 >> 0;
    |     ^^^^^^ help: consider reducing it to: `1`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:66:5
+  --> $DIR/identity_op.rs:78:5
    |
 LL |     42 >> 0;
    |     ^^^^^^^ help: consider reducing it to: `42`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:67:5
+  --> $DIR/identity_op.rs:80:5
    |
 LL |     &x >> 0;
    |     ^^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:68:5
+  --> $DIR/identity_op.rs:82:5
    |
 LL |     x >> &0;
    |     ^^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:75:5
+  --> $DIR/identity_op.rs:90:5
    |
 LL |     2 % 3;
    |     ^^^^^ help: consider reducing it to: `2`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:76:5
+  --> $DIR/identity_op.rs:92:5
    |
 LL |     -2 % 3;
    |     ^^^^^^ help: consider reducing it to: `-2`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:77:5
+  --> $DIR/identity_op.rs:94:5
    |
 LL |     2 % -3 + x;
    |     ^^^^^^ help: consider reducing it to: `2`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:78:5
+  --> $DIR/identity_op.rs:96:5
    |
 LL |     -2 % -3 + x;
    |     ^^^^^^^ help: consider reducing it to: `-2`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:79:9
+  --> $DIR/identity_op.rs:98:9
    |
 LL |     x + 1 % 3;
    |         ^^^^^ help: consider reducing it to: `1`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:87:5
+  --> $DIR/identity_op.rs:107:5
    |
 LL |     0 + if b { 1 } else { 2 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:88:5
+  --> $DIR/identity_op.rs:109:5
    |
 LL |     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:89:5
+  --> $DIR/identity_op.rs:111:5
    |
 LL |     0 + match a { 0 => 10, _ => 20 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:90:5
+  --> $DIR/identity_op.rs:113:5
    |
 LL |     0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:91:5
+  --> $DIR/identity_op.rs:115:5
    |
 LL |     0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:92:5
+  --> $DIR/identity_op.rs:117:5
    |
 LL |     0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:93:5
+  --> $DIR/identity_op.rs:119:5
    |
 LL |     (if b { 1 } else { 2 }) + 0;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:95:5
+  --> $DIR/identity_op.rs:122:5
    |
 LL |     0 + { a } + 3;
    |     ^^^^^^^^^ help: consider reducing it to: `({ a })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:96:5
+  --> $DIR/identity_op.rs:124:5
    |
 LL |     0 + { a } * 2;
    |     ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:97:5
+  --> $DIR/identity_op.rs:126:5
    |
 LL |     0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:102:7
+  --> $DIR/identity_op.rs:133:7
    |
 LL |     f(1 * a + { 8 * 5 });
    |       ^^^^^ help: consider reducing it to: `a`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:103:7
+  --> $DIR/identity_op.rs:135:7
    |
 LL |     f(0 + if b { 1 } else { 2 } + 3);
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:104:20
+  --> $DIR/identity_op.rs:138:20
    |
 LL |     const _: i32 = { 2 * 4 } + 0 + 3;
    |                    ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:105:20
+  --> $DIR/identity_op.rs:140:20
    |
 LL |     const _: i32 = 0 + { 1 + 2 * 3 } + 3;
    |                    ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:107:5
+  --> $DIR/identity_op.rs:143:5
    |
 LL |     0 + a as usize;
    |     ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:108:13
+  --> $DIR/identity_op.rs:145:13
    |
 LL |     let _ = 0 + a as usize;
    |             ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:109:5
+  --> $DIR/identity_op.rs:147:5
    |
 LL |     0 + { a } as usize;
    |     ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:111:9
+  --> $DIR/identity_op.rs:150:9
    |
 LL |     2 * (0 + { a });
    |         ^^^^^^^^^^^ help: consider reducing it to: `{ a }`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:112:5
+  --> $DIR/identity_op.rs:152:5
    |
 LL |     1 * ({ a } + 4);
    |     ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:113:5
+  --> $DIR/identity_op.rs:154:5
    |
 LL |     1 * 1;
    |     ^^^^^ help: consider reducing it to: `1`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:117:18
+  --> $DIR/identity_op.rs:159:18
    |
 LL |     let _: i32 = &x + 0;
    |                  ^^^^^^ help: consider reducing it to: `x`
 
 error: this operation has no effect
-  --> $DIR/identity_op.rs:121:5
+  --> $DIR/identity_op.rs:164:5
    |
 LL |     0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
 
-error: aborting due to 40 previous errors
+error: this operation has no effect
+  --> $DIR/identity_op.rs:175:22
+   |
+LL |         let _: i32 = *x + 0;
+   |                      ^^^^^^ help: consider reducing it to: `*x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:177:22
+   |
+LL |         let _: i32 = x + 0;
+   |                      ^^^^^ help: consider reducing it to: `*x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:182:22
+   |
+LL |         let _: i32 = **x + 0;
+   |                      ^^^^^^^ help: consider reducing it to: `**x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:185:22
+   |
+LL |         let _: i32 = *x + 0;
+   |                      ^^^^^^ help: consider reducing it to: `**x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:191:22
+   |
+LL |         let _: i32 = ***x + 0;
+   |                      ^^^^^^^^ help: consider reducing it to: `***x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:193:22
+   |
+LL |         let _: i32 = **x + 0;
+   |                      ^^^^^^^ help: consider reducing it to: `***x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:196:22
+   |
+LL |         let _: i32 = *&x + 0;
+   |                      ^^^^^^^ help: consider reducing it to: `*&x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:198:22
+   |
+LL |         let _: i32 = **&&x + 0;
+   |                      ^^^^^^^^^ help: consider reducing it to: `**&&x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:200:22
+   |
+LL |         let _: i32 = *&*&x + 0;
+   |                      ^^^^^^^^^ help: consider reducing it to: `*&*&x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:202:22
+   |
+LL |         let _: i32 = **&&*&x + 0;
+   |                      ^^^^^^^^^^^ help: consider reducing it to: `**&&*&x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:209:22
+   |
+LL |         let _: i32 = **&&*&x + 0;
+   |                      ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
+
+error: this operation has no effect
+  --> $DIR/identity_op.rs:211:22
+   |
+LL |         let _: i32 = **&&*&x + 0;
+   |                      ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
+
+error: aborting due to 52 previous errors