]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
Auto merge of #9058 - xFrednet:changelog-1-62, r=flip1995
[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, 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::intravisit::{walk_ty, Visitor};
10 use rustc_hir::{
11     self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
12     ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
13     TraitItemKind, TyKind, UnOp,
14 };
15 use rustc_infer::infer::TyCtxtInferExt;
16 use rustc_lint::{LateContext, LateLintPass};
17 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
18 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
19 use rustc_session::{declare_tool_lint, impl_lint_pass};
20 use rustc_span::{symbol::sym, Span, Symbol};
21 use rustc_trait_selection::infer::InferCtxtExt;
22
23 declare_clippy_lint! {
24     /// ### What it does
25     /// Checks for explicit `deref()` or `deref_mut()` method calls.
26     ///
27     /// ### Why is this bad?
28     /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
29     /// when not part of a method chain.
30     ///
31     /// ### Example
32     /// ```rust
33     /// use std::ops::Deref;
34     /// let a: &mut String = &mut String::from("foo");
35     /// let b: &str = a.deref();
36     /// ```
37     ///
38     /// Use instead:
39     /// ```rust
40     /// let a: &mut String = &mut String::from("foo");
41     /// let b = &*a;
42     /// ```
43     ///
44     /// This lint excludes:
45     /// ```rust,ignore
46     /// let _ = d.unwrap().deref();
47     /// ```
48     #[clippy::version = "1.44.0"]
49     pub EXPLICIT_DEREF_METHODS,
50     pedantic,
51     "Explicit use of deref or deref_mut method while not in a method chain."
52 }
53
54 declare_clippy_lint! {
55     /// ### What it does
56     /// Checks for address of operations (`&`) that are going to
57     /// be dereferenced immediately by the compiler.
58     ///
59     /// ### Why is this bad?
60     /// Suggests that the receiver of the expression borrows
61     /// the expression.
62     ///
63     /// ### Example
64     /// ```rust
65     /// fn fun(_a: &i32) {}
66     ///
67     /// let x: &i32 = &&&&&&5;
68     /// fun(&x);
69     /// ```
70     ///
71     /// Use instead:
72     /// ```rust
73     /// # fn fun(_a: &i32) {}
74     /// let x: &i32 = &5;
75     /// fun(x);
76     /// ```
77     #[clippy::version = "pre 1.29.0"]
78     pub NEEDLESS_BORROW,
79     style,
80     "taking a reference that is going to be automatically dereferenced"
81 }
82
83 declare_clippy_lint! {
84     /// ### What it does
85     /// Checks for `ref` bindings which create a reference to a reference.
86     ///
87     /// ### Why is this bad?
88     /// The address-of operator at the use site is clearer about the need for a reference.
89     ///
90     /// ### Example
91     /// ```rust
92     /// let x = Some("");
93     /// if let Some(ref x) = x {
94     ///     // use `x` here
95     /// }
96     /// ```
97     ///
98     /// Use instead:
99     /// ```rust
100     /// let x = Some("");
101     /// if let Some(x) = x {
102     ///     // use `&x` here
103     /// }
104     /// ```
105     #[clippy::version = "1.54.0"]
106     pub REF_BINDING_TO_REFERENCE,
107     pedantic,
108     "`ref` binding to a reference"
109 }
110
111 declare_clippy_lint! {
112     /// ### What it does
113     /// Checks for dereferencing expressions which would be covered by auto-deref.
114     ///
115     /// ### Why is this bad?
116     /// This unnecessarily complicates the code.
117     ///
118     /// ### Example
119     /// ```rust
120     /// let x = String::new();
121     /// let y: &str = &*x;
122     /// ```
123     /// Use instead:
124     /// ```rust
125     /// let x = String::new();
126     /// let y: &str = &x;
127     /// ```
128     #[clippy::version = "1.60.0"]
129     pub EXPLICIT_AUTO_DEREF,
130     complexity,
131     "dereferencing when the compiler would automatically dereference"
132 }
133
134 impl_lint_pass!(Dereferencing => [
135     EXPLICIT_DEREF_METHODS,
136     NEEDLESS_BORROW,
137     REF_BINDING_TO_REFERENCE,
138     EXPLICIT_AUTO_DEREF,
139 ]);
140
141 #[derive(Default)]
142 pub struct Dereferencing {
143     state: Option<(State, StateData)>,
144
145     // While parsing a `deref` method call in ufcs form, the path to the function is itself an
146     // expression. This is to store the id of that expression so it can be skipped when
147     // `check_expr` is called for it.
148     skip_expr: Option<HirId>,
149
150     /// The body the first local was found in. Used to emit lints when the traversal of the body has
151     /// been finished. Note we can't lint at the end of every body as they can be nested within each
152     /// other.
153     current_body: Option<BodyId>,
154     /// The list of locals currently being checked by the lint.
155     /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
156     /// This is needed for or patterns where one of the branches can be linted, but another can not
157     /// be.
158     ///
159     /// e.g. `m!(x) | Foo::Bar(ref x)`
160     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
161 }
162
163 struct StateData {
164     /// Span of the top level expression
165     span: Span,
166     hir_id: HirId,
167     position: Position,
168 }
169
170 struct DerefedBorrow {
171     count: usize,
172     msg: &'static str,
173 }
174
175 enum State {
176     // Any number of deref method calls.
177     DerefMethod {
178         // The number of calls in a sequence which changed the referenced type
179         ty_changed_count: usize,
180         is_final_ufcs: bool,
181         /// The required mutability
182         target_mut: Mutability,
183     },
184     DerefedBorrow(DerefedBorrow),
185     ExplicitDeref {
186         // Span and id of the top-level deref expression if the parent expression is a borrow.
187         deref_span_id: Option<(Span, HirId)>,
188     },
189     ExplicitDerefField {
190         name: Symbol,
191     },
192     Reborrow {
193         deref_span: Span,
194         deref_hir_id: HirId,
195     },
196     Borrow,
197 }
198
199 // A reference operation considered by this lint pass
200 enum RefOp {
201     Method(Mutability),
202     Deref,
203     AddrOf,
204 }
205
206 struct RefPat {
207     /// Whether every usage of the binding is dereferenced.
208     always_deref: bool,
209     /// The spans of all the ref bindings for this local.
210     spans: Vec<Span>,
211     /// The applicability of this suggestion.
212     app: Applicability,
213     /// All the replacements which need to be made.
214     replacements: Vec<(Span, String)>,
215     /// The [`HirId`] that the lint should be emitted at.
216     hir_id: HirId,
217 }
218
219 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
220     #[expect(clippy::too_many_lines)]
221     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
222         // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
223         if Some(expr.hir_id) == self.skip_expr.take() {
224             return;
225         }
226
227         if let Some(local) = path_to_local(expr) {
228             self.check_local_usage(cx, expr, local);
229         }
230
231         // Stop processing sub expressions when a macro call is seen
232         if expr.span.from_expansion() {
233             if let Some((state, data)) = self.state.take() {
234                 report(cx, expr, state, data);
235             }
236             return;
237         }
238
239         let typeck = cx.typeck_results();
240         let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
241             x
242         } else {
243             // The whole chain of reference operations has been seen
244             if let Some((state, data)) = self.state.take() {
245                 report(cx, expr, state, data);
246             }
247             return;
248         };
249
250         match (self.state.take(), kind) {
251             (None, kind) => {
252                 let expr_ty = typeck.expr_ty(expr);
253                 let (position, adjustments) = walk_parents(cx, expr);
254
255                 match kind {
256                     RefOp::Deref => {
257                         if let Position::FieldAccess(name) = position
258                             && !ty_contains_field(typeck.expr_ty(sub_expr), name)
259                         {
260                             self.state = Some((
261                                 State::ExplicitDerefField { name },
262                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
263                             ));
264                         } else if position.is_deref_stable() {
265                             self.state = Some((
266                                 State::ExplicitDeref { deref_span_id: None },
267                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
268                             ));
269                         }
270                     }
271                     RefOp::Method(target_mut)
272                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
273                             && position.lint_explicit_deref() =>
274                     {
275                         self.state = Some((
276                             State::DerefMethod {
277                                 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
278                                     0
279                                 } else {
280                                     1
281                                 },
282                                 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
283                                 target_mut,
284                             },
285                             StateData {
286                                 span: expr.span,
287                                 hir_id: expr.hir_id,
288                                 position
289                             },
290                         ));
291                     },
292                     RefOp::AddrOf => {
293                         // Find the number of times the borrow is auto-derefed.
294                         let mut iter = adjustments.iter();
295                         let mut deref_count = 0usize;
296                         let next_adjust = loop {
297                             match iter.next() {
298                                 Some(adjust) => {
299                                     if !matches!(adjust.kind, Adjust::Deref(_)) {
300                                         break Some(adjust);
301                                     } else if !adjust.target.is_ref() {
302                                         deref_count += 1;
303                                         break iter.next();
304                                     }
305                                     deref_count += 1;
306                                 },
307                                 None => break None,
308                             };
309                         };
310
311                         // Determine the required number of references before any can be removed. In all cases the
312                         // reference made by the current expression will be removed. After that there are four cases to
313                         // handle.
314                         //
315                         // 1. Auto-borrow will trigger in the current position, so no further references are required.
316                         // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
317                         //    handle the automatically inserted re-borrow.
318                         // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
319                         //    start auto-deref.
320                         // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
321                         //    adjustments will not be inserted automatically, then leave one further reference to avoid
322                         //    moving a mutable borrow.
323                         //    e.g.
324                         //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
325                         //            let x = match x {
326                         //                // Removing the borrow will cause `x` to be moved
327                         //                Some(x) => &mut *x,
328                         //                None => y
329                         //            };
330                         //        }
331                         let deref_msg =
332                             "this expression creates a reference which is immediately dereferenced by the compiler";
333                         let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
334
335                         let (required_refs, msg) = if position.can_auto_borrow() {
336                             (1, if deref_count == 1 { borrow_msg } else { deref_msg })
337                         } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
338                             next_adjust.map(|a| &a.kind)
339                         {
340                             if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
341                             {
342                                 (3, deref_msg)
343                             } else {
344                                 (2, deref_msg)
345                             }
346                         } else {
347                             (2, deref_msg)
348                         };
349
350                         if deref_count >= required_refs {
351                             self.state = Some((
352                                 State::DerefedBorrow(DerefedBorrow {
353                                     // One of the required refs is for the current borrow expression, the remaining ones
354                                     // can't be removed without breaking the code. See earlier comment.
355                                     count: deref_count - required_refs,
356                                     msg,
357                                 }),
358                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
359                             ));
360                         } else if position.is_deref_stable() {
361                             self.state = Some((
362                                 State::Borrow,
363                                 StateData {
364                                     span: expr.span,
365                                     hir_id: expr.hir_id,
366                                     position
367                                 },
368                             ));
369                         }
370                     },
371                     RefOp::Method(..) => (),
372                 }
373             },
374             (
375                 Some((
376                     State::DerefMethod {
377                         target_mut,
378                         ty_changed_count,
379                         ..
380                     },
381                     data,
382                 )),
383                 RefOp::Method(_),
384             ) => {
385                 self.state = Some((
386                     State::DerefMethod {
387                         ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
388                             ty_changed_count
389                         } else {
390                             ty_changed_count + 1
391                         },
392                         is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
393                         target_mut,
394                     },
395                     data,
396                 ));
397             },
398             (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
399                 self.state = Some((
400                     State::DerefedBorrow(DerefedBorrow {
401                         count: state.count - 1,
402                         ..state
403                     }),
404                     data,
405                 ));
406             },
407             (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
408                 let position = data.position;
409                 report(cx, expr, State::DerefedBorrow(state), data);
410                 if position.is_deref_stable() {
411                     self.state = Some((
412                         State::Borrow,
413                         StateData {
414                             span: expr.span,
415                             hir_id: expr.hir_id,
416                             position,
417                         },
418                     ));
419                 }
420             },
421             (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
422                 let position = data.position;
423                 report(cx, expr, State::DerefedBorrow(state), data);
424                 if let Position::FieldAccess(name) = position
425                     && !ty_contains_field(typeck.expr_ty(sub_expr), name)
426                 {
427                     self.state = Some((
428                         State::ExplicitDerefField { name },
429                         StateData { span: expr.span, hir_id: expr.hir_id, position },
430                     ));
431                 } else if position.is_deref_stable() {
432                     self.state = Some((
433                         State::ExplicitDeref { deref_span_id: None },
434                         StateData { span: expr.span, hir_id: expr.hir_id, position },
435                     ));
436                 }
437             },
438
439             (Some((State::Borrow, data)), RefOp::Deref) => {
440                 if typeck.expr_ty(sub_expr).is_ref() {
441                     self.state = Some((
442                         State::Reborrow {
443                             deref_span: expr.span,
444                             deref_hir_id: expr.hir_id,
445                         },
446                         data,
447                     ));
448                 } else {
449                     self.state = Some((
450                         State::ExplicitDeref {
451                             deref_span_id: Some((expr.span, expr.hir_id)),
452                         },
453                         data,
454                     ));
455                 }
456             },
457             (
458                 Some((
459                     State::Reborrow {
460                         deref_span,
461                         deref_hir_id,
462                     },
463                     data,
464                 )),
465                 RefOp::Deref,
466             ) => {
467                 self.state = Some((
468                     State::ExplicitDeref {
469                         deref_span_id: Some((deref_span, deref_hir_id)),
470                     },
471                     data,
472                 ));
473             },
474             (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
475                 self.state = state;
476             },
477             (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
478                 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
479             {
480                 self.state = Some((State::ExplicitDerefField { name }, data));
481             },
482
483             (Some((state, data)), _) => report(cx, expr, state, data),
484         }
485     }
486
487     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
488         if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
489             if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
490                 // This binding id has been seen before. Add this pattern to the list of changes.
491                 if let Some(prev_pat) = opt_prev_pat {
492                     if pat.span.from_expansion() {
493                         // Doesn't match the context of the previous pattern. Can't lint here.
494                         *opt_prev_pat = None;
495                     } else {
496                         prev_pat.spans.push(pat.span);
497                         prev_pat.replacements.push((
498                             pat.span,
499                             snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
500                                 .0
501                                 .into(),
502                         ));
503                     }
504                 }
505                 return;
506             }
507
508             if_chain! {
509                 if !pat.span.from_expansion();
510                 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
511                 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
512                 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
513                 then {
514                     let mut app = Applicability::MachineApplicable;
515                     let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
516                     self.current_body = self.current_body.or(cx.enclosing_body);
517                     self.ref_locals.insert(
518                         id,
519                         Some(RefPat {
520                             always_deref: true,
521                             spans: vec![pat.span],
522                             app,
523                             replacements: vec![(pat.span, snip.into())],
524                             hir_id: pat.hir_id
525                         }),
526                     );
527                 }
528             }
529         }
530     }
531
532     fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
533         if Some(body.id()) == self.current_body {
534             for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
535                 let replacements = pat.replacements;
536                 let app = pat.app;
537                 let lint = if pat.always_deref {
538                     NEEDLESS_BORROW
539                 } else {
540                     REF_BINDING_TO_REFERENCE
541                 };
542                 span_lint_hir_and_then(
543                     cx,
544                     lint,
545                     pat.hir_id,
546                     pat.spans,
547                     "this pattern creates a reference to a reference",
548                     |diag| {
549                         diag.multipart_suggestion("try this", replacements, app);
550                     },
551                 );
552             }
553             self.current_body = None;
554         }
555     }
556 }
557
558 fn try_parse_ref_op<'tcx>(
559     tcx: TyCtxt<'tcx>,
560     typeck: &'tcx TypeckResults<'_>,
561     expr: &'tcx Expr<'_>,
562 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
563     let (def_id, arg) = match expr.kind {
564         ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
565         ExprKind::Call(
566             Expr {
567                 kind: ExprKind::Path(path),
568                 hir_id,
569                 ..
570             },
571             [arg],
572         ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
573         ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
574             return Some((RefOp::Deref, sub_expr));
575         },
576         ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
577         _ => return None,
578     };
579     if tcx.is_diagnostic_item(sym::deref_method, def_id) {
580         Some((RefOp::Method(Mutability::Not), arg))
581     } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
582         Some((RefOp::Method(Mutability::Mut), arg))
583     } else {
584         None
585     }
586 }
587
588 // Checks whether the type for a deref call actually changed the type, not just the mutability of
589 // the reference.
590 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
591     match (result_ty.kind(), arg_ty.kind()) {
592         (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
593
594         // The result type for a deref method is always a reference
595         // Not matching the previous pattern means the argument type is not a reference
596         // This means that the type did change
597         _ => false,
598     }
599 }
600
601 /// The position of an expression relative to it's parent.
602 #[derive(Clone, Copy)]
603 enum Position {
604     MethodReceiver,
605     /// The method is defined on a reference type. e.g. `impl Foo for &T`
606     MethodReceiverRefImpl,
607     Callee,
608     FieldAccess(Symbol),
609     Postfix,
610     Deref,
611     /// Any other location which will trigger auto-deref to a specific time.
612     DerefStable(i8),
613     /// Any other location which will trigger auto-reborrowing.
614     ReborrowStable(i8),
615     Other(i8),
616 }
617 impl Position {
618     fn is_deref_stable(self) -> bool {
619         matches!(self, Self::DerefStable(_))
620     }
621
622     fn is_reborrow_stable(self) -> bool {
623         matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
624     }
625
626     fn can_auto_borrow(self) -> bool {
627         matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
628     }
629
630     fn lint_explicit_deref(self) -> bool {
631         matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
632     }
633
634     fn precedence(self) -> i8 {
635         match self {
636             Self::MethodReceiver
637             | Self::MethodReceiverRefImpl
638             | Self::Callee
639             | Self::FieldAccess(_)
640             | Self::Postfix => PREC_POSTFIX,
641             Self::Deref => PREC_PREFIX,
642             Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
643         }
644     }
645 }
646
647 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
648 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
649 /// locations as those follow different rules.
650 #[allow(clippy::too_many_lines)]
651 fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
652     let mut adjustments = [].as_slice();
653     let mut precedence = 0i8;
654     let ctxt = e.span.ctxt();
655     let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
656         // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
657         if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
658             adjustments = cx.typeck_results().expr_adjustments(e);
659         }
660         match parent {
661             Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
662                 Some(binding_ty_auto_deref_stability(ty, precedence))
663             },
664             Node::Item(&Item {
665                 kind: ItemKind::Static(..) | ItemKind::Const(..),
666                 def_id,
667                 span,
668                 ..
669             })
670             | Node::TraitItem(&TraitItem {
671                 kind: TraitItemKind::Const(..),
672                 def_id,
673                 span,
674                 ..
675             })
676             | Node::ImplItem(&ImplItem {
677                 kind: ImplItemKind::Const(..),
678                 def_id,
679                 span,
680                 ..
681             }) if span.ctxt() == ctxt => {
682                 let ty = cx.tcx.type_of(def_id);
683                 Some(if ty.is_ref() {
684                     Position::DerefStable(precedence)
685                 } else {
686                     Position::Other(precedence)
687                 })
688             },
689
690             Node::Item(&Item {
691                 kind: ItemKind::Fn(..),
692                 def_id,
693                 span,
694                 ..
695             })
696             | Node::TraitItem(&TraitItem {
697                 kind: TraitItemKind::Fn(..),
698                 def_id,
699                 span,
700                 ..
701             })
702             | Node::ImplItem(&ImplItem {
703                 kind: ImplItemKind::Fn(..),
704                 def_id,
705                 span,
706                 ..
707             }) if span.ctxt() == ctxt => {
708                 let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
709                 Some(if !output.is_ref() {
710                     Position::Other(precedence)
711                 } else if output.has_placeholders() || output.has_opaque_types() {
712                     Position::ReborrowStable(precedence)
713                 } else {
714                     Position::DerefStable(precedence)
715                 })
716             },
717
718             Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
719                 ExprKind::Ret(_) => {
720                     let output = cx
721                         .tcx
722                         .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
723                         .skip_binder()
724                         .output();
725                     Some(if !output.is_ref() {
726                         Position::Other(precedence)
727                     } else if output.has_placeholders() || output.has_opaque_types() {
728                         Position::ReborrowStable(precedence)
729                     } else {
730                         Position::DerefStable(precedence)
731                     })
732                 },
733                 ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
734                 ExprKind::Call(func, args) => args
735                     .iter()
736                     .position(|arg| arg.hir_id == child_id)
737                     .zip(expr_sig(cx, func))
738                     .and_then(|(i, sig)| sig.input_with_hir(i))
739                     .map(|(hir_ty, ty)| match hir_ty {
740                         // Type inference for closures can depend on how they're called. Only go by the explicit
741                         // types here.
742                         Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
743                         None => param_auto_deref_stability(ty.skip_binder(), precedence),
744                     }),
745                 ExprKind::MethodCall(_, args, _) => {
746                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
747                     args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
748                         if i == 0 {
749                             // Check for calls to trait methods where the trait is implemented on a reference.
750                             // Two cases need to be handled:
751                             // * `self` methods on `&T` will never have auto-borrow
752                             // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
753                             //   priority.
754                             if e.hir_id != child_id {
755                                 Position::ReborrowStable(precedence)
756                             } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
757                                 && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
758                                 && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
759                                 && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
760                                     || cx.tcx.mk_substs([].iter())
761                                 ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
762                                     // Trait methods taking `&self`
763                                     sub_ty
764                                 } else {
765                                     // Trait methods taking `self`
766                                     arg_ty
767                                 } && impl_ty.is_ref()
768                                 && cx.tcx.infer_ctxt().enter(|infcx|
769                                     infcx
770                                         .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
771                                         .must_apply_modulo_regions()
772                                 )
773                             {
774                                 Position::MethodReceiverRefImpl
775                             } else {
776                                 Position::MethodReceiver
777                             }
778                         } else {
779                             param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
780                         }
781                     })
782                 },
783                 ExprKind::Struct(path, fields, _) => {
784                     let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
785                     fields
786                         .iter()
787                         .find(|f| f.expr.hir_id == child_id)
788                         .zip(variant)
789                         .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
790                         .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
791                 },
792                 ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
793                 ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
794                 ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
795                 | ExprKind::Index(child, _)
796                     if child.hir_id == e.hir_id =>
797                 {
798                     Some(Position::Postfix)
799                 },
800                 _ if child_id == e.hir_id => {
801                     precedence = parent.precedence().order();
802                     None
803                 },
804                 _ => None,
805             },
806             _ => None,
807         }
808     })
809     .unwrap_or(Position::Other(precedence));
810     (position, adjustments)
811 }
812
813 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
814 //
815 // e.g.
816 // let x = Box::new(Box::new(0u32));
817 // let y1: &Box<_> = x.deref();
818 // let y2: &Box<_> = &x;
819 //
820 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
821 // switching to auto-dereferencing.
822 fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
823     let TyKind::Rptr(_, ty) = &ty.kind else {
824         return Position::Other(precedence);
825     };
826     let mut ty = ty;
827
828     loop {
829         break match ty.ty.kind {
830             TyKind::Rptr(_, ref ref_ty) => {
831                 ty = ref_ty;
832                 continue;
833             },
834             TyKind::Path(
835                 QPath::TypeRelative(_, path)
836                 | QPath::Resolved(
837                     _,
838                     Path {
839                         segments: [.., path], ..
840                     },
841                 ),
842             ) => {
843                 if let Some(args) = path.args
844                     && args.args.iter().any(|arg| match arg {
845                         GenericArg::Infer(_) => true,
846                         GenericArg::Type(ty) => ty_contains_infer(ty),
847                         _ => false,
848                     })
849                 {
850                     Position::ReborrowStable(precedence)
851                 } else {
852                     Position::DerefStable(precedence)
853                 }
854             },
855             TyKind::Slice(_)
856             | TyKind::Array(..)
857             | TyKind::BareFn(_)
858             | TyKind::Never
859             | TyKind::Tup(_)
860             | TyKind::Ptr(_)
861             | TyKind::TraitObject(..)
862             | TyKind::Path(_) => Position::DerefStable(precedence),
863             TyKind::OpaqueDef(..)
864             | TyKind::Infer
865             | TyKind::Typeof(..)
866             | TyKind::Err => Position::ReborrowStable(precedence),
867         };
868     }
869 }
870
871 // Checks whether a type is inferred at some point.
872 // e.g. `_`, `Box<_>`, `[_]`
873 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
874     struct V(bool);
875     impl Visitor<'_> for V {
876         fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
877             if self.0
878                 || matches!(
879                     ty.kind,
880                     TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
881                 )
882             {
883                 self.0 = true;
884             } else {
885                 walk_ty(self, ty);
886             }
887         }
888
889         fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
890             if self.0 || matches!(arg, GenericArg::Infer(_)) {
891                 self.0 = true;
892             } else if let GenericArg::Type(ty) = arg {
893                 self.visit_ty(ty);
894             }
895         }
896     }
897     let mut v = V(false);
898     v.visit_ty(ty);
899     v.0
900 }
901
902 // Checks whether a type is stable when switching to auto dereferencing,
903 fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
904     let ty::Ref(_, mut ty, _) = *ty.kind() else {
905         return Position::Other(precedence);
906     };
907
908     loop {
909         break match *ty.kind() {
910             ty::Ref(_, ref_ty, _) => {
911                 ty = ref_ty;
912                 continue;
913             },
914             ty::Infer(_)
915             | ty::Error(_)
916             | ty::Param(_)
917             | ty::Bound(..)
918             | ty::Opaque(..)
919             | ty::Placeholder(_)
920             | ty::Dynamic(..) => Position::ReborrowStable(precedence),
921             ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
922                 Position::ReborrowStable(precedence)
923             },
924             ty::Adt(..)
925             | ty::Bool
926             | ty::Char
927             | ty::Int(_)
928             | ty::Uint(_)
929             | ty::Float(_)
930             | ty::Foreign(_)
931             | ty::Str
932             | ty::Array(..)
933             | ty::Slice(..)
934             | ty::RawPtr(..)
935             | ty::FnDef(..)
936             | ty::FnPtr(_)
937             | ty::Closure(..)
938             | ty::Generator(..)
939             | ty::GeneratorWitness(..)
940             | ty::Never
941             | ty::Tuple(_)
942             | ty::Projection(_) => Position::DerefStable(precedence),
943         };
944     }
945 }
946
947 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
948     if let ty::Adt(adt, _) = *ty.kind() {
949         adt.is_struct() && adt.all_fields().any(|f| f.name == name)
950     } else {
951         false
952     }
953 }
954
955 #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
956 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
957     match state {
958         State::DerefMethod {
959             ty_changed_count,
960             is_final_ufcs,
961             target_mut,
962         } => {
963             let mut app = Applicability::MachineApplicable;
964             let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
965             let ty = cx.typeck_results().expr_ty(expr);
966             let (_, ref_count) = peel_mid_ty_refs(ty);
967             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
968                 // a deref call changing &T -> &U requires two deref operators the first time
969                 // this occurs. One to remove the reference, a second to call the deref impl.
970                 "*".repeat(ty_changed_count + 1)
971             } else {
972                 "*".repeat(ty_changed_count)
973             };
974             let addr_of_str = if ty_changed_count < ref_count {
975                 // Check if a reborrow from &mut T -> &T is required.
976                 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
977                     "&*"
978                 } else {
979                     ""
980                 }
981             } else if target_mut == Mutability::Mut {
982                 "&mut "
983             } else {
984                 "&"
985             };
986
987             let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
988                 format!("({})", expr_str)
989             } else {
990                 expr_str.into_owned()
991             };
992
993             span_lint_and_sugg(
994                 cx,
995                 EXPLICIT_DEREF_METHODS,
996                 data.span,
997                 match target_mut {
998                     Mutability::Not => "explicit `deref` method call",
999                     Mutability::Mut => "explicit `deref_mut` method call",
1000                 },
1001                 "try this",
1002                 format!("{}{}{}", addr_of_str, deref_str, expr_str),
1003                 app,
1004             );
1005         },
1006         State::DerefedBorrow(state) => {
1007             let mut app = Applicability::MachineApplicable;
1008             let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1009             span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
1010                 let sugg = if !snip_is_macro
1011                     && expr.precedence().order() < data.position.precedence()
1012                     && !has_enclosing_paren(&snip)
1013                 {
1014                     format!("({})", snip)
1015                 } else {
1016                     snip.into()
1017                 };
1018                 diag.span_suggestion(data.span, "change this to", sugg, app);
1019             });
1020         },
1021         State::ExplicitDeref { deref_span_id } => {
1022             let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
1023                 && !cx.typeck_results().expr_ty(expr).is_ref()
1024             {
1025                 (span, hir_id, PREC_PREFIX)
1026             } else {
1027                 (data.span, data.hir_id, data.position.precedence())
1028             };
1029             span_lint_hir_and_then(
1030                 cx,
1031                 EXPLICIT_AUTO_DEREF,
1032                 hir_id,
1033                 span,
1034                 "deref which would be done by auto-deref",
1035                 |diag| {
1036                     let mut app = Applicability::MachineApplicable;
1037                     let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
1038                     let sugg =
1039                         if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
1040                             format!("({})", snip)
1041                         } else {
1042                             snip.into()
1043                         };
1044                     diag.span_suggestion(span, "try this", sugg, app);
1045                 },
1046             );
1047         },
1048         State::ExplicitDerefField { .. } => {
1049             span_lint_hir_and_then(
1050                 cx,
1051                 EXPLICIT_AUTO_DEREF,
1052                 data.hir_id,
1053                 data.span,
1054                 "deref which would be done by auto-deref",
1055                 |diag| {
1056                     let mut app = Applicability::MachineApplicable;
1057                     let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
1058                     diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
1059                 },
1060             );
1061         },
1062         State::Borrow | State::Reborrow { .. } => (),
1063     }
1064 }
1065
1066 impl Dereferencing {
1067     fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1068         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1069             if let Some(pat) = outer_pat {
1070                 // Check for auto-deref
1071                 if !matches!(
1072                     cx.typeck_results().expr_adjustments(e),
1073                     [
1074                         Adjustment {
1075                             kind: Adjust::Deref(_),
1076                             ..
1077                         },
1078                         Adjustment {
1079                             kind: Adjust::Deref(_),
1080                             ..
1081                         },
1082                         ..
1083                     ]
1084                 ) {
1085                     match get_parent_expr(cx, e) {
1086                         // Field accesses are the same no matter the number of references.
1087                         Some(Expr {
1088                             kind: ExprKind::Field(..),
1089                             ..
1090                         }) => (),
1091                         Some(&Expr {
1092                             span,
1093                             kind: ExprKind::Unary(UnOp::Deref, _),
1094                             ..
1095                         }) if !span.from_expansion() => {
1096                             // Remove explicit deref.
1097                             let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1098                             pat.replacements.push((span, snip.into()));
1099                         },
1100                         Some(parent) if !parent.span.from_expansion() => {
1101                             // Double reference might be needed at this point.
1102                             if parent.precedence().order() == PREC_POSTFIX {
1103                                 // Parentheses would be needed here, don't lint.
1104                                 *outer_pat = None;
1105                             } else {
1106                                 pat.always_deref = false;
1107                                 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1108                                 pat.replacements.push((e.span, format!("&{}", snip)));
1109                             }
1110                         },
1111                         _ if !e.span.from_expansion() => {
1112                             // Double reference might be needed at this point.
1113                             pat.always_deref = false;
1114                             let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1115                             pat.replacements.push((e.span, format!("&{}", snip)));
1116                         },
1117                         // Edge case for macros. The span of the identifier will usually match the context of the
1118                         // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1119                         // macros
1120                         _ => *outer_pat = None,
1121                     }
1122                 }
1123             }
1124         }
1125     }
1126 }