about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/dereference.rs37
-rw-r--r--clippy_utils/src/lib.rs11
-rw-r--r--tests/ui/explicit_auto_deref.fixed36
-rw-r--r--tests/ui/explicit_auto_deref.rs36
-rw-r--r--tests/ui/explicit_auto_deref.stderr8
5 files changed, 116 insertions, 12 deletions
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index a115f8d0631..253f9959e13 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
 use clippy_utils::{
-    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
+    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
+    ExprUseNode,
 };
 use core::mem;
 use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
@@ -175,6 +176,7 @@ struct StateData<'tcx> {
     adjusted_ty: Ty<'tcx>,
 }
 
+#[derive(Debug)]
 struct DerefedBorrow {
     count: usize,
     msg: &'static str,
@@ -182,6 +184,7 @@ struct DerefedBorrow {
     for_field_access: Option<Symbol>,
 }
 
+#[derive(Debug)]
 enum State {
     // Any number of deref method calls.
     DerefMethod {
@@ -744,7 +747,7 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo
     }
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 enum TyCoercionStability {
     Deref,
     Reborrow,
@@ -1042,16 +1045,28 @@ fn report<'tcx>(
                 return;
             }
 
-            let (prefix, precedence) = if let Some(mutability) = mutability
-                && !typeck.expr_ty(expr).is_ref()
+            let ty = typeck.expr_ty(expr);
+
+            // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst).
+            if let ty::Ref(_, dst, _) = data.adjusted_ty.kind()
+                && dst.is_slice()
             {
-                let prefix = match mutability {
-                    Mutability::Not => "&",
-                    Mutability::Mut => "&mut ",
-                };
-                (prefix, PREC_PREFIX)
-            } else {
-                ("", 0)
+                let (src, n_src_refs) = peel_middle_ty_refs(ty);
+                if n_src_refs >= 2 && src.is_array() {
+                    return;
+                }
+            }
+
+            let (prefix, precedence) = match mutability {
+                Some(mutability) if !ty.is_ref() => {
+                    let prefix = match mutability {
+                        Mutability::Not => "&",
+                        Mutability::Mut => "&mut ",
+                    };
+                    (prefix, PREC_PREFIX)
+                },
+                None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0),
+                _ => ("", 0),
             };
             span_lint_hir_and_then(
                 cx,
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 8a6750c8997..45a31fe442b 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -2492,6 +2492,17 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize)
     }
 }
 
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
+    let mut count = 0;
+    while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
+        ty = *dest_ty;
+        count += 1;
+    }
+    (ty, count)
+}
+
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed
index e6ca4bb66cc..255b2c5a220 100644
--- a/tests/ui/explicit_auto_deref.fixed
+++ b/tests/ui/explicit_auto_deref.fixed
@@ -345,3 +345,39 @@ fn main() {
         let _ = &mut ({ *x.u }).x;
     }
 }
+
+mod issue_12969 {
+    use std::ops::Deref;
+
+    struct Wrapper<T>(T);
+
+    impl<T> Deref for Wrapper<T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    fn foo(_bar: &str) {}
+
+    fn bar() {
+        let wrapped_bar = Wrapper("");
+
+        foo(&wrapped_bar);
+    }
+}
+
+mod issue_9841 {
+    fn takes_array_ref<T, const N: usize>(array: &&[T; N]) {
+        takes_slice(*array)
+    }
+
+    fn takes_array_ref_ref<T, const N: usize>(array: &&&[T; N]) {
+        takes_slice(**array)
+    }
+
+    fn takes_slice<T>(slice: &[T]) {
+        todo!()
+    }
+}
diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs
index 7531e1f87b7..99906999f01 100644
--- a/tests/ui/explicit_auto_deref.rs
+++ b/tests/ui/explicit_auto_deref.rs
@@ -345,3 +345,39 @@ fn main() {
         let _ = &mut ({ *x.u }).x;
     }
 }
+
+mod issue_12969 {
+    use std::ops::Deref;
+
+    struct Wrapper<T>(T);
+
+    impl<T> Deref for Wrapper<T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    fn foo(_bar: &str) {}
+
+    fn bar() {
+        let wrapped_bar = Wrapper("");
+
+        foo(&*wrapped_bar);
+    }
+}
+
+mod issue_9841 {
+    fn takes_array_ref<T, const N: usize>(array: &&[T; N]) {
+        takes_slice(*array)
+    }
+
+    fn takes_array_ref_ref<T, const N: usize>(array: &&&[T; N]) {
+        takes_slice(**array)
+    }
+
+    fn takes_slice<T>(slice: &[T]) {
+        todo!()
+    }
+}
diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr
index 56a183de348..53784934f63 100644
--- a/tests/ui/explicit_auto_deref.stderr
+++ b/tests/ui/explicit_auto_deref.stderr
@@ -271,5 +271,11 @@ error: deref which would be done by auto-deref
 LL |         let _ = &mut (*{ x.u }).x;
    |                      ^^^^^^^^^^ help: try: `{ x.u }`
 
-error: aborting due to 45 previous errors
+error: deref which would be done by auto-deref
+  --> tests/ui/explicit_auto_deref.rs:367:13
+   |
+LL |         foo(&*wrapped_bar);
+   |             ^^^^^^^^^^^^^ help: try: `&wrapped_bar`
+
+error: aborting due to 46 previous errors