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};
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 parent = get_parent_node(cx.tcx, expr.hir_id);
248 let expr_ty = typeck.expr_ty(expr);
251 if let Some(Node::Expr(e)) = parent
252 && let ExprKind::Field(_, name) = e.kind
253 && !ty_contains_field(typeck.expr_ty(sub_expr), name.name)
256 State::ExplicitDerefField { name: name.name },
257 StateData { span: expr.span, hir_id: expr.hir_id },
261 RefOp::Method(target_mut)
262 if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
263 && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
267 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
272 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
282 let (stability, adjustments) = walk_parents(cx, expr);
283 // Find the number of times the borrow is auto-derefed.
284 let mut iter = adjustments.iter();
285 let mut deref_count = 0usize;
286 let next_adjust = loop {
289 if !matches!(adjust.kind, Adjust::Deref(_)) {
291 } else if !adjust.target.is_ref() {
301 // Determine the required number of references before any can be removed. In all cases the
302 // reference made by the current expression will be removed. After that there are four cases to
305 // 1. Auto-borrow will trigger in the current position, so no further references are required.
306 // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
307 // handle the automatically inserted re-borrow.
308 // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
310 // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
311 // adjustments will not be inserted automatically, then leave one further reference to avoid
312 // moving a mutable borrow.
314 // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
316 // // Removing the borrow will cause `x` to be moved
317 // Some(x) => &mut *x,
322 "this expression creates a reference which is immediately dereferenced by the compiler";
323 let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
325 let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id)
327 (1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
328 } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
329 next_adjust.map(|a| &a.kind)
331 if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !stability.is_reborrow_stable()
341 if deref_count >= required_refs {
343 State::DerefedBorrow {
344 // One of the required refs is for the current borrow expression, the remaining ones
345 // can't be removed without breaking the code. See earlier comment.
346 count: deref_count - required_refs,
355 } else if stability.is_deref_stable() {
365 RefOp::Method(..) => (),
381 ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
386 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
394 State::DerefedBorrow {
404 State::DerefedBorrow {
412 (Some((State::Borrow, data)), RefOp::Deref) => {
413 if typeck.expr_ty(sub_expr).is_ref() {
416 deref_span: expr.span,
417 deref_hir_id: expr.hir_id,
423 State::ExplicitDeref {
424 deref_span: expr.span,
425 deref_hir_id: expr.hir_id,
442 State::ExplicitDeref {
449 (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
452 (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
453 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
455 self.state = Some((State::ExplicitDerefField { name }, data));
458 (Some((state, data)), _) => report(cx, expr, state, data),
462 fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
463 if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
464 if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
465 // This binding id has been seen before. Add this pattern to the list of changes.
466 if let Some(prev_pat) = opt_prev_pat {
467 if pat.span.from_expansion() {
468 // Doesn't match the context of the previous pattern. Can't lint here.
469 *opt_prev_pat = None;
471 prev_pat.spans.push(pat.span);
472 prev_pat.replacements.push((
474 snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
484 if !pat.span.from_expansion();
485 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
486 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
487 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
489 let mut app = Applicability::MachineApplicable;
490 let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
491 self.current_body = self.current_body.or(cx.enclosing_body);
492 self.ref_locals.insert(
496 spans: vec![pat.span],
498 replacements: vec![(pat.span, snip.into())],
507 fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
508 if Some(body.id()) == self.current_body {
509 for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
510 let replacements = pat.replacements;
512 let lint = if pat.always_deref {
515 REF_BINDING_TO_REFERENCE
517 span_lint_hir_and_then(
522 "this pattern creates a reference to a reference",
524 diag.multipart_suggestion("try this", replacements, app);
528 self.current_body = None;
533 fn try_parse_ref_op<'tcx>(
535 typeck: &'tcx TypeckResults<'_>,
536 expr: &'tcx Expr<'_>,
537 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
538 let (def_id, arg) = match expr.kind {
539 ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
542 kind: ExprKind::Path(path),
547 ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
548 ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
549 return Some((RefOp::Deref, sub_expr));
551 ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
554 if tcx.is_diagnostic_item(sym::deref_method, def_id) {
555 Some((RefOp::Method(Mutability::Not), arg))
556 } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
557 Some((RefOp::Method(Mutability::Mut), arg))
563 // Checks whether the type for a deref call actually changed the type, not just the mutability of
565 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
566 match (result_ty.kind(), arg_ty.kind()) {
567 (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
569 // The result type for a deref method is always a reference
570 // Not matching the previous pattern means the argument type is not a reference
571 // This means that the type did change
576 // Checks whether the parent node is a suitable context for switching from a deref method to the
578 fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
579 let parent = match parent {
580 Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
584 // Leave deref calls in the middle of a method chain.
585 // e.g. x.deref().foo()
586 ExprKind::MethodCall(_, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
588 // Leave deref calls resulting in a called function
589 // e.g. (x.deref())()
590 ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
592 // Makes an ugly suggestion
593 // e.g. *x.deref() => *&*x
594 ExprKind::Unary(UnOp::Deref, _)
595 // Postfix expressions would require parens
596 | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
597 | ExprKind::Field(..)
598 | ExprKind::Index(..)
599 | ExprKind::Err => false,
602 | ExprKind::ConstBlock(..)
605 | ExprKind::MethodCall(..)
607 | ExprKind::Binary(..)
608 | ExprKind::Unary(..)
612 | ExprKind::DropTemps(..)
615 | ExprKind::Match(..)
617 | ExprKind::Closure{..}
618 | ExprKind::Block(..)
619 | ExprKind::Assign(..)
620 | ExprKind::AssignOp(..)
622 | ExprKind::AddrOf(..)
623 | ExprKind::Break(..)
624 | ExprKind::Continue(..)
626 | ExprKind::InlineAsm(..)
627 | ExprKind::Struct(..)
628 | ExprKind::Repeat(..)
629 | ExprKind::Yield(..) => true,
633 /// How stable the result of auto-deref is.
634 #[derive(Clone, Copy)]
635 enum AutoDerefStability {
636 /// Auto-deref will always choose the same type.
638 /// Auto-deref will always reborrow a reference.
640 /// Auto-deref will not occur, or it may select a different type.
643 impl AutoDerefStability {
644 fn is_deref_stable(self) -> bool {
645 matches!(self, Self::Deref)
648 fn is_reborrow_stable(self) -> bool {
649 matches!(self, Self::Deref | Self::Reborrow)
653 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
654 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
655 /// locations as those follow different rules.
656 fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (AutoDerefStability, &'tcx [Adjustment<'tcx>]) {
657 let mut adjustments = [].as_slice();
658 let stability = walk_to_expr_usage(cx, e, &mut |node, child_id| {
659 // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
660 if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
661 adjustments = cx.typeck_results().expr_adjustments(e);
664 Node::Local(Local { ty: Some(ty), .. }) => Some(binding_ty_auto_deref_stability(ty)),
666 kind: ItemKind::Static(..) | ItemKind::Const(..),
669 | Node::TraitItem(&TraitItem {
670 kind: TraitItemKind::Const(..),
673 | Node::ImplItem(&ImplItem {
674 kind: ImplItemKind::Const(..),
676 }) => Some(AutoDerefStability::Deref),
679 kind: ItemKind::Fn(..),
683 | Node::TraitItem(&TraitItem {
684 kind: TraitItemKind::Fn(..),
688 | Node::ImplItem(&ImplItem {
689 kind: ImplItemKind::Fn(..),
693 let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
694 Some(if output.has_placeholders() || output.has_opaque_types() {
695 AutoDerefStability::Reborrow
697 AutoDerefStability::Deref
701 Node::Expr(e) => match e.kind {
702 ExprKind::Ret(_) => {
705 .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
708 Some(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 if the given expression is a position which can auto-borrow.
752 fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
753 if let Some(Node::Expr(parent)) = parent {
755 // ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
756 ExprKind::Field(..) => true,
757 ExprKind::Call(f, _) => f.hir_id == child_id,
765 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
768 // let x = Box::new(Box::new(0u32));
769 // let y1: &Box<_> = x.deref();
770 // let y2: &Box<_> = &x;
772 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
773 // switching to auto-dereferencing.
774 fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>) -> AutoDerefStability {
775 let TyKind::Rptr(_, ty) = &ty.kind else {
776 return AutoDerefStability::None;
781 break match ty.ty.kind {
782 TyKind::Rptr(_, ref ref_ty) => {
787 QPath::TypeRelative(_, path)
791 segments: [.., path], ..
795 if let Some(args) = path.args
796 && args.args.iter().any(|arg| match arg {
797 GenericArg::Infer(_) => true,
798 GenericArg::Type(ty) => ty_contains_infer(ty),
802 AutoDerefStability::Reborrow
804 AutoDerefStability::Deref
813 | TyKind::TraitObject(..)
814 | TyKind::Path(_) => AutoDerefStability::Deref,
815 TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::Err => AutoDerefStability::Reborrow,
820 // Checks whether a type is inferred at some point.
821 // e.g. `_`, `Box<_>`, `[_]`
822 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
824 TyKind::Slice(ty) | TyKind::Array(ty, _) => ty_contains_infer(ty),
825 TyKind::Ptr(ty) | TyKind::Rptr(_, ty) => ty_contains_infer(ty.ty),
826 TyKind::Tup(tys) => tys.iter().any(ty_contains_infer),
827 TyKind::BareFn(ty) => {
828 if ty.decl.inputs.iter().any(ty_contains_infer) {
831 if let FnRetTy::Return(ty) = &ty.decl.output {
832 ty_contains_infer(ty)
838 QPath::TypeRelative(_, path)
842 segments: [.., path], ..
845 ) => path.args.map_or(false, |args| {
846 args.args.iter().any(|arg| match arg {
847 GenericArg::Infer(_) => true,
848 GenericArg::Type(ty) => ty_contains_infer(ty),
852 TyKind::Path(_) | TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err => true,
853 TyKind::Never | TyKind::TraitObject(..) => false,
857 // Checks whether a type is stable when switching to auto dereferencing,
858 fn param_auto_deref_stability(ty: Ty<'_>) -> AutoDerefStability {
859 let ty::Ref(_, mut ty, _) = *ty.kind() else {
860 return AutoDerefStability::None;
864 break match *ty.kind() {
865 ty::Ref(_, ref_ty, _) => {
883 | ty::GeneratorWitness(..)
886 | ty::Projection(_) => AutoDerefStability::Deref,
893 | ty::Dynamic(..) => AutoDerefStability::Reborrow,
894 ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => AutoDerefStability::Reborrow,
895 ty::Adt(..) => AutoDerefStability::Deref,
900 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
901 if let ty::Adt(adt, _) = *ty.kind() {
902 adt.is_struct() && adt.non_enum_variant().fields.iter().any(|f| f.name == name)
908 #[expect(clippy::needless_pass_by_value)]
909 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
916 let mut app = Applicability::MachineApplicable;
917 let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
918 let ty = cx.typeck_results().expr_ty(expr);
919 let (_, ref_count) = peel_mid_ty_refs(ty);
920 let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
921 // a deref call changing &T -> &U requires two deref operators the first time
922 // this occurs. One to remove the reference, a second to call the deref impl.
923 "*".repeat(ty_changed_count + 1)
925 "*".repeat(ty_changed_count)
927 let addr_of_str = if ty_changed_count < ref_count {
928 // Check if a reborrow from &mut T -> &T is required.
929 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
934 } else if target_mut == Mutability::Mut {
940 let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
941 format!("({})", expr_str)
943 expr_str.into_owned()
948 EXPLICIT_DEREF_METHODS,
951 Mutability::Not => "explicit `deref` method call",
952 Mutability::Mut => "explicit `deref_mut` method call",
955 format!("{}{}{}", addr_of_str, deref_str, expr_str),
959 State::DerefedBorrow {
964 let mut app = Applicability::MachineApplicable;
965 let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
966 span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| {
967 let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
968 format!("({})", snip)
972 diag.span_suggestion(data.span, "change this to", sugg, app);
975 State::ExplicitDeref {
979 let (span, hir_id) = if cx.typeck_results().expr_ty(expr).is_ref() {
980 (data.span, data.hir_id)
982 (deref_span, deref_hir_id)
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, span.ctxt(), "..", &mut app).0;
993 diag.span_suggestion(span, "try this", snip.into_owned(), app);
997 State::ExplicitDerefField { .. } => {
998 span_lint_hir_and_then(
1000 EXPLICIT_AUTO_DEREF,
1003 "deref which would be done by auto-deref",
1005 let mut app = Applicability::MachineApplicable;
1006 let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
1007 diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
1011 State::Borrow | State::Reborrow { .. } => (),
1015 impl Dereferencing {
1016 fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1017 if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1018 if let Some(pat) = outer_pat {
1019 // Check for auto-deref
1021 cx.typeck_results().expr_adjustments(e),
1024 kind: Adjust::Deref(_),
1028 kind: Adjust::Deref(_),
1034 match get_parent_expr(cx, e) {
1035 // Field accesses are the same no matter the number of references.
1037 kind: ExprKind::Field(..),
1042 kind: ExprKind::Unary(UnOp::Deref, _),
1044 }) if !span.from_expansion() => {
1045 // Remove explicit deref.
1046 let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1047 pat.replacements.push((span, snip.into()));
1049 Some(parent) if !parent.span.from_expansion() => {
1050 // Double reference might be needed at this point.
1051 if parent.precedence().order() == PREC_POSTFIX {
1052 // Parentheses would be needed here, don't lint.
1055 pat.always_deref = false;
1056 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1057 pat.replacements.push((e.span, format!("&{}", snip)));
1060 _ if !e.span.from_expansion() => {
1061 // Double reference might be needed at this point.
1062 pat.always_deref = false;
1063 let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1064 pat.replacements.push((e.span, format!("&{}", snip)));
1066 // Edge case for macros. The span of the identifier will usually match the context of the
1067 // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1069 _ => *outer_pat = None,