]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
Lint field accesses in `explicit_auto_deref`
[rust.git] / clippy_lints / src / dereference.rs
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;
9 use rustc_hir::{
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,
13 };
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};
19
20 declare_clippy_lint! {
21     /// ### What it does
22     /// Checks for explicit `deref()` or `deref_mut()` method calls.
23     ///
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.
27     ///
28     /// ### Example
29     /// ```rust
30     /// use std::ops::Deref;
31     /// let a: &mut String = &mut String::from("foo");
32     /// let b: &str = a.deref();
33     /// ```
34     ///
35     /// Use instead:
36     /// ```rust
37     /// let a: &mut String = &mut String::from("foo");
38     /// let b = &*a;
39     /// ```
40     ///
41     /// This lint excludes:
42     /// ```rust,ignore
43     /// let _ = d.unwrap().deref();
44     /// ```
45     #[clippy::version = "1.44.0"]
46     pub EXPLICIT_DEREF_METHODS,
47     pedantic,
48     "Explicit use of deref or deref_mut method while not in a method chain."
49 }
50
51 declare_clippy_lint! {
52     /// ### What it does
53     /// Checks for address of operations (`&`) that are going to
54     /// be dereferenced immediately by the compiler.
55     ///
56     /// ### Why is this bad?
57     /// Suggests that the receiver of the expression borrows
58     /// the expression.
59     ///
60     /// ### Example
61     /// ```rust
62     /// fn fun(_a: &i32) {}
63     ///
64     /// let x: &i32 = &&&&&&5;
65     /// fun(&x);
66     /// ```
67     ///
68     /// Use instead:
69     /// ```rust
70     /// # fn fun(_a: &i32) {}
71     /// let x: &i32 = &5;
72     /// fun(x);
73     /// ```
74     #[clippy::version = "pre 1.29.0"]
75     pub NEEDLESS_BORROW,
76     style,
77     "taking a reference that is going to be automatically dereferenced"
78 }
79
80 declare_clippy_lint! {
81     /// ### What it does
82     /// Checks for `ref` bindings which create a reference to a reference.
83     ///
84     /// ### Why is this bad?
85     /// The address-of operator at the use site is clearer about the need for a reference.
86     ///
87     /// ### Example
88     /// ```rust
89     /// let x = Some("");
90     /// if let Some(ref x) = x {
91     ///     // use `x` here
92     /// }
93     /// ```
94     ///
95     /// Use instead:
96     /// ```rust
97     /// let x = Some("");
98     /// if let Some(x) = x {
99     ///     // use `&x` here
100     /// }
101     /// ```
102     #[clippy::version = "1.54.0"]
103     pub REF_BINDING_TO_REFERENCE,
104     pedantic,
105     "`ref` binding to a reference"
106 }
107
108 declare_clippy_lint! {
109     /// ### What it does
110     /// Checks for dereferencing expressions which would be covered by auto-deref.
111     ///
112     /// ### Why is this bad?
113     /// This unnecessarily complicates the code.
114     ///
115     /// ### Example
116     /// ```rust
117     /// let x = String::new();
118     /// let y: &str = &*x;
119     /// ```
120     /// Use instead:
121     /// ```rust
122     /// let x = String::new();
123     /// let y: &str = &x;
124     /// ```
125     #[clippy::version = "1.60.0"]
126     pub EXPLICIT_AUTO_DEREF,
127     complexity,
128     "dereferencing when the compiler would automatically dereference"
129 }
130
131 impl_lint_pass!(Dereferencing => [
132     EXPLICIT_DEREF_METHODS,
133     NEEDLESS_BORROW,
134     REF_BINDING_TO_REFERENCE,
135     EXPLICIT_AUTO_DEREF,
136 ]);
137
138 #[derive(Default)]
139 pub struct Dereferencing {
140     state: Option<(State, StateData)>,
141
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>,
146
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
149     /// other.
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
154     /// be.
155     ///
156     /// e.g. `m!(x) | Foo::Bar(ref x)`
157     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
158 }
159
160 struct StateData {
161     /// Span of the top level expression
162     span: Span,
163     hir_id: HirId,
164 }
165
166 enum State {
167     // Any number of deref method calls.
168     DerefMethod {
169         // The number of calls in a sequence which changed the referenced type
170         ty_changed_count: usize,
171         is_final_ufcs: bool,
172         /// The required mutability
173         target_mut: Mutability,
174     },
175     DerefedBorrow {
176         count: usize,
177         required_precedence: i8,
178         msg: &'static str,
179     },
180     ExplicitDeref {
181         deref_span: Span,
182         deref_hir_id: HirId,
183     },
184     ExplicitDerefField {
185         name: Symbol,
186     },
187     Reborrow {
188         deref_span: Span,
189         deref_hir_id: HirId,
190     },
191     Borrow,
192 }
193
194 // A reference operation considered by this lint pass
195 enum RefOp {
196     Method(Mutability),
197     Deref,
198     AddrOf,
199 }
200
201 struct RefPat {
202     /// Whether every usage of the binding is dereferenced.
203     always_deref: bool,
204     /// The spans of all the ref bindings for this local.
205     spans: Vec<Span>,
206     /// The applicability of this suggestion.
207     app: Applicability,
208     /// All the replacements which need to be made.
209     replacements: Vec<(Span, String)>,
210     /// The [`HirId`] that the lint should be emitted at.
211     hir_id: HirId,
212 }
213
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() {
219             return;
220         }
221
222         if let Some(local) = path_to_local(expr) {
223             self.check_local_usage(cx, expr, local);
224         }
225
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);
230             }
231             return;
232         }
233
234         let typeck = cx.typeck_results();
235         let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
236             x
237         } else {
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);
241             }
242             return;
243         };
244
245         match (self.state.take(), kind) {
246             (None, kind) => {
247                 let parent = get_parent_node(cx.tcx, expr.hir_id);
248                 let expr_ty = typeck.expr_ty(expr);
249                 match kind {
250                     RefOp::Deref => {
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)
254                         {
255                             self.state = Some((
256                                 State::ExplicitDerefField { name: name.name },
257                                 StateData { span: expr.span, hir_id: expr.hir_id },
258                             ));
259                         }
260                     }
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) =>
264                     {
265                         self.state = Some((
266                             State::DerefMethod {
267                                 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
268                                     0
269                                 } else {
270                                     1
271                                 },
272                                 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
273                                 target_mut,
274                             },
275                             StateData {
276                                 span: expr.span,
277                                 hir_id: expr.hir_id,
278                             },
279                         ));
280                     },
281                     RefOp::AddrOf => {
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 {
287                             match iter.next() {
288                                 Some(adjust) => {
289                                     if !matches!(adjust.kind, Adjust::Deref(_)) {
290                                         break Some(adjust);
291                                     } else if !adjust.target.is_ref() {
292                                         deref_count += 1;
293                                         break iter.next();
294                                     }
295                                     deref_count += 1;
296                                 },
297                                 None => break None,
298                             };
299                         };
300
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
303                         // handle.
304                         //
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
309                         //    start auto-deref.
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.
313                         //    e.g.
314                         //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
315                         //            let x = match x {
316                         //                // Removing the borrow will cause `x` to be moved
317                         //                Some(x) => &mut *x,
318                         //                None => y
319                         //            };
320                         //        }
321                         let deref_msg =
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";
324
325                         let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id)
326                         {
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)
330                         {
331                             if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !stability.is_reborrow_stable()
332                             {
333                                 (3, 0, deref_msg)
334                             } else {
335                                 (2, 0, deref_msg)
336                             }
337                         } else {
338                             (2, 0, deref_msg)
339                         };
340
341                         if deref_count >= required_refs {
342                             self.state = Some((
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,
347                                     required_precedence,
348                                     msg,
349                                 },
350                                 StateData {
351                                     span: expr.span,
352                                     hir_id: expr.hir_id,
353                                 },
354                             ));
355                         } else if stability.is_deref_stable() {
356                             self.state = Some((
357                                 State::Borrow,
358                                 StateData {
359                                     span: expr.span,
360                                     hir_id: expr.hir_id,
361                                 },
362                             ));
363                         }
364                     },
365                     RefOp::Method(..) => (),
366                 }
367             },
368             (
369                 Some((
370                     State::DerefMethod {
371                         target_mut,
372                         ty_changed_count,
373                         ..
374                     },
375                     data,
376                 )),
377                 RefOp::Method(_),
378             ) => {
379                 self.state = Some((
380                     State::DerefMethod {
381                         ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
382                             ty_changed_count
383                         } else {
384                             ty_changed_count + 1
385                         },
386                         is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
387                         target_mut,
388                     },
389                     data,
390                 ));
391             },
392             (
393                 Some((
394                     State::DerefedBorrow {
395                         count,
396                         required_precedence,
397                         msg,
398                     },
399                     data,
400                 )),
401                 RefOp::AddrOf,
402             ) if count != 0 => {
403                 self.state = Some((
404                     State::DerefedBorrow {
405                         count: count - 1,
406                         required_precedence,
407                         msg,
408                     },
409                     data,
410                 ));
411             },
412             (Some((State::Borrow, data)), RefOp::Deref) => {
413                 if typeck.expr_ty(sub_expr).is_ref() {
414                     self.state = Some((
415                         State::Reborrow {
416                             deref_span: expr.span,
417                             deref_hir_id: expr.hir_id,
418                         },
419                         data,
420                     ));
421                 } else {
422                     self.state = Some((
423                         State::ExplicitDeref {
424                             deref_span: expr.span,
425                             deref_hir_id: expr.hir_id,
426                         },
427                         data,
428                     ));
429                 }
430             },
431             (
432                 Some((
433                     State::Reborrow {
434                         deref_span,
435                         deref_hir_id,
436                     },
437                     data,
438                 )),
439                 RefOp::Deref,
440             ) => {
441                 self.state = Some((
442                     State::ExplicitDeref {
443                         deref_span,
444                         deref_hir_id,
445                     },
446                     data,
447                 ));
448             },
449             (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
450                 self.state = state;
451             },
452             (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
453                 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
454             {
455                 self.state = Some((State::ExplicitDerefField { name }, data));
456             },
457
458             (Some((state, data)), _) => report(cx, expr, state, data),
459         }
460     }
461
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;
470                     } else {
471                         prev_pat.spans.push(pat.span);
472                         prev_pat.replacements.push((
473                             pat.span,
474                             snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
475                                 .0
476                                 .into(),
477                         ));
478                     }
479                 }
480                 return;
481             }
482
483             if_chain! {
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();
488                 then {
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(
493                         id,
494                         Some(RefPat {
495                             always_deref: true,
496                             spans: vec![pat.span],
497                             app,
498                             replacements: vec![(pat.span, snip.into())],
499                             hir_id: pat.hir_id
500                         }),
501                     );
502                 }
503             }
504         }
505     }
506
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;
511                 let app = pat.app;
512                 let lint = if pat.always_deref {
513                     NEEDLESS_BORROW
514                 } else {
515                     REF_BINDING_TO_REFERENCE
516                 };
517                 span_lint_hir_and_then(
518                     cx,
519                     lint,
520                     pat.hir_id,
521                     pat.spans,
522                     "this pattern creates a reference to a reference",
523                     |diag| {
524                         diag.multipart_suggestion("try this", replacements, app);
525                     },
526                 );
527             }
528             self.current_body = None;
529         }
530     }
531 }
532
533 fn try_parse_ref_op<'tcx>(
534     tcx: TyCtxt<'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),
540         ExprKind::Call(
541             Expr {
542                 kind: ExprKind::Path(path),
543                 hir_id,
544                 ..
545             },
546             [arg],
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));
550         },
551         ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
552         _ => return None,
553     };
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))
558     } else {
559         None
560     }
561 }
562
563 // Checks whether the type for a deref call actually changed the type, not just the mutability of
564 // the reference.
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,
568
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
572         _ => false,
573     }
574 }
575
576 // Checks whether the parent node is a suitable context for switching from a deref method to the
577 // deref operator.
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,
581         _ => return true,
582     };
583     match parent.kind {
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,
587
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,
591
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,
600
601         ExprKind::Box(..)
602         | ExprKind::ConstBlock(..)
603         | ExprKind::Array(_)
604         | ExprKind::Call(..)
605         | ExprKind::MethodCall(..)
606         | ExprKind::Tup(..)
607         | ExprKind::Binary(..)
608         | ExprKind::Unary(..)
609         | ExprKind::Lit(..)
610         | ExprKind::Cast(..)
611         | ExprKind::Type(..)
612         | ExprKind::DropTemps(..)
613         | ExprKind::If(..)
614         | ExprKind::Loop(..)
615         | ExprKind::Match(..)
616         | ExprKind::Let(..)
617         | ExprKind::Closure{..}
618         | ExprKind::Block(..)
619         | ExprKind::Assign(..)
620         | ExprKind::AssignOp(..)
621         | ExprKind::Path(..)
622         | ExprKind::AddrOf(..)
623         | ExprKind::Break(..)
624         | ExprKind::Continue(..)
625         | ExprKind::Ret(..)
626         | ExprKind::InlineAsm(..)
627         | ExprKind::Struct(..)
628         | ExprKind::Repeat(..)
629         | ExprKind::Yield(..) => true,
630     }
631 }
632
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.
637     Deref,
638     /// Auto-deref will always reborrow a reference.
639     Reborrow,
640     /// Auto-deref will not occur, or it may select a different type.
641     None,
642 }
643 impl AutoDerefStability {
644     fn is_deref_stable(self) -> bool {
645         matches!(self, Self::Deref)
646     }
647
648     fn is_reborrow_stable(self) -> bool {
649         matches!(self, Self::Deref | Self::Reborrow)
650     }
651 }
652
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);
662         }
663         match node {
664             Node::Local(Local { ty: Some(ty), .. }) => Some(binding_ty_auto_deref_stability(ty)),
665             Node::Item(&Item {
666                 kind: ItemKind::Static(..) | ItemKind::Const(..),
667                 ..
668             })
669             | Node::TraitItem(&TraitItem {
670                 kind: TraitItemKind::Const(..),
671                 ..
672             })
673             | Node::ImplItem(&ImplItem {
674                 kind: ImplItemKind::Const(..),
675                 ..
676             }) => Some(AutoDerefStability::Deref),
677
678             Node::Item(&Item {
679                 kind: ItemKind::Fn(..),
680                 def_id,
681                 ..
682             })
683             | Node::TraitItem(&TraitItem {
684                 kind: TraitItemKind::Fn(..),
685                 def_id,
686                 ..
687             })
688             | Node::ImplItem(&ImplItem {
689                 kind: ImplItemKind::Fn(..),
690                 def_id,
691                 ..
692             }) => {
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
696                 } else {
697                     AutoDerefStability::Deref
698                 })
699             },
700
701             Node::Expr(e) => match e.kind {
702                 ExprKind::Ret(_) => {
703                     let output = cx
704                         .tcx
705                         .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
706                         .skip_binder()
707                         .output();
708                     Some(if output.has_placeholders() || output.has_opaque_types() {
709                         AutoDerefStability::Reborrow
710                     } else {
711                         AutoDerefStability::Deref
712                     })
713                 },
714                 ExprKind::Call(func, args) => args
715                     .iter()
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
721                         // types here.
722                         Some(ty) => binding_ty_auto_deref_stability(ty),
723                         None => param_auto_deref_stability(ty.skip_binder()),
724                     }),
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)
730                     })
731                 },
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));
735                     fields
736                         .iter()
737                         .find(|f| f.expr.hir_id == child_id)
738                         .zip(variant)
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)))
741                 },
742                 _ => None,
743             },
744             _ => None,
745         }
746     })
747     .unwrap_or(AutoDerefStability::None);
748     (stability, adjustments)
749 }
750
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 {
754         match parent.kind {
755             // ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
756             ExprKind::Field(..) => true,
757             ExprKind::Call(f, _) => f.hir_id == child_id,
758             _ => false,
759         }
760     } else {
761         false
762     }
763 }
764
765 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
766 //
767 // e.g.
768 // let x = Box::new(Box::new(0u32));
769 // let y1: &Box<_> = x.deref();
770 // let y2: &Box<_> = &x;
771 //
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;
777     };
778     let mut ty = ty;
779
780     loop {
781         break match ty.ty.kind {
782             TyKind::Rptr(_, ref ref_ty) => {
783                 ty = ref_ty;
784                 continue;
785             },
786             TyKind::Path(
787                 QPath::TypeRelative(_, path)
788                 | QPath::Resolved(
789                     _,
790                     Path {
791                         segments: [.., path], ..
792                     },
793                 ),
794             ) => {
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),
799                         _ => false,
800                     })
801                 {
802                     AutoDerefStability::Reborrow
803                 } else {
804                     AutoDerefStability::Deref
805                 }
806             },
807             TyKind::Slice(_)
808             | TyKind::Array(..)
809             | TyKind::BareFn(_)
810             | TyKind::Never
811             | TyKind::Tup(_)
812             | TyKind::Ptr(_)
813             | TyKind::TraitObject(..)
814             | TyKind::Path(_) => AutoDerefStability::Deref,
815             TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::Err => AutoDerefStability::Reborrow,
816         };
817     }
818 }
819
820 // Checks whether a type is inferred at some point.
821 // e.g. `_`, `Box<_>`, `[_]`
822 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
823     match &ty.kind {
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) {
829                 return true;
830             }
831             if let FnRetTy::Return(ty) = &ty.decl.output {
832                 ty_contains_infer(ty)
833             } else {
834                 false
835             }
836         },
837         &TyKind::Path(
838             QPath::TypeRelative(_, path)
839             | QPath::Resolved(
840                 _,
841                 Path {
842                     segments: [.., path], ..
843                 },
844             ),
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),
849                 _ => false,
850             })
851         }),
852         TyKind::Path(_) | TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err => true,
853         TyKind::Never | TyKind::TraitObject(..) => false,
854     }
855 }
856
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;
861     };
862
863     loop {
864         break match *ty.kind() {
865             ty::Ref(_, ref_ty, _) => {
866                 ty = ref_ty;
867                 continue;
868             },
869             ty::Bool
870             | ty::Char
871             | ty::Int(_)
872             | ty::Uint(_)
873             | ty::Float(_)
874             | ty::Foreign(_)
875             | ty::Str
876             | ty::Array(..)
877             | ty::Slice(..)
878             | ty::RawPtr(..)
879             | ty::FnDef(..)
880             | ty::FnPtr(_)
881             | ty::Closure(..)
882             | ty::Generator(..)
883             | ty::GeneratorWitness(..)
884             | ty::Never
885             | ty::Tuple(_)
886             | ty::Projection(_) => AutoDerefStability::Deref,
887             ty::Infer(_)
888             | ty::Error(_)
889             | ty::Param(_)
890             | ty::Bound(..)
891             | ty::Opaque(..)
892             | ty::Placeholder(_)
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,
896         };
897     }
898 }
899
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)
903     } else {
904         false
905     }
906 }
907
908 #[expect(clippy::needless_pass_by_value)]
909 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
910     match state {
911         State::DerefMethod {
912             ty_changed_count,
913             is_final_ufcs,
914             target_mut,
915         } => {
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)
924             } else {
925                 "*".repeat(ty_changed_count)
926             };
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)) {
930                     "&*"
931                 } else {
932                     ""
933                 }
934             } else if target_mut == Mutability::Mut {
935                 "&mut "
936             } else {
937                 "&"
938             };
939
940             let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
941                 format!("({})", expr_str)
942             } else {
943                 expr_str.into_owned()
944             };
945
946             span_lint_and_sugg(
947                 cx,
948                 EXPLICIT_DEREF_METHODS,
949                 data.span,
950                 match target_mut {
951                     Mutability::Not => "explicit `deref` method call",
952                     Mutability::Mut => "explicit `deref_mut` method call",
953                 },
954                 "try this",
955                 format!("{}{}{}", addr_of_str, deref_str, expr_str),
956                 app,
957             );
958         },
959         State::DerefedBorrow {
960             required_precedence,
961             msg,
962             ..
963         } => {
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)
969                 } else {
970                     snip.into()
971                 };
972                 diag.span_suggestion(data.span, "change this to", sugg, app);
973             });
974         },
975         State::ExplicitDeref {
976             deref_span,
977             deref_hir_id,
978         } => {
979             let (span, hir_id) = if cx.typeck_results().expr_ty(expr).is_ref() {
980                 (data.span, data.hir_id)
981             } else {
982                 (deref_span, deref_hir_id)
983             };
984             span_lint_hir_and_then(
985                 cx,
986                 EXPLICIT_AUTO_DEREF,
987                 hir_id,
988                 span,
989                 "deref which would be done by auto-deref",
990                 |diag| {
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);
994                 },
995             );
996         },
997         State::ExplicitDerefField { .. } => {
998             span_lint_hir_and_then(
999                 cx,
1000                 EXPLICIT_AUTO_DEREF,
1001                 data.hir_id,
1002                 data.span,
1003                 "deref which would be done by auto-deref",
1004                 |diag| {
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);
1008                 },
1009             );
1010         },
1011         State::Borrow | State::Reborrow { .. } => (),
1012     }
1013 }
1014
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
1020                 if !matches!(
1021                     cx.typeck_results().expr_adjustments(e),
1022                     [
1023                         Adjustment {
1024                             kind: Adjust::Deref(_),
1025                             ..
1026                         },
1027                         Adjustment {
1028                             kind: Adjust::Deref(_),
1029                             ..
1030                         },
1031                         ..
1032                     ]
1033                 ) {
1034                     match get_parent_expr(cx, e) {
1035                         // Field accesses are the same no matter the number of references.
1036                         Some(Expr {
1037                             kind: ExprKind::Field(..),
1038                             ..
1039                         }) => (),
1040                         Some(&Expr {
1041                             span,
1042                             kind: ExprKind::Unary(UnOp::Deref, _),
1043                             ..
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()));
1048                         },
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.
1053                                 *outer_pat = None;
1054                             } else {
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)));
1058                             }
1059                         },
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)));
1065                         },
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
1068                         // macros
1069                         _ => *outer_pat = None,
1070                     }
1071                 }
1072             }
1073         }
1074     }
1075 }