1 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
2 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
3 use clippy_utils::sugg::has_enclosing_paren;
4 use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
5 use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, walk_to_expr_usage};
6 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
7 use rustc_data_structures::fx::FxIndexMap;
8 use rustc_errors::Applicability;
10 self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem,
11 ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
12 TraitItemKind, TyKind, UnOp,
14 use rustc_lint::{LateContext, LateLintPass};
15 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
16 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
17 use rustc_session::{declare_tool_lint, impl_lint_pass};
18 use rustc_span::{symbol::sym, Span, Symbol, SyntaxContext};
20 declare_clippy_lint! {
22 /// Checks for explicit `deref()` or `deref_mut()` method calls.
24 /// ### Why is this bad?
25 /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
26 /// when not part of a method chain.
30 /// use std::ops::Deref;
31 /// let a: &mut String = &mut String::from("foo");
32 /// let b: &str = a.deref();
37 /// let a: &mut String = &mut String::from("foo");
41 /// This lint excludes:
43 /// let _ = d.unwrap().deref();
45 #[clippy::version = "1.44.0"]
46 pub EXPLICIT_DEREF_METHODS,
48 "Explicit use of deref or deref_mut method while not in a method chain."
51 declare_clippy_lint! {
53 /// Checks for address of operations (`&`) that are going to
54 /// be dereferenced immediately by the compiler.
56 /// ### Why is this bad?
57 /// Suggests that the receiver of the expression borrows
62 /// fn fun(_a: &i32) {}
64 /// let x: &i32 = &&&&&&5;
70 /// # fn fun(_a: &i32) {}
74 #[clippy::version = "pre 1.29.0"]
77 "taking a reference that is going to be automatically dereferenced"
80 declare_clippy_lint! {
82 /// Checks for `ref` bindings which create a reference to a reference.
84 /// ### Why is this bad?
85 /// The address-of operator at the use site is clearer about the need for a reference.
90 /// if let Some(ref x) = x {
98 /// if let Some(x) = x {
102 #[clippy::version = "1.54.0"]
103 pub REF_BINDING_TO_REFERENCE,
105 "`ref` binding to a reference"
108 declare_clippy_lint! {
110 /// Checks for dereferencing expressions which would be covered by auto-deref.
112 /// ### Why is this bad?
113 /// This unnecessarily complicates the code.
117 /// let x = String::new();
118 /// let y: &str = &*x;
122 /// let x = String::new();
123 /// let y: &str = &x;
125 #[clippy::version = "1.60.0"]
126 pub EXPLICIT_AUTO_DEREF,
128 "dereferencing when the compiler would automatically dereference"
131 impl_lint_pass!(Dereferencing => [
132 EXPLICIT_DEREF_METHODS,
134 REF_BINDING_TO_REFERENCE,
139 pub struct Dereferencing {
140 state: Option<(State, StateData)>,
142 // While parsing a `deref` method call in ufcs form, the path to the function is itself an
143 // expression. This is to store the id of that expression so it can be skipped when
144 // `check_expr` is called for it.
145 skip_expr: Option<HirId>,
147 /// The body the first local was found in. Used to emit lints when the traversal of the body has
148 /// been finished. Note we can't lint at the end of every body as they can be nested within each
150 current_body: Option<BodyId>,
151 /// The list of locals currently being checked by the lint.
152 /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
153 /// This is needed for or patterns where one of the branches can be linted, but another can not
156 /// e.g. `m!(x) | Foo::Bar(ref x)`
157 ref_locals: FxIndexMap<HirId, Option<RefPat>>,
161 /// Span of the top level expression
167 // Any number of deref method calls.
169 // The number of calls in a sequence which changed the referenced type
170 ty_changed_count: usize,
172 /// The required mutability
173 target_mut: Mutability,
177 required_precedence: i8,
194 // A reference operation considered by this lint pass
202 /// Whether every usage of the binding is dereferenced.
204 /// The spans of all the ref bindings for this local.
206 /// The applicability of this suggestion.
208 /// All the replacements which need to be made.
209 replacements: Vec<(Span, String)>,
210 /// The [`HirId`] that the lint should be emitted at.
214 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
215 #[expect(clippy::too_many_lines)]
216 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
217 // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
218 if Some(expr.hir_id) == self.skip_expr.take() {
222 if let Some(local) = path_to_local(expr) {
223 self.check_local_usage(cx, expr, local);
226 // Stop processing sub expressions when a macro call is seen
227 if expr.span.from_expansion() {
228 if let Some((state, data)) = self.state.take() {
229 report(cx, expr, state, data);
234 let typeck = cx.typeck_results();
235 let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
238 // The whole chain of reference operations has been seen
239 if let Some((state, data)) = self.state.take() {
240 report(cx, expr, state, data);
245 match (self.state.take(), kind) {
247 let expr_ty = typeck.expr_ty(expr);
248 let (position, parent_ctxt) = get_expr_position(cx, expr);
249 let (stability, adjustments) = walk_parents(cx, expr);
253 if let Position::FieldAccess(name) = position
254 && !ty_contains_field(typeck.expr_ty(sub_expr), name)
257 State::ExplicitDerefField { name },
258 StateData { span: expr.span, hir_id: expr.hir_id },
260 } else if stability.is_deref_stable() {
262 State::ExplicitDeref { deref_span: expr.span, deref_hir_id: expr.hir_id },
263 StateData { span: expr.span, hir_id: expr.hir_id },
267 RefOp::Method(target_mut)
268 if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
269 && (position.lint_explicit_deref() || parent_ctxt != expr.span.ctxt()) =>
273 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
278 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
288 // Find the number of times the borrow is auto-derefed.
289 let mut iter = adjustments.iter();
290 let mut deref_count = 0usize;
291 let next_adjust = loop {
294 if !matches!(adjust.kind, Adjust::Deref(_)) {
296 } else if !adjust.target.is_ref() {
306 // Determine the required number of references before any can be removed. In all cases the
307 // reference made by the current expression will be removed. After that there are four cases to
310 // 1. Auto-borrow will trigger in the current position, so no further references are required.
311 // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
312 // handle the automatically inserted re-borrow.
313 // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
315 // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
316 // adjustments will not be inserted automatically, then leave one further reference to avoid
317 // moving a mutable borrow.
319 // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
321 // // Removing the borrow will cause `x` to be moved
322 // Some(x) => &mut *x,
327 "this expression creates a reference which is immediately dereferenced by the compiler";
328 let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
330 let (required_refs, required_precedence, msg) = if position.can_auto_borrow() {
331 (1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
332 } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
333 next_adjust.map(|a| &a.kind)
335 if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !stability.is_reborrow_stable()
345 if deref_count >= required_refs {
347 State::DerefedBorrow {
348 // One of the required refs is for the current borrow expression, the remaining ones
349 // can't be removed without breaking the code. See earlier comment.
350 count: deref_count - required_refs,
359 } else if stability.is_deref_stable() {
369 RefOp::Method(..) => (),
385 ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
390 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
398 State::DerefedBorrow {
408 State::DerefedBorrow {
416 (Some((State::Borrow, data)), RefOp::Deref) => {
417 if typeck.expr_ty(sub_expr).is_ref() {
420 deref_span: expr.span,
421 deref_hir_id: expr.hir_id,
427 State::ExplicitDeref {
428 deref_span: expr.span,
429 deref_hir_id: expr.hir_id,
446 State::ExplicitDeref {
453 (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
456 (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
457 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
459 self.state = Some((State::ExplicitDerefField { name }, data));
462 (Some((state, data)), _) => report(cx, expr, state, data),
466 fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
467 if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
468 if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
469 // This binding id has been seen before. Add this pattern to the list of changes.
470 if let Some(prev_pat) = opt_prev_pat {
471 if pat.span.from_expansion() {
472 // Doesn't match the context of the previous pattern. Can't lint here.
473 *opt_prev_pat = None;
475 prev_pat.spans.push(pat.span);
476 prev_pat.replacements.push((
478 snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
488 if !pat.span.from_expansion();
489 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
490 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
491 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
493 let mut app = Applicability::MachineApplicable;
494 let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
495 self.current_body = self.current_body.or(cx.enclosing_body);
496 self.ref_locals.insert(
500 spans: vec![pat.span],
502 replacements: vec![(pat.span, snip.into())],
511 fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
512 if Some(body.id()) == self.current_body {
513 for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
514 let replacements = pat.replacements;
516 let lint = if pat.always_deref {
519 REF_BINDING_TO_REFERENCE
521 span_lint_hir_and_then(
526 "this pattern creates a reference to a reference",
528 diag.multipart_suggestion("try this", replacements, app);
532 self.current_body = None;
537 fn try_parse_ref_op<'tcx>(
539 typeck: &'tcx TypeckResults<'_>,
540 expr: &'tcx Expr<'_>,
541 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
542 let (def_id, arg) = match expr.kind {
543 ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
546 kind: ExprKind::Path(path),
551 ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
552 ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
553 return Some((RefOp::Deref, sub_expr));
555 ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
558 if tcx.is_diagnostic_item(sym::deref_method, def_id) {
559 Some((RefOp::Method(Mutability::Not), arg))
560 } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
561 Some((RefOp::Method(Mutability::Mut), arg))
567 // Checks whether the type for a deref call actually changed the type, not just the mutability of
569 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
570 match (result_ty.kind(), arg_ty.kind()) {
571 (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
573 // The result type for a deref method is always a reference
574 // Not matching the previous pattern means the argument type is not a reference
575 // This means that the type did change
580 #[derive(Clone, Copy)]
590 fn can_auto_borrow(self) -> bool {
591 matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
594 fn lint_explicit_deref(self) -> bool {
595 matches!(self, Self::Other)
599 /// Get which position an expression is in relative to it's parent.
600 fn get_expr_position(cx: &LateContext<'_>, e: &Expr<'_>) -> (Position, SyntaxContext) {
601 if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, e.hir_id) {
602 let pos = match parent.kind {
603 ExprKind::MethodCall(_, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => Position::MethodReceiver,
604 ExprKind::Field(_, name) => Position::FieldAccess(name.name),
605 ExprKind::Call(f, _) if f.hir_id == e.hir_id => Position::Callee,
606 ExprKind::Unary(UnOp::Deref, _) => Position::Deref,
607 ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) | ExprKind::Index(..) => {
610 _ => Position::Other,
612 (pos, parent.span.ctxt())
614 (Position::Other, SyntaxContext::root())
618 /// How stable the result of auto-deref is.
619 #[derive(Clone, Copy)]
620 enum AutoDerefStability {
621 /// Auto-deref will always choose the same type.
623 /// Auto-deref will always reborrow a reference.
625 /// Auto-deref will not occur, or it may select a different type.
628 impl AutoDerefStability {
629 fn is_deref_stable(self) -> bool {
630 matches!(self, Self::Deref)
633 fn is_reborrow_stable(self) -> bool {
634 matches!(self, Self::Deref | Self::Reborrow)
638 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
639 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
640 /// locations as those follow different rules.
641 #[allow(clippy::too_many_lines)]
642 fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (AutoDerefStability, &'tcx [Adjustment<'tcx>]) {
643 let mut adjustments = [].as_slice();
644 let stability = walk_to_expr_usage(cx, e, &mut |node, child_id| {
645 // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
646 if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
647 adjustments = cx.typeck_results().expr_adjustments(e);
650 Node::Local(Local { ty: Some(ty), .. }) => Some(binding_ty_auto_deref_stability(ty)),
652 kind: ItemKind::Static(..) | ItemKind::Const(..),
656 | Node::TraitItem(&TraitItem {
657 kind: TraitItemKind::Const(..),
661 | Node::ImplItem(&ImplItem {
662 kind: ImplItemKind::Const(..),
666 let ty = cx.tcx.type_of(def_id);
667 Some(if ty.is_ref() {
668 AutoDerefStability::None
670 AutoDerefStability::Deref
675 kind: ItemKind::Fn(..),
679 | Node::TraitItem(&TraitItem {
680 kind: TraitItemKind::Fn(..),
684 | Node::ImplItem(&ImplItem {
685 kind: ImplItemKind::Fn(..),
689 let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
690 Some(if !output.is_ref() {
691 AutoDerefStability::None
692 } else if output.has_placeholders() || output.has_opaque_types() {
693 AutoDerefStability::Reborrow
695 AutoDerefStability::Deref
699 Node::Expr(e) => match e.kind {
700 ExprKind::Ret(_) => {
703 .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
706 Some(if !output.is_ref() {
707 AutoDerefStability::None
708 } else if output.has_placeholders() || output.has_opaque_types() {
709 AutoDerefStability::Reborrow
711 AutoDerefStability::Deref
714 ExprKind::Call(func, args) => args
716 .position(|arg| arg.hir_id == child_id)
717 .zip(expr_sig(cx, func))
718 .and_then(|(i, sig)| sig.input_with_hir(i))
719 .map(|(hir_ty, ty)| match hir_ty {
720 // Type inference for closures can depend on how they're called. Only go by the explicit
722 Some(ty) => binding_ty_auto_deref_stability(ty),
723 None => param_auto_deref_stability(ty.skip_binder()),
725 ExprKind::MethodCall(_, [_, args @ ..], _) => {
726 let id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
727 args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
728 let arg = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
729 param_auto_deref_stability(arg)
732 ExprKind::MethodCall(..) => Some(AutoDerefStability::Reborrow),
733 ExprKind::Struct(path, fields, _) => {
734 let variant = variant_of_res(cx, cx.qpath_res(path, e.hir_id));
737 .find(|f| f.expr.hir_id == child_id)
739 .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
740 .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did)))
747 .unwrap_or(AutoDerefStability::None);
748 (stability, adjustments)
751 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
754 // let x = Box::new(Box::new(0u32));
755 // let y1: &Box<_> = x.deref();
756 // let y2: &Box<_> = &x;
758 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
759 // switching to auto-dereferencing.
760 fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>) -> AutoDerefStability {
761 let TyKind::Rptr(_, ty) = &ty.kind else {
762 return AutoDerefStability::None;
767 break match ty.ty.kind {
768 TyKind::Rptr(_, ref ref_ty) => {
773 QPath::TypeRelative(_, path)
777 segments: [.., path], ..
781 if let Some(args) = path.args
782 && args.args.iter().any(|arg| match arg {
783 GenericArg::Infer(_) => true,
784 GenericArg::Type(ty) => ty_contains_infer(ty),
788 AutoDerefStability::Reborrow
790 AutoDerefStability::Deref
799 | TyKind::TraitObject(..)
800 | TyKind::Path(_) => AutoDerefStability::Deref,
801 TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::Err => AutoDerefStability::Reborrow,
806 // Checks whether a type is inferred at some point.
807 // e.g. `_`, `Box<_>`, `[_]`
808 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
810 TyKind::Slice(ty) | TyKind::Array(ty, _) => ty_contains_infer(ty),
811 TyKind::Ptr(ty) | TyKind::Rptr(_, ty) => ty_contains_infer(ty.ty),
812 TyKind::Tup(tys) => tys.iter().any(ty_contains_infer),
813 TyKind::BareFn(ty) => {
814 if ty.decl.inputs.iter().any(ty_contains_infer) {
817 if let FnRetTy::Return(ty) = &ty.decl.output {
818 ty_contains_infer(ty)
824 QPath::TypeRelative(_, path)
828 segments: [.., path], ..
831 ) => path.args.map_or(false, |args| {
832 args.args.iter().any(|arg| match arg {
833 GenericArg::Infer(_) => true,
834 GenericArg::Type(ty) => ty_contains_infer(ty),
838 TyKind::Path(_) | TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err => true,
839 TyKind::Never | TyKind::TraitObject(..) => false,
843 // Checks whether a type is stable when switching to auto dereferencing,
844 fn param_auto_deref_stability(ty: Ty<'_>) -> AutoDerefStability {
845 let ty::Ref(_, mut ty, _) = *ty.kind() else {
846 return AutoDerefStability::None;
850 break match *ty.kind() {
851 ty::Ref(_, ref_ty, _) => {
869 | ty::GeneratorWitness(..)
872 | ty::Projection(_) => AutoDerefStability::Deref,
879 | ty::Dynamic(..) => AutoDerefStability::Reborrow,
880 ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => AutoDerefStability::Reborrow,
881 ty::Adt(..) => AutoDerefStability::Deref,
886 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
887 if let ty::Adt(adt, _) = *ty.kind() {
888 adt.is_struct() && adt.non_enum_variant().fields.iter().any(|f| f.name == name)
894 #[expect(clippy::needless_pass_by_value)]
895 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
902 let mut app = Applicability::MachineApplicable;
903 let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
904 let ty = cx.typeck_results().expr_ty(expr);
905 let (_, ref_count) = peel_mid_ty_refs(ty);
906 let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
907 // a deref call changing &T -> &U requires two deref operators the first time
908 // this occurs. One to remove the reference, a second to call the deref impl.
909 "*".repeat(ty_changed_count + 1)
911 "*".repeat(ty_changed_count)
913 let addr_of_str = if ty_changed_count < ref_count {
914 // Check if a reborrow from &mut T -> &T is required.
915 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
920 } else if target_mut == Mutability::Mut {
926 let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
927 format!("({})", expr_str)
929 expr_str.into_owned()
934 EXPLICIT_DEREF_METHODS,
937 Mutability::Not => "explicit `deref` method call",
938 Mutability::Mut => "explicit `deref_mut` method call",
941 format!("{}{}{}", addr_of_str, deref_str, expr_str),
945 State::DerefedBorrow {
950 let mut app = Applicability::MachineApplicable;
951 let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
952 span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| {
953 let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
954 format!("({})", snip)
958 diag.span_suggestion(data.span, "change this to", sugg, app);
961 State::ExplicitDeref {
965 let (span, hir_id) = if cx.typeck_results().expr_ty(expr).is_ref() {
966 (data.span, data.hir_id)
968 (deref_span, deref_hir_id)
970 span_lint_hir_and_then(
975 "deref which would be done by auto-deref",
977 let mut app = Applicability::MachineApplicable;
978 let snip = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app).0;
979 diag.span_suggestion(span, "try this", snip.into_owned(), app);
983 State::ExplicitDerefField { .. } => {
984 span_lint_hir_and_then(
989 "deref which would be done by auto-deref",
991 let mut app = Applicability::MachineApplicable;
992 let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
993 diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
997 State::Borrow | State::Reborrow { .. } => (),
1001 impl Dereferencing {
1002 fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1003 if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1004 if let Some(pat) = outer_pat {
1005 // Check for auto-deref
1007 cx.typeck_results().expr_adjustments(e),
1010 kind: Adjust::Deref(_),
1014 kind: Adjust::Deref(_),
1020 match get_parent_expr(cx, e) {
1021 // Field accesses are the same no matter the number of references.
1023 kind: ExprKind::Field(..),
1028 kind: ExprKind::Unary(UnOp::Deref, _),
1030 }) if !span.from_expansion() => {
1031 // Remove explicit deref.
1032 let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1033 pat.replacements.push((span, snip.into()));
1035 Some(parent) if !parent.span.from_expansion() => {
1036 // Double reference might be needed at this point.
1037 if parent.precedence().order() == PREC_POSTFIX {
1038 // Parentheses would be needed here, don't lint.
1041 pat.always_deref = false;
1042 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1043 pat.replacements.push((e.span, format!("&{}", snip)));
1046 _ if !e.span.from_expansion() => {
1047 // Double reference might be needed at this point.
1048 pat.always_deref = false;
1049 let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1050 pat.replacements.push((e.span, format!("&{}", snip)));
1052 // Edge case for macros. The span of the identifier will usually match the context of the
1053 // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1055 _ => *outer_pat = None,