]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
Fix ICE in `dereference.rs`
[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, FnRetTy, 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 owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
721                     Some(
722                         if let Node::Expr(Expr {
723                             kind: ExprKind::Closure { fn_decl, .. },
724                             ..
725                         }) = cx.tcx.hir().get(owner_id)
726                         {
727                             match fn_decl.output {
728                                 FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
729                                 FnRetTy::DefaultReturn(_) => Position::Other(precedence),
730                             }
731                         } else {
732                             let output = cx
733                                 .tcx
734                                 .fn_sig(cx.tcx.hir().local_def_id(owner_id))
735                                 .skip_binder()
736                                 .output();
737                             if !output.is_ref() {
738                                 Position::Other(precedence)
739                             } else if output.has_placeholders() || output.has_opaque_types() {
740                                 Position::ReborrowStable(precedence)
741                             } else {
742                                 Position::DerefStable(precedence)
743                             }
744                         },
745                     )
746                 },
747                 ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
748                 ExprKind::Call(func, args) => args
749                     .iter()
750                     .position(|arg| arg.hir_id == child_id)
751                     .zip(expr_sig(cx, func))
752                     .and_then(|(i, sig)| sig.input_with_hir(i))
753                     .map(|(hir_ty, ty)| match hir_ty {
754                         // Type inference for closures can depend on how they're called. Only go by the explicit
755                         // types here.
756                         Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
757                         None => param_auto_deref_stability(ty.skip_binder(), precedence),
758                     }),
759                 ExprKind::MethodCall(_, args, _) => {
760                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
761                     args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
762                         if i == 0 {
763                             // Check for calls to trait methods where the trait is implemented on a reference.
764                             // Two cases need to be handled:
765                             // * `self` methods on `&T` will never have auto-borrow
766                             // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
767                             //   priority.
768                             if e.hir_id != child_id {
769                                 Position::ReborrowStable(precedence)
770                             } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
771                                 && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
772                                 && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
773                                 && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
774                                     || cx.tcx.mk_substs([].iter())
775                                 ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
776                                     // Trait methods taking `&self`
777                                     sub_ty
778                                 } else {
779                                     // Trait methods taking `self`
780                                     arg_ty
781                                 } && impl_ty.is_ref()
782                                 && cx.tcx.infer_ctxt().enter(|infcx|
783                                     infcx
784                                         .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
785                                         .must_apply_modulo_regions()
786                                 )
787                             {
788                                 Position::MethodReceiverRefImpl
789                             } else {
790                                 Position::MethodReceiver
791                             }
792                         } else {
793                             param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
794                         }
795                     })
796                 },
797                 ExprKind::Struct(path, fields, _) => {
798                     let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
799                     fields
800                         .iter()
801                         .find(|f| f.expr.hir_id == child_id)
802                         .zip(variant)
803                         .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
804                         .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
805                 },
806                 ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
807                 ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
808                 ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
809                 | ExprKind::Index(child, _)
810                     if child.hir_id == e.hir_id =>
811                 {
812                     Some(Position::Postfix)
813                 },
814                 _ if child_id == e.hir_id => {
815                     precedence = parent.precedence().order();
816                     None
817                 },
818                 _ => None,
819             },
820             _ => None,
821         }
822     })
823     .unwrap_or(Position::Other(precedence));
824     (position, adjustments)
825 }
826
827 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
828 //
829 // e.g.
830 // let x = Box::new(Box::new(0u32));
831 // let y1: &Box<_> = x.deref();
832 // let y2: &Box<_> = &x;
833 //
834 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
835 // switching to auto-dereferencing.
836 fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
837     let TyKind::Rptr(_, ty) = &ty.kind else {
838         return Position::Other(precedence);
839     };
840     let mut ty = ty;
841
842     loop {
843         break match ty.ty.kind {
844             TyKind::Rptr(_, ref ref_ty) => {
845                 ty = ref_ty;
846                 continue;
847             },
848             TyKind::Path(
849                 QPath::TypeRelative(_, path)
850                 | QPath::Resolved(
851                     _,
852                     Path {
853                         segments: [.., path], ..
854                     },
855                 ),
856             ) => {
857                 if let Some(args) = path.args
858                     && args.args.iter().any(|arg| match arg {
859                         GenericArg::Infer(_) => true,
860                         GenericArg::Type(ty) => ty_contains_infer(ty),
861                         _ => false,
862                     })
863                 {
864                     Position::ReborrowStable(precedence)
865                 } else {
866                     Position::DerefStable(precedence)
867                 }
868             },
869             TyKind::Slice(_)
870             | TyKind::Array(..)
871             | TyKind::BareFn(_)
872             | TyKind::Never
873             | TyKind::Tup(_)
874             | TyKind::Ptr(_)
875             | TyKind::TraitObject(..)
876             | TyKind::Path(_) => Position::DerefStable(precedence),
877             TyKind::OpaqueDef(..)
878             | TyKind::Infer
879             | TyKind::Typeof(..)
880             | TyKind::Err => Position::ReborrowStable(precedence),
881         };
882     }
883 }
884
885 // Checks whether a type is inferred at some point.
886 // e.g. `_`, `Box<_>`, `[_]`
887 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
888     struct V(bool);
889     impl Visitor<'_> for V {
890         fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
891             if self.0
892                 || matches!(
893                     ty.kind,
894                     TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
895                 )
896             {
897                 self.0 = true;
898             } else {
899                 walk_ty(self, ty);
900             }
901         }
902
903         fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
904             if self.0 || matches!(arg, GenericArg::Infer(_)) {
905                 self.0 = true;
906             } else if let GenericArg::Type(ty) = arg {
907                 self.visit_ty(ty);
908             }
909         }
910     }
911     let mut v = V(false);
912     v.visit_ty(ty);
913     v.0
914 }
915
916 // Checks whether a type is stable when switching to auto dereferencing,
917 fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
918     let ty::Ref(_, mut ty, _) = *ty.kind() else {
919         return Position::Other(precedence);
920     };
921
922     loop {
923         break match *ty.kind() {
924             ty::Ref(_, ref_ty, _) => {
925                 ty = ref_ty;
926                 continue;
927             },
928             ty::Infer(_)
929             | ty::Error(_)
930             | ty::Param(_)
931             | ty::Bound(..)
932             | ty::Opaque(..)
933             | ty::Placeholder(_)
934             | ty::Dynamic(..) => Position::ReborrowStable(precedence),
935             ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
936                 Position::ReborrowStable(precedence)
937             },
938             ty::Adt(..)
939             | ty::Bool
940             | ty::Char
941             | ty::Int(_)
942             | ty::Uint(_)
943             | ty::Float(_)
944             | ty::Foreign(_)
945             | ty::Str
946             | ty::Array(..)
947             | ty::Slice(..)
948             | ty::RawPtr(..)
949             | ty::FnDef(..)
950             | ty::FnPtr(_)
951             | ty::Closure(..)
952             | ty::Generator(..)
953             | ty::GeneratorWitness(..)
954             | ty::Never
955             | ty::Tuple(_)
956             | ty::Projection(_) => Position::DerefStable(precedence),
957         };
958     }
959 }
960
961 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
962     if let ty::Adt(adt, _) = *ty.kind() {
963         adt.is_struct() && adt.all_fields().any(|f| f.name == name)
964     } else {
965         false
966     }
967 }
968
969 #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
970 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
971     match state {
972         State::DerefMethod {
973             ty_changed_count,
974             is_final_ufcs,
975             target_mut,
976         } => {
977             let mut app = Applicability::MachineApplicable;
978             let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
979             let ty = cx.typeck_results().expr_ty(expr);
980             let (_, ref_count) = peel_mid_ty_refs(ty);
981             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
982                 // a deref call changing &T -> &U requires two deref operators the first time
983                 // this occurs. One to remove the reference, a second to call the deref impl.
984                 "*".repeat(ty_changed_count + 1)
985             } else {
986                 "*".repeat(ty_changed_count)
987             };
988             let addr_of_str = if ty_changed_count < ref_count {
989                 // Check if a reborrow from &mut T -> &T is required.
990                 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
991                     "&*"
992                 } else {
993                     ""
994                 }
995             } else if target_mut == Mutability::Mut {
996                 "&mut "
997             } else {
998                 "&"
999             };
1000
1001             let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
1002                 format!("({})", expr_str)
1003             } else {
1004                 expr_str.into_owned()
1005             };
1006
1007             span_lint_and_sugg(
1008                 cx,
1009                 EXPLICIT_DEREF_METHODS,
1010                 data.span,
1011                 match target_mut {
1012                     Mutability::Not => "explicit `deref` method call",
1013                     Mutability::Mut => "explicit `deref_mut` method call",
1014                 },
1015                 "try this",
1016                 format!("{}{}{}", addr_of_str, deref_str, expr_str),
1017                 app,
1018             );
1019         },
1020         State::DerefedBorrow(state) => {
1021             let mut app = Applicability::MachineApplicable;
1022             let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1023             span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
1024                 let sugg = if !snip_is_macro
1025                     && expr.precedence().order() < data.position.precedence()
1026                     && !has_enclosing_paren(&snip)
1027                 {
1028                     format!("({})", snip)
1029                 } else {
1030                     snip.into()
1031                 };
1032                 diag.span_suggestion(data.span, "change this to", sugg, app);
1033             });
1034         },
1035         State::ExplicitDeref { deref_span_id } => {
1036             let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
1037                 && !cx.typeck_results().expr_ty(expr).is_ref()
1038             {
1039                 (span, hir_id, PREC_PREFIX)
1040             } else {
1041                 (data.span, data.hir_id, data.position.precedence())
1042             };
1043             span_lint_hir_and_then(
1044                 cx,
1045                 EXPLICIT_AUTO_DEREF,
1046                 hir_id,
1047                 span,
1048                 "deref which would be done by auto-deref",
1049                 |diag| {
1050                     let mut app = Applicability::MachineApplicable;
1051                     let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
1052                     let sugg =
1053                         if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
1054                             format!("({})", snip)
1055                         } else {
1056                             snip.into()
1057                         };
1058                     diag.span_suggestion(span, "try this", sugg, app);
1059                 },
1060             );
1061         },
1062         State::ExplicitDerefField { .. } => {
1063             span_lint_hir_and_then(
1064                 cx,
1065                 EXPLICIT_AUTO_DEREF,
1066                 data.hir_id,
1067                 data.span,
1068                 "deref which would be done by auto-deref",
1069                 |diag| {
1070                     let mut app = Applicability::MachineApplicable;
1071                     let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
1072                     diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
1073                 },
1074             );
1075         },
1076         State::Borrow | State::Reborrow { .. } => (),
1077     }
1078 }
1079
1080 impl Dereferencing {
1081     fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1082         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1083             if let Some(pat) = outer_pat {
1084                 // Check for auto-deref
1085                 if !matches!(
1086                     cx.typeck_results().expr_adjustments(e),
1087                     [
1088                         Adjustment {
1089                             kind: Adjust::Deref(_),
1090                             ..
1091                         },
1092                         Adjustment {
1093                             kind: Adjust::Deref(_),
1094                             ..
1095                         },
1096                         ..
1097                     ]
1098                 ) {
1099                     match get_parent_expr(cx, e) {
1100                         // Field accesses are the same no matter the number of references.
1101                         Some(Expr {
1102                             kind: ExprKind::Field(..),
1103                             ..
1104                         }) => (),
1105                         Some(&Expr {
1106                             span,
1107                             kind: ExprKind::Unary(UnOp::Deref, _),
1108                             ..
1109                         }) if !span.from_expansion() => {
1110                             // Remove explicit deref.
1111                             let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1112                             pat.replacements.push((span, snip.into()));
1113                         },
1114                         Some(parent) if !parent.span.from_expansion() => {
1115                             // Double reference might be needed at this point.
1116                             if parent.precedence().order() == PREC_POSTFIX {
1117                                 // Parentheses would be needed here, don't lint.
1118                                 *outer_pat = None;
1119                             } else {
1120                                 pat.always_deref = false;
1121                                 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1122                                 pat.replacements.push((e.span, format!("&{}", snip)));
1123                             }
1124                         },
1125                         _ if !e.span.from_expansion() => {
1126                             // Double reference might be needed at this point.
1127                             pat.always_deref = false;
1128                             let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1129                             pat.replacements.push((e.span, format!("&{}", snip)));
1130                         },
1131                         // Edge case for macros. The span of the identifier will usually match the context of the
1132                         // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1133                         // macros
1134                         _ => *outer_pat = None,
1135                     }
1136                 }
1137             }
1138         }
1139     }
1140 }