about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-08 15:20:24 +0000
committerbors <bors@rust-lang.org>2022-08-08 15:20:24 +0000
commit4912c0ece4a7082bd6fda38fe0ea1de4335ce2a7 (patch)
tree02b6aa25592d003805f89864e72a35389fe83165
parent0ee702514e482ee7901fba03d03dc9bd68809c92 (diff)
parentecb51fe6a55f975f2d3bfd65d3568c263c248460 (diff)
downloadrust-4912c0ece4a7082bd6fda38fe0ea1de4335ce2a7.tar.gz
rust-4912c0ece4a7082bd6fda38fe0ea1de4335ce2a7.zip
Auto merge of #9126 - Jarcho:auto_deref_sugg, r=Manishearth
`explicit_auto_deref` changes

fixes #9123
fixes #9109
fixes #9143
fixes #9101

This avoid suggesting code which hits a rustc bug. Basically `&{x}` won't use auto-deref if the target type is `Sized`.

changelog: Don't suggest using auto deref for block expressions when the target type is `Sized`
changelog: Include the borrow in the suggestion for `explicit_auto_deref`
changelog: Don't lint `explicit_auto_deref` on `dyn Trait` return
changelog: Don't lint `explicit_auto_deref` when other adjustments are required
changelog: Lint `explicit_auto_deref` in implicit return positions for closures
-rw-r--r--clippy_lints/src/dereference.rs334
-rw-r--r--clippy_utils/src/ty.rs54
-rw-r--r--tests/ui/explicit_auto_deref.fixed51
-rw-r--r--tests/ui/explicit_auto_deref.rs51
-rw-r--r--tests/ui/explicit_auto_deref.stderr144
5 files changed, 436 insertions, 198 deletions
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index a90f894a7b1..5d41c63928d 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
+use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
 use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage};
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
@@ -15,9 +15,9 @@ use rustc_hir::{
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults};
+use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_clippy_lint! {
@@ -183,24 +183,24 @@ enum State {
     },
     DerefedBorrow(DerefedBorrow),
     ExplicitDeref {
-        // Span and id of the top-level deref expression if the parent expression is a borrow.
-        deref_span_id: Option<(Span, HirId)>,
+        mutability: Option<Mutability>,
     },
     ExplicitDerefField {
         name: Symbol,
     },
     Reborrow {
-        deref_span: Span,
-        deref_hir_id: HirId,
+        mutability: Mutability,
+    },
+    Borrow {
+        mutability: Mutability,
     },
-    Borrow,
 }
 
 // A reference operation considered by this lint pass
 enum RefOp {
     Method(Mutability),
     Deref,
-    AddrOf,
+    AddrOf(Mutability),
 }
 
 struct RefPat {
@@ -263,7 +263,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             ));
                         } else if position.is_deref_stable() {
                             self.state = Some((
-                                State::ExplicitDeref { deref_span_id: None },
+                                State::ExplicitDeref { mutability: None },
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
                         }
@@ -289,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             },
                         ));
                     },
-                    RefOp::AddrOf => {
+                    RefOp::AddrOf(mutability) => {
                         // Find the number of times the borrow is auto-derefed.
                         let mut iter = adjustments.iter();
                         let mut deref_count = 0usize;
@@ -357,9 +357,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                                 }),
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
-                        } else if position.is_deref_stable() {
+                        } else if position.is_deref_stable()
+                            // Auto-deref doesn't combine with other adjustments
+                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
+                            && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
+                        {
                             self.state = Some((
-                                State::Borrow,
+                                State::Borrow { mutability },
                                 StateData {
                                     span: expr.span,
                                     hir_id: expr.hir_id,
@@ -395,7 +399,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     data,
                 ));
             },
-            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
                 self.state = Some((
                     State::DerefedBorrow(DerefedBorrow {
                         count: state.count - 1,
@@ -404,12 +408,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     data,
                 ));
             },
-            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
                 let position = data.position;
                 report(cx, expr, State::DerefedBorrow(state), data);
                 if position.is_deref_stable() {
                     self.state = Some((
-                        State::Borrow,
+                        State::Borrow { mutability },
                         StateData {
                             span: expr.span,
                             hir_id: expr.hir_id,
@@ -430,43 +434,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     ));
                 } else if position.is_deref_stable() {
                     self.state = Some((
-                        State::ExplicitDeref { deref_span_id: None },
+                        State::ExplicitDeref { mutability: None },
                         StateData { span: expr.span, hir_id: expr.hir_id, position },
                     ));
                 }
             },
 
-            (Some((State::Borrow, data)), RefOp::Deref) => {
+            (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
                 if typeck.expr_ty(sub_expr).is_ref() {
-                    self.state = Some((
-                        State::Reborrow {
-                            deref_span: expr.span,
-                            deref_hir_id: expr.hir_id,
-                        },
-                        data,
-                    ));
+                    self.state = Some((State::Reborrow { mutability }, data));
                 } else {
                     self.state = Some((
                         State::ExplicitDeref {
-                            deref_span_id: Some((expr.span, expr.hir_id)),
+                            mutability: Some(mutability),
                         },
                         data,
                     ));
                 }
             },
-            (
-                Some((
-                    State::Reborrow {
-                        deref_span,
-                        deref_hir_id,
-                    },
-                    data,
-                )),
-                RefOp::Deref,
-            ) => {
+            (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
                 self.state = Some((
                     State::ExplicitDeref {
-                        deref_span_id: Some((deref_span, deref_hir_id)),
+                        mutability: Some(mutability),
                     },
                     data,
                 ));
@@ -573,7 +562,7 @@ fn try_parse_ref_op<'tcx>(
         ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
             return Some((RefOp::Deref, sub_expr));
         },
-        ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
+        ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
         _ => return None,
     };
     if tcx.is_diagnostic_item(sym::deref_method, def_id) {
@@ -609,18 +598,21 @@ enum Position {
     Postfix,
     Deref,
     /// Any other location which will trigger auto-deref to a specific time.
-    DerefStable(i8),
+    /// Contains the precedence of the parent expression and whether the target type is sized.
+    DerefStable(i8, bool),
     /// Any other location which will trigger auto-reborrowing.
+    /// Contains the precedence of the parent expression.
     ReborrowStable(i8),
+    /// Contains the precedence of the parent expression.
     Other(i8),
 }
 impl Position {
     fn is_deref_stable(self) -> bool {
-        matches!(self, Self::DerefStable(_))
+        matches!(self, Self::DerefStable(..))
     }
 
     fn is_reborrow_stable(self) -> bool {
-        matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
+        matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
     }
 
     fn can_auto_borrow(self) -> bool {
@@ -628,7 +620,7 @@ impl Position {
     }
 
     fn lint_explicit_deref(self) -> bool {
-        matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
+        matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
     }
 
     fn precedence(self) -> i8 {
@@ -639,7 +631,7 @@ impl Position {
             | Self::FieldAccess(_)
             | Self::Postfix => PREC_POSTFIX,
             Self::Deref => PREC_PREFIX,
-            Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
+            Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
         }
     }
 }
@@ -659,7 +651,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
         }
         match parent {
             Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
-                Some(binding_ty_auto_deref_stability(ty, precedence))
+                Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
             },
             Node::Item(&Item {
                 kind: ItemKind::Static(..) | ItemKind::Const(..),
@@ -680,11 +672,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                 ..
             }) if span.ctxt() == ctxt => {
                 let ty = cx.tcx.type_of(def_id);
-                Some(if ty.is_ref() {
-                    Position::DerefStable(precedence)
-                } else {
-                    Position::Other(precedence)
-                })
+                Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
             },
 
             Node::Item(&Item {
@@ -705,45 +693,38 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                 span,
                 ..
             }) if span.ctxt() == ctxt => {
-                let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
-                Some(if !output.is_ref() {
-                    Position::Other(precedence)
-                } else if output.has_placeholders() || output.has_opaque_types() {
-                    Position::ReborrowStable(precedence)
-                } else {
-                    Position::DerefStable(precedence)
-                })
+                let output = cx
+                    .tcx
+                    .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
+                Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
             },
 
             Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
                 ExprKind::Ret(_) => {
                     let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
                     Some(
-                        if let Node::Expr(Expr {
-                            kind: ExprKind::Closure(&Closure { fn_decl, .. }),
-                            ..
-                        }) = cx.tcx.hir().get(owner_id)
+                        if let Node::Expr(
+                            closure_expr @ Expr {
+                                kind: ExprKind::Closure(closure),
+                                ..
+                            },
+                        ) = cx.tcx.hir().get(owner_id)
                         {
-                            match fn_decl.output {
-                                FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
-                                FnRetTy::DefaultReturn(_) => Position::Other(precedence),
-                            }
+                            closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
                         } else {
                             let output = cx
                                 .tcx
-                                .fn_sig(cx.tcx.hir().local_def_id(owner_id))
-                                .skip_binder()
-                                .output();
-                            if !output.is_ref() {
-                                Position::Other(precedence)
-                            } else if output.has_placeholders() || output.has_opaque_types() {
-                                Position::ReborrowStable(precedence)
-                            } else {
-                                Position::DerefStable(precedence)
-                            }
+                                .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
+                            ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
                         },
                     )
                 },
+                ExprKind::Closure(closure) => Some(closure_result_position(
+                    cx,
+                    closure,
+                    cx.typeck_results().expr_ty(parent),
+                    precedence,
+                )),
                 ExprKind::Call(func, _) if func.hir_id == child_id => {
                     (child_id == e.hir_id).then_some(Position::Callee)
                 },
@@ -755,8 +736,9 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                     .map(|(hir_ty, ty)| match hir_ty {
                         // Type inference for closures can depend on how they're called. Only go by the explicit
                         // types here.
-                        Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
-                        None => param_auto_deref_stability(ty.skip_binder(), precedence),
+                        Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
+                        None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
+                            .position_for_arg(),
                     }),
                 ExprKind::MethodCall(_, args, _) => {
                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
@@ -797,7 +779,12 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                                 Position::MethodReceiver
                             }
                         } else {
-                            param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
+                            ty_auto_deref_stability(
+                                cx,
+                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
+                                precedence,
+                            )
+                            .position_for_arg()
                         }
                     })
                 },
@@ -808,7 +795,9 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                         .find(|f| f.expr.hir_id == child_id)
                         .zip(variant)
                         .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
-                        .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
+                        .map(|field| {
+                            ty_auto_deref_stability(cx, cx.tcx.type_of(field.did), precedence).position_for_arg()
+                        })
                 },
                 ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
                 ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
@@ -831,6 +820,26 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
     (position, adjustments)
 }
 
+fn closure_result_position<'tcx>(
+    cx: &LateContext<'tcx>,
+    closure: &'tcx Closure<'_>,
+    ty: Ty<'tcx>,
+    precedence: i8,
+) -> Position {
+    match closure.fn_decl.output {
+        FnRetTy::Return(hir_ty) => {
+            if let Some(sig) = ty_sig(cx, ty)
+                && let Some(output) = sig.output()
+            {
+                binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
+            } else {
+                Position::Other(precedence)
+            }
+        },
+        FnRetTy::DefaultReturn(_) => Position::Other(precedence),
+    }
+}
+
 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
 //
 // e.g.
@@ -840,7 +849,12 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
 //
 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
 // switching to auto-dereferencing.
-fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
+fn binding_ty_auto_deref_stability<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: &'tcx hir::Ty<'_>,
+    precedence: i8,
+    binder_args: &'tcx List<BoundVariableKind>,
+) -> Position {
     let TyKind::Rptr(_, ty) = &ty.kind else {
         return Position::Other(precedence);
     };
@@ -870,21 +884,33 @@ fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position
                 {
                     Position::ReborrowStable(precedence)
                 } else {
-                    Position::DerefStable(precedence)
+                    Position::DerefStable(
+                        precedence,
+                        cx.tcx
+                            .erase_late_bound_regions(Binder::bind_with_vars(
+                                cx.typeck_results().node_type(ty.ty.hir_id),
+                                binder_args,
+                            ))
+                            .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
+                    )
                 }
             },
-            TyKind::Slice(_)
-            | TyKind::Array(..)
-            | TyKind::BareFn(_)
-            | TyKind::Never
+            TyKind::Slice(_) => Position::DerefStable(precedence, false),
+            TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
+            TyKind::Never
             | TyKind::Tup(_)
-            | TyKind::Ptr(_)
-            | TyKind::TraitObject(..)
-            | TyKind::Path(_) => Position::DerefStable(precedence),
-            TyKind::OpaqueDef(..)
-            | TyKind::Infer
-            | TyKind::Typeof(..)
-            | TyKind::Err => Position::ReborrowStable(precedence),
+            | TyKind::Path(_) => Position::DerefStable(
+                precedence,
+                cx.tcx
+                    .erase_late_bound_regions(Binder::bind_with_vars(
+                        cx.typeck_results().node_type(ty.ty.hir_id),
+                        binder_args,
+                    ))
+                    .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
+            ),
+            TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
+                Position::ReborrowStable(precedence)
+            },
         };
     }
 }
@@ -920,10 +946,39 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
     v.0
 }
 
+struct TyPosition<'tcx> {
+    position: Position,
+    ty: Option<Ty<'tcx>>,
+}
+impl From<Position> for TyPosition<'_> {
+    fn from(position: Position) -> Self {
+        Self { position, ty: None }
+    }
+}
+impl<'tcx> TyPosition<'tcx> {
+    fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
+        Self {
+            position: Position::ReborrowStable(precedence),
+            ty: Some(ty),
+        }
+    }
+    fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
+        match (self.position, self.ty) {
+            (Position::ReborrowStable(precedence), Some(ty)) => {
+                Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
+            },
+            (position, _) => position,
+        }
+    }
+    fn position_for_arg(self) -> Position {
+        self.position
+    }
+}
+
 // Checks whether a type is stable when switching to auto dereferencing,
-fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
+fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
     let ty::Ref(_, mut ty, _) = *ty.kind() else {
-        return Position::Other(precedence);
+        return Position::Other(precedence).into();
     };
 
     loop {
@@ -932,35 +987,38 @@ fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
                 ty = ref_ty;
                 continue;
             },
-            ty::Infer(_)
-            | ty::Error(_)
-            | ty::Param(_)
-            | ty::Bound(..)
-            | ty::Opaque(..)
-            | ty::Placeholder(_)
-            | ty::Dynamic(..) => Position::ReborrowStable(precedence),
-            ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
-                Position::ReborrowStable(precedence)
+            ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
+            ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
+                Position::ReborrowStable(precedence).into()
             },
-            ty::Adt(..)
-            | ty::Bool
+            ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
+                Position::ReborrowStable(precedence).into()
+            },
+            ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
+                TyPosition::new_deref_stable_for_result(precedence, ty)
+            },
+            ty::Bool
             | ty::Char
             | ty::Int(_)
             | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Foreign(_)
-            | ty::Str
             | ty::Array(..)
-            | ty::Slice(..)
+            | ty::Float(_)
             | ty::RawPtr(..)
+            | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
+            ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
+            ty::Adt(..)
+            | ty::Foreign(_)
             | ty::FnDef(..)
-            | ty::FnPtr(_)
-            | ty::Closure(..)
             | ty::Generator(..)
             | ty::GeneratorWitness(..)
+            | ty::Closure(..)
             | ty::Never
             | ty::Tuple(_)
-            | ty::Projection(_) => Position::DerefStable(precedence),
+            | ty::Projection(_) => Position::DerefStable(
+                precedence,
+                ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
+            )
+            .into(),
         };
     }
 }
@@ -1040,34 +1098,64 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
                 diag.span_suggestion(data.span, "change this to", sugg, app);
             });
         },
-        State::ExplicitDeref { deref_span_id } => {
-            let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
+        State::ExplicitDeref { mutability } => {
+            if matches!(
+                expr.kind,
+                ExprKind::Block(..)
+                    | ExprKind::ConstBlock(_)
+                    | ExprKind::If(..)
+                    | ExprKind::Loop(..)
+                    | ExprKind::Match(..)
+            ) && matches!(data.position, Position::DerefStable(_, true))
+            {
+                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
+                return;
+            }
+
+            let (prefix, precedence) = if let Some(mutability) = mutability
                 && !cx.typeck_results().expr_ty(expr).is_ref()
             {
-                (span, hir_id, PREC_PREFIX)
+                let prefix = match mutability {
+                    Mutability::Not => "&",
+                    Mutability::Mut => "&mut ",
+                };
+                (prefix, 0)
             } else {
-                (data.span, data.hir_id, data.position.precedence())
+                ("", data.position.precedence())
             };
             span_lint_hir_and_then(
                 cx,
                 EXPLICIT_AUTO_DEREF,
-                hir_id,
-                span,
+                data.hir_id,
+                data.span,
                 "deref which would be done by auto-deref",
                 |diag| {
                     let mut app = Applicability::MachineApplicable;
-                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
+                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
                     let sugg =
                         if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
-                            format!("({})", snip)
+                            format!("{}({})", prefix, snip)
                         } else {
-                            snip.into()
+                            format!("{}{}", prefix, snip)
                         };
-                    diag.span_suggestion(span, "try this", sugg, app);
+                    diag.span_suggestion(data.span, "try this", sugg, app);
                 },
             );
         },
         State::ExplicitDerefField { .. } => {
+            if matches!(
+                expr.kind,
+                ExprKind::Block(..)
+                    | ExprKind::ConstBlock(_)
+                    | ExprKind::If(..)
+                    | ExprKind::Loop(..)
+                    | ExprKind::Match(..)
+            ) && matches!(data.position, Position::DerefStable(_, true))
+            {
+                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
+                return;
+            }
+
             span_lint_hir_and_then(
                 cx,
                 EXPLICIT_AUTO_DEREF,
@@ -1081,7 +1169,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
                 },
             );
         },
-        State::Borrow | State::Reborrow { .. } => (),
+        State::Borrow { .. } | State::Reborrow { .. } => (),
     }
 }
 
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index a05d633d980..e7d670766a0 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -503,7 +503,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
 pub enum ExprFnSig<'tcx> {
     Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
     Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
-    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
+    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
 }
 impl<'tcx> ExprFnSig<'tcx> {
     /// Gets the argument type at the given offset. This will return `None` when the index is out of
@@ -518,7 +518,7 @@ impl<'tcx> ExprFnSig<'tcx> {
                 }
             },
             Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
-            Self::Trait(inputs, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
+            Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
         }
     }
 
@@ -541,7 +541,7 @@ impl<'tcx> ExprFnSig<'tcx> {
                 decl.and_then(|decl| decl.inputs.get(i)),
                 sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
             )),
-            Self::Trait(inputs, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
+            Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
         }
     }
 
@@ -550,12 +550,16 @@ impl<'tcx> ExprFnSig<'tcx> {
     pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
         match self {
             Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
-            Self::Trait(_, output) => output,
+            Self::Trait(_, output, _) => output,
         }
     }
 
     pub fn predicates_id(&self) -> Option<DefId> {
-        if let ExprFnSig::Sig(_, id) = *self { id } else { None }
+        if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
+            id
+        } else {
+            None
+        }
     }
 }
 
@@ -568,7 +572,8 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
     }
 }
 
-fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+/// If the type is function like, get the signature for it.
+pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
     if ty.is_box() {
         return ty_sig(cx, ty.boxed_ty());
     }
@@ -580,7 +585,7 @@ fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>>
             Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
         },
         ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
-        ty::Opaque(id, _) => ty_sig(cx, cx.tcx.type_of(id)),
+        ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
         ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
         ty::Dynamic(bounds, _) => {
             let lang_items = cx.tcx.lang_items();
@@ -594,26 +599,31 @@ fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>>
                         .projection_bounds()
                         .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
                         .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
-                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
+                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
                 },
                 _ => None,
             }
         },
         ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
             Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
-            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
+            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
         },
-        ty::Param(_) => sig_from_bounds(cx, ty),
+        ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
         _ => None,
     }
 }
 
-fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+fn sig_from_bounds<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: Ty<'tcx>,
+    predicates: &'tcx [Predicate<'tcx>],
+    predicates_id: Option<DefId>,
+) -> Option<ExprFnSig<'tcx>> {
     let mut inputs = None;
     let mut output = None;
     let lang_items = cx.tcx.lang_items();
 
-    for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
+    for pred in predicates {
         match pred.kind().skip_binder() {
             PredicateKind::Trait(p)
                 if (lang_items.fn_trait() == Some(p.def_id())
@@ -621,11 +631,12 @@ fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnS
                     || lang_items.fn_once_trait() == Some(p.def_id()))
                     && p.self_ty() == ty =>
             {
-                if inputs.is_some() {
+                let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
+                if inputs.map_or(false, |inputs| i != inputs) {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
                 }
-                inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
+                inputs = Some(i);
             },
             PredicateKind::Projection(p)
                 if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
@@ -641,7 +652,7 @@ fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnS
         }
     }
 
-    inputs.map(|ty| ExprFnSig::Trait(ty, output))
+    inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
 }
 
 fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
@@ -661,14 +672,15 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
                     || lang_items.fn_mut_trait() == Some(p.def_id())
                     || lang_items.fn_once_trait() == Some(p.def_id())) =>
             {
-                if inputs.is_some() {
+                let i = pred
+                    .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
+                    .subst(cx.tcx, ty.substs);
+
+                if inputs.map_or(false, |inputs| inputs != i) {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
                 }
-                inputs = Some(
-                    pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
-                        .subst(cx.tcx, ty.substs),
-                );
+                inputs = Some(i);
             },
             PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
                 if output.is_some() {
@@ -684,7 +696,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
         }
     }
 
-    inputs.map(|ty| ExprFnSig::Trait(ty, output))
+    inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
 }
 
 #[derive(Clone, Copy)]
diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed
index a650fdc1f89..d1d35e5c0eb 100644
--- a/tests/ui/explicit_auto_deref.fixed
+++ b/tests/ui/explicit_auto_deref.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(closure_lifetime_binder)]
 #![warn(clippy::explicit_auto_deref)]
 #![allow(
     dead_code,
@@ -67,6 +68,8 @@ fn main() {
     let s = String::new();
 
     let _: &str = &s;
+    let _: &str = &{ String::new() };
+    let _: &str = &mut { String::new() };
     let _ = &*s; // Don't lint. Inferred type would change.
     let _: &_ = &*s; // Don't lint. Inferred type would change.
 
@@ -215,4 +218,52 @@ fn main() {
     let s = &"str";
     let _ = || return *s;
     let _ = || -> &'static str { return s };
+
+    struct X;
+    struct Y(X);
+    impl core::ops::Deref for Y {
+        type Target = X;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    let _: &X = &*{ Y(X) };
+    let _: &X = &*match 0 {
+        #[rustfmt::skip]
+        0 => { Y(X) },
+        _ => panic!(),
+    };
+    let _: &X = &*if true { Y(X) } else { panic!() };
+
+    fn deref_to_u<U, T: core::ops::Deref<Target = U>>(x: &T) -> &U {
+        x
+    }
+
+    let _ = |x: &'static Box<dyn Iterator<Item = u32>>| -> &'static dyn Iterator<Item = u32> { &**x };
+    fn ret_any(x: &Box<dyn std::any::Any>) -> &dyn std::any::Any {
+        &**x
+    }
+
+    let x = String::new();
+    let _: *const str = &*x;
+
+    struct S7([u32; 1]);
+    impl core::ops::Deref for S7 {
+        type Target = [u32; 1];
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    let x = S7([0]);
+    let _: &[u32] = &*x;
+
+    let c1 = |_: &Vec<&u32>| {};
+    let x = &&vec![&1u32];
+    c1(x);
+    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
+        if b {
+            return x;
+        }
+        x
+    };
 }
diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs
index 8f4f352576a..deedafad153 100644
--- a/tests/ui/explicit_auto_deref.rs
+++ b/tests/ui/explicit_auto_deref.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(closure_lifetime_binder)]
 #![warn(clippy::explicit_auto_deref)]
 #![allow(
     dead_code,
@@ -67,6 +68,8 @@ fn main() {
     let s = String::new();
 
     let _: &str = &*s;
+    let _: &str = &*{ String::new() };
+    let _: &str = &mut *{ String::new() };
     let _ = &*s; // Don't lint. Inferred type would change.
     let _: &_ = &*s; // Don't lint. Inferred type would change.
 
@@ -215,4 +218,52 @@ fn main() {
     let s = &"str";
     let _ = || return *s;
     let _ = || -> &'static str { return *s };
+
+    struct X;
+    struct Y(X);
+    impl core::ops::Deref for Y {
+        type Target = X;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    let _: &X = &*{ Y(X) };
+    let _: &X = &*match 0 {
+        #[rustfmt::skip]
+        0 => { Y(X) },
+        _ => panic!(),
+    };
+    let _: &X = &*if true { Y(X) } else { panic!() };
+
+    fn deref_to_u<U, T: core::ops::Deref<Target = U>>(x: &T) -> &U {
+        &**x
+    }
+
+    let _ = |x: &'static Box<dyn Iterator<Item = u32>>| -> &'static dyn Iterator<Item = u32> { &**x };
+    fn ret_any(x: &Box<dyn std::any::Any>) -> &dyn std::any::Any {
+        &**x
+    }
+
+    let x = String::new();
+    let _: *const str = &*x;
+
+    struct S7([u32; 1]);
+    impl core::ops::Deref for S7 {
+        type Target = [u32; 1];
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    let x = S7([0]);
+    let _: &[u32] = &*x;
+
+    let c1 = |_: &Vec<&u32>| {};
+    let x = &&vec![&1u32];
+    c1(*x);
+    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
+        if b {
+            return *x;
+        }
+        *x
+    };
 }
diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr
index 92765307ea7..91863abcc5d 100644
--- a/tests/ui/explicit_auto_deref.stderr
+++ b/tests/ui/explicit_auto_deref.stderr
@@ -1,202 +1,238 @@
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:69:20
+  --> $DIR/explicit_auto_deref.rs:70:19
    |
 LL |     let _: &str = &*s;
-   |                    ^^ help: try this: `s`
+   |                   ^^^ help: try this: `&s`
    |
    = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:73:12
+  --> $DIR/explicit_auto_deref.rs:71:19
+   |
+LL |     let _: &str = &*{ String::new() };
+   |                   ^^^^^^^^^^^^^^^^^^^ help: try this: `&{ String::new() }`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:72:19
+   |
+LL |     let _: &str = &mut *{ String::new() };
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&mut { String::new() }`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:76:11
    |
 LL |     f_str(&*s);
-   |            ^^ help: try this: `s`
+   |           ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:77:14
+  --> $DIR/explicit_auto_deref.rs:80:13
    |
 LL |     f_str_t(&*s, &*s); // Don't lint second param.
-   |              ^^ help: try this: `s`
+   |             ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:80:25
+  --> $DIR/explicit_auto_deref.rs:83:24
    |
 LL |     let _: &Box<i32> = &**b;
-   |                         ^^^ help: try this: `b`
+   |                        ^^^^ help: try this: `&b`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:86:8
+  --> $DIR/explicit_auto_deref.rs:89:7
    |
 LL |     c(&*s);
-   |        ^^ help: try this: `s`
+   |       ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:92:9
+  --> $DIR/explicit_auto_deref.rs:95:9
    |
 LL |         &**x
    |         ^^^^ help: try this: `x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:96:11
+  --> $DIR/explicit_auto_deref.rs:99:11
    |
 LL |         { &**x }
    |           ^^^^ help: try this: `x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:100:9
+  --> $DIR/explicit_auto_deref.rs:103:9
    |
 LL |         &**{ x }
    |         ^^^^^^^^ help: try this: `{ x }`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:104:9
+  --> $DIR/explicit_auto_deref.rs:107:9
    |
 LL |         &***x
    |         ^^^^^ help: try this: `x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:121:13
+  --> $DIR/explicit_auto_deref.rs:124:12
    |
 LL |         f1(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:122:13
+  --> $DIR/explicit_auto_deref.rs:125:12
    |
 LL |         f2(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:123:13
+  --> $DIR/explicit_auto_deref.rs:126:12
    |
 LL |         f3(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:124:28
+  --> $DIR/explicit_auto_deref.rs:127:27
    |
 LL |         f4.callable_str()(&*x);
-   |                            ^^ help: try this: `x`
+   |                           ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:125:13
+  --> $DIR/explicit_auto_deref.rs:128:12
    |
 LL |         f5(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:126:13
+  --> $DIR/explicit_auto_deref.rs:129:12
    |
 LL |         f6(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:127:28
+  --> $DIR/explicit_auto_deref.rs:130:27
    |
 LL |         f7.callable_str()(&*x);
-   |                            ^^ help: try this: `x`
+   |                           ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:128:26
+  --> $DIR/explicit_auto_deref.rs:131:25
    |
 LL |         f8.callable_t()(&*x);
-   |                          ^^ help: try this: `x`
+   |                         ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:129:13
+  --> $DIR/explicit_auto_deref.rs:132:12
    |
 LL |         f9(&*x);
-   |             ^^ help: try this: `x`
+   |            ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:130:14
+  --> $DIR/explicit_auto_deref.rs:133:13
    |
 LL |         f10(&*x);
-   |              ^^ help: try this: `x`
+   |             ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:131:27
+  --> $DIR/explicit_auto_deref.rs:134:26
    |
 LL |         f11.callable_t()(&*x);
-   |                           ^^ help: try this: `x`
+   |                          ^^^ help: try this: `&x`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:135:17
+  --> $DIR/explicit_auto_deref.rs:138:16
    |
 LL |     let _ = S1(&*s);
-   |                 ^^ help: try this: `s`
+   |                ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:140:22
+  --> $DIR/explicit_auto_deref.rs:143:21
    |
 LL |     let _ = S2 { s: &*s };
-   |                      ^^ help: try this: `s`
+   |                     ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:156:30
+  --> $DIR/explicit_auto_deref.rs:159:30
    |
 LL |             let _ = Self::S1(&**s);
    |                              ^^^^ help: try this: `s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:157:35
+  --> $DIR/explicit_auto_deref.rs:160:35
    |
 LL |             let _ = Self::S2 { s: &**s };
    |                                   ^^^^ help: try this: `s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:160:21
+  --> $DIR/explicit_auto_deref.rs:163:20
    |
 LL |     let _ = E1::S1(&*s);
-   |                     ^^ help: try this: `s`
+   |                    ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:161:26
+  --> $DIR/explicit_auto_deref.rs:164:25
    |
 LL |     let _ = E1::S2 { s: &*s };
-   |                          ^^ help: try this: `s`
+   |                         ^^^ help: try this: `&s`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:179:13
+  --> $DIR/explicit_auto_deref.rs:182:13
    |
 LL |     let _ = (*b).foo;
    |             ^^^^ help: try this: `b`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:180:13
+  --> $DIR/explicit_auto_deref.rs:183:13
    |
 LL |     let _ = (**b).foo;
    |             ^^^^^ help: try this: `b`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:195:19
+  --> $DIR/explicit_auto_deref.rs:198:19
    |
 LL |     let _ = f_str(*ref_str);
    |                   ^^^^^^^^ help: try this: `ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:197:19
+  --> $DIR/explicit_auto_deref.rs:200:19
    |
 LL |     let _ = f_str(**ref_ref_str);
    |                   ^^^^^^^^^^^^^ help: try this: `ref_ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:207:13
+  --> $DIR/explicit_auto_deref.rs:210:13
    |
 LL |     f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
    |             ^^^^^^^^ help: try this: `ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:208:12
+  --> $DIR/explicit_auto_deref.rs:211:12
    |
 LL |     f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
    |            ^^^^^^^^^^ help: try this: `ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:217:41
+  --> $DIR/explicit_auto_deref.rs:220:41
    |
 LL |     let _ = || -> &'static str { return *s };
    |                                         ^^ help: try this: `s`
 
-error: aborting due to 33 previous errors
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:239:9
+   |
+LL |         &**x
+   |         ^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:262:8
+   |
+LL |     c1(*x);
+   |        ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:265:20
+   |
+LL |             return *x;
+   |                    ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:267:9
+   |
+LL |         *x
+   |         ^^ help: try this: `x`
+
+error: aborting due to 39 previous errors