diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 4c1ca75e0db..164d51ecb75 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ 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_POSTFIX, PREC_PREFIX}; @@ -1044,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 fda870f435a..89686916671 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2471,6 +2471,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 22bc0949659..255b2c5a220 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -364,13 +364,17 @@ mod issue_12969 { fn bar() { let wrapped_bar = Wrapper(""); - foo(wrapped_bar); + foo(&wrapped_bar); } } mod issue_9841 { fn takes_array_ref(array: &&[T; N]) { - takes_slice(array) + takes_slice(*array) + } + + fn takes_array_ref_ref(array: &&&[T; N]) { + takes_slice(**array) } fn takes_slice(slice: &[T]) { diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index 2b3a28f8b6f..99906999f01 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -373,6 +373,10 @@ mod issue_9841 { takes_slice(*array) } + fn takes_array_ref_ref(array: &&&[T; N]) { + takes_slice(**array) + } + fn takes_slice(slice: &[T]) { todo!() } diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index 45059267362..53784934f63 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -275,13 +275,7 @@ 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` + | ^^^^^^^^^^^^^ help: try: `&wrapped_bar` -error: deref which would be done by auto-deref - --> tests/ui/explicit_auto_deref.rs:373:21 - | -LL | takes_slice(*array) - | ^^^^^^ help: try: `array` - -error: aborting due to 47 previous errors +error: aborting due to 46 previous errors