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