]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
Auto merge of #9167 - aldhsu:fix-trait-duplication-false-pos, r=flip1995
[rust.git] / clippy_lints / src / dereference.rs
1 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
2 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
3 use clippy_utils::sugg::has_enclosing_paren;
4 use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, 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, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
19 use rustc_session::{declare_tool_lint, impl_lint_pass};
20 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
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         mutability: Option<Mutability>,
187     },
188     ExplicitDerefField {
189         name: Symbol,
190     },
191     Reborrow {
192         mutability: Mutability,
193     },
194     Borrow {
195         mutability: Mutability,
196     },
197 }
198
199 // A reference operation considered by this lint pass
200 enum RefOp {
201     Method(Mutability),
202     Deref,
203     AddrOf(Mutability),
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 { mutability: 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(mutability) => {
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                             // Auto-deref doesn't combine with other adjustments
362                             && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
363                             && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
364                         {
365                             self.state = Some((
366                                 State::Borrow { mutability },
367                                 StateData {
368                                     span: expr.span,
369                                     hir_id: expr.hir_id,
370                                     position
371                                 },
372                             ));
373                         }
374                     },
375                     RefOp::Method(..) => (),
376                 }
377             },
378             (
379                 Some((
380                     State::DerefMethod {
381                         target_mut,
382                         ty_changed_count,
383                         ..
384                     },
385                     data,
386                 )),
387                 RefOp::Method(_),
388             ) => {
389                 self.state = Some((
390                     State::DerefMethod {
391                         ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
392                             ty_changed_count
393                         } else {
394                             ty_changed_count + 1
395                         },
396                         is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
397                         target_mut,
398                     },
399                     data,
400                 ));
401             },
402             (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
403                 self.state = Some((
404                     State::DerefedBorrow(DerefedBorrow {
405                         count: state.count - 1,
406                         ..state
407                     }),
408                     data,
409                 ));
410             },
411             (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
412                 let position = data.position;
413                 report(cx, expr, State::DerefedBorrow(state), data);
414                 if position.is_deref_stable() {
415                     self.state = Some((
416                         State::Borrow { mutability },
417                         StateData {
418                             span: expr.span,
419                             hir_id: expr.hir_id,
420                             position,
421                         },
422                     ));
423                 }
424             },
425             (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
426                 let position = data.position;
427                 report(cx, expr, State::DerefedBorrow(state), data);
428                 if let Position::FieldAccess(name) = position
429                     && !ty_contains_field(typeck.expr_ty(sub_expr), name)
430                 {
431                     self.state = Some((
432                         State::ExplicitDerefField { name },
433                         StateData { span: expr.span, hir_id: expr.hir_id, position },
434                     ));
435                 } else if position.is_deref_stable() {
436                     self.state = Some((
437                         State::ExplicitDeref { mutability: None },
438                         StateData { span: expr.span, hir_id: expr.hir_id, position },
439                     ));
440                 }
441             },
442
443             (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
444                 if typeck.expr_ty(sub_expr).is_ref() {
445                     self.state = Some((State::Reborrow { mutability }, data));
446                 } else {
447                     self.state = Some((
448                         State::ExplicitDeref {
449                             mutability: Some(mutability),
450                         },
451                         data,
452                     ));
453                 }
454             },
455             (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
456                 self.state = Some((
457                     State::ExplicitDeref {
458                         mutability: Some(mutability),
459                     },
460                     data,
461                 ));
462             },
463             (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
464                 self.state = state;
465             },
466             (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
467                 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
468             {
469                 self.state = Some((State::ExplicitDerefField { name }, data));
470             },
471
472             (Some((state, data)), _) => report(cx, expr, state, data),
473         }
474     }
475
476     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
477         if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
478             if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
479                 // This binding id has been seen before. Add this pattern to the list of changes.
480                 if let Some(prev_pat) = opt_prev_pat {
481                     if pat.span.from_expansion() {
482                         // Doesn't match the context of the previous pattern. Can't lint here.
483                         *opt_prev_pat = None;
484                     } else {
485                         prev_pat.spans.push(pat.span);
486                         prev_pat.replacements.push((
487                             pat.span,
488                             snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
489                                 .0
490                                 .into(),
491                         ));
492                     }
493                 }
494                 return;
495             }
496
497             if_chain! {
498                 if !pat.span.from_expansion();
499                 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
500                 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
501                 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
502                 then {
503                     let mut app = Applicability::MachineApplicable;
504                     let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
505                     self.current_body = self.current_body.or(cx.enclosing_body);
506                     self.ref_locals.insert(
507                         id,
508                         Some(RefPat {
509                             always_deref: true,
510                             spans: vec![pat.span],
511                             app,
512                             replacements: vec![(pat.span, snip.into())],
513                             hir_id: pat.hir_id
514                         }),
515                     );
516                 }
517             }
518         }
519     }
520
521     fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
522         if Some(body.id()) == self.current_body {
523             for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
524                 let replacements = pat.replacements;
525                 let app = pat.app;
526                 let lint = if pat.always_deref {
527                     NEEDLESS_BORROW
528                 } else {
529                     REF_BINDING_TO_REFERENCE
530                 };
531                 span_lint_hir_and_then(
532                     cx,
533                     lint,
534                     pat.hir_id,
535                     pat.spans,
536                     "this pattern creates a reference to a reference",
537                     |diag| {
538                         diag.multipart_suggestion("try this", replacements, app);
539                     },
540                 );
541             }
542             self.current_body = None;
543         }
544     }
545 }
546
547 fn try_parse_ref_op<'tcx>(
548     tcx: TyCtxt<'tcx>,
549     typeck: &'tcx TypeckResults<'_>,
550     expr: &'tcx Expr<'_>,
551 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
552     let (def_id, arg) = match expr.kind {
553         ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
554         ExprKind::Call(
555             Expr {
556                 kind: ExprKind::Path(path),
557                 hir_id,
558                 ..
559             },
560             [arg],
561         ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
562         ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
563             return Some((RefOp::Deref, sub_expr));
564         },
565         ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
566         _ => return None,
567     };
568     if tcx.is_diagnostic_item(sym::deref_method, def_id) {
569         Some((RefOp::Method(Mutability::Not), arg))
570     } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
571         Some((RefOp::Method(Mutability::Mut), arg))
572     } else {
573         None
574     }
575 }
576
577 // Checks whether the type for a deref call actually changed the type, not just the mutability of
578 // the reference.
579 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
580     match (result_ty.kind(), arg_ty.kind()) {
581         (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
582
583         // The result type for a deref method is always a reference
584         // Not matching the previous pattern means the argument type is not a reference
585         // This means that the type did change
586         _ => false,
587     }
588 }
589
590 /// The position of an expression relative to it's parent.
591 #[derive(Clone, Copy)]
592 enum Position {
593     MethodReceiver,
594     /// The method is defined on a reference type. e.g. `impl Foo for &T`
595     MethodReceiverRefImpl,
596     Callee,
597     FieldAccess(Symbol),
598     Postfix,
599     Deref,
600     /// Any other location which will trigger auto-deref to a specific time.
601     /// Contains the precedence of the parent expression and whether the target type is sized.
602     DerefStable(i8, bool),
603     /// Any other location which will trigger auto-reborrowing.
604     /// Contains the precedence of the parent expression.
605     ReborrowStable(i8),
606     /// Contains the precedence of the parent expression.
607     Other(i8),
608 }
609 impl Position {
610     fn is_deref_stable(self) -> bool {
611         matches!(self, Self::DerefStable(..))
612     }
613
614     fn is_reborrow_stable(self) -> bool {
615         matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
616     }
617
618     fn can_auto_borrow(self) -> bool {
619         matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
620     }
621
622     fn lint_explicit_deref(self) -> bool {
623         matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
624     }
625
626     fn precedence(self) -> i8 {
627         match self {
628             Self::MethodReceiver
629             | Self::MethodReceiverRefImpl
630             | Self::Callee
631             | Self::FieldAccess(_)
632             | Self::Postfix => PREC_POSTFIX,
633             Self::Deref => PREC_PREFIX,
634             Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
635         }
636     }
637 }
638
639 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
640 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
641 /// locations as those follow different rules.
642 #[allow(clippy::too_many_lines)]
643 fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
644     let mut adjustments = [].as_slice();
645     let mut precedence = 0i8;
646     let ctxt = e.span.ctxt();
647     let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
648         // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
649         if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
650             adjustments = cx.typeck_results().expr_adjustments(e);
651         }
652         match parent {
653             Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
654                 Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
655             },
656             Node::Item(&Item {
657                 kind: ItemKind::Static(..) | ItemKind::Const(..),
658                 def_id,
659                 span,
660                 ..
661             })
662             | Node::TraitItem(&TraitItem {
663                 kind: TraitItemKind::Const(..),
664                 def_id,
665                 span,
666                 ..
667             })
668             | Node::ImplItem(&ImplItem {
669                 kind: ImplItemKind::Const(..),
670                 def_id,
671                 span,
672                 ..
673             }) if span.ctxt() == ctxt => {
674                 let ty = cx.tcx.type_of(def_id);
675                 Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
676             },
677
678             Node::Item(&Item {
679                 kind: ItemKind::Fn(..),
680                 def_id,
681                 span,
682                 ..
683             })
684             | Node::TraitItem(&TraitItem {
685                 kind: TraitItemKind::Fn(..),
686                 def_id,
687                 span,
688                 ..
689             })
690             | Node::ImplItem(&ImplItem {
691                 kind: ImplItemKind::Fn(..),
692                 def_id,
693                 span,
694                 ..
695             }) if span.ctxt() == ctxt => {
696                 let output = cx
697                     .tcx
698                     .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
699                 Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
700             },
701
702             Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
703                 ExprKind::Ret(_) => {
704                     let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
705                     Some(
706                         if let Node::Expr(
707                             closure_expr @ Expr {
708                                 kind: ExprKind::Closure(closure),
709                                 ..
710                             },
711                         ) = cx.tcx.hir().get(owner_id)
712                         {
713                             closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
714                         } else {
715                             let output = cx
716                                 .tcx
717                                 .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
718                             ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
719                         },
720                     )
721                 },
722                 ExprKind::Closure(closure) => Some(closure_result_position(
723                     cx,
724                     closure,
725                     cx.typeck_results().expr_ty(parent),
726                     precedence,
727                 )),
728                 ExprKind::Call(func, _) if func.hir_id == child_id => {
729                     (child_id == e.hir_id).then_some(Position::Callee)
730                 },
731                 ExprKind::Call(func, args) => args
732                     .iter()
733                     .position(|arg| arg.hir_id == child_id)
734                     .zip(expr_sig(cx, func))
735                     .and_then(|(i, sig)| sig.input_with_hir(i))
736                     .map(|(hir_ty, ty)| match hir_ty {
737                         // Type inference for closures can depend on how they're called. Only go by the explicit
738                         // types here.
739                         Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
740                         None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
741                             .position_for_arg(),
742                     }),
743                 ExprKind::MethodCall(_, args, _) => {
744                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
745                     args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
746                         if i == 0 {
747                             // Check for calls to trait methods where the trait is implemented on a reference.
748                             // Two cases need to be handled:
749                             // * `self` methods on `&T` will never have auto-borrow
750                             // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
751                             //   priority.
752                             if e.hir_id != child_id {
753                                 Position::ReborrowStable(precedence)
754                             } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
755                                 && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
756                                 && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
757                                 && let subs = match cx
758                                     .typeck_results()
759                                     .node_substs_opt(parent.hir_id)
760                                     .and_then(|subs| subs.get(1..))
761                                 {
762                                     Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
763                                     None => cx.tcx.mk_substs([].iter()),
764                                 } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
765                                     // Trait methods taking `&self`
766                                     sub_ty
767                                 } else {
768                                     // Trait methods taking `self`
769                                     arg_ty
770                                 } && impl_ty.is_ref()
771                                 && cx.tcx.infer_ctxt().enter(|infcx|
772                                     infcx
773                                         .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
774                                         .must_apply_modulo_regions()
775                                 )
776                             {
777                                 Position::MethodReceiverRefImpl
778                             } else {
779                                 Position::MethodReceiver
780                             }
781                         } else {
782                             ty_auto_deref_stability(
783                                 cx,
784                                 cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
785                                 precedence,
786                             )
787                             .position_for_arg()
788                         }
789                     })
790                 },
791                 ExprKind::Struct(path, fields, _) => {
792                     let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
793                     fields
794                         .iter()
795                         .find(|f| f.expr.hir_id == child_id)
796                         .zip(variant)
797                         .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
798                         .map(|field| {
799                             ty_auto_deref_stability(cx, cx.tcx.type_of(field.did), precedence).position_for_arg()
800                         })
801                 },
802                 ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
803                 ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
804                 ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
805                 | ExprKind::Index(child, _)
806                     if child.hir_id == e.hir_id =>
807                 {
808                     Some(Position::Postfix)
809                 },
810                 _ if child_id == e.hir_id => {
811                     precedence = parent.precedence().order();
812                     None
813                 },
814                 _ => None,
815             },
816             _ => None,
817         }
818     })
819     .unwrap_or(Position::Other(precedence));
820     (position, adjustments)
821 }
822
823 fn closure_result_position<'tcx>(
824     cx: &LateContext<'tcx>,
825     closure: &'tcx Closure<'_>,
826     ty: Ty<'tcx>,
827     precedence: i8,
828 ) -> Position {
829     match closure.fn_decl.output {
830         FnRetTy::Return(hir_ty) => {
831             if let Some(sig) = ty_sig(cx, ty)
832                 && let Some(output) = sig.output()
833             {
834                 binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
835             } else {
836                 Position::Other(precedence)
837             }
838         },
839         FnRetTy::DefaultReturn(_) => Position::Other(precedence),
840     }
841 }
842
843 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
844 //
845 // e.g.
846 // let x = Box::new(Box::new(0u32));
847 // let y1: &Box<_> = x.deref();
848 // let y2: &Box<_> = &x;
849 //
850 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
851 // switching to auto-dereferencing.
852 fn binding_ty_auto_deref_stability<'tcx>(
853     cx: &LateContext<'tcx>,
854     ty: &'tcx hir::Ty<'_>,
855     precedence: i8,
856     binder_args: &'tcx List<BoundVariableKind>,
857 ) -> Position {
858     let TyKind::Rptr(_, ty) = &ty.kind else {
859         return Position::Other(precedence);
860     };
861     let mut ty = ty;
862
863     loop {
864         break match ty.ty.kind {
865             TyKind::Rptr(_, ref ref_ty) => {
866                 ty = ref_ty;
867                 continue;
868             },
869             TyKind::Path(
870                 QPath::TypeRelative(_, path)
871                 | QPath::Resolved(
872                     _,
873                     Path {
874                         segments: [.., path], ..
875                     },
876                 ),
877             ) => {
878                 if let Some(args) = path.args
879                     && args.args.iter().any(|arg| match arg {
880                         GenericArg::Infer(_) => true,
881                         GenericArg::Type(ty) => ty_contains_infer(ty),
882                         _ => false,
883                     })
884                 {
885                     Position::ReborrowStable(precedence)
886                 } else {
887                     Position::DerefStable(
888                         precedence,
889                         cx.tcx
890                             .erase_late_bound_regions(Binder::bind_with_vars(
891                                 cx.typeck_results().node_type(ty.ty.hir_id),
892                                 binder_args,
893                             ))
894                             .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
895                     )
896                 }
897             },
898             TyKind::Slice(_) => Position::DerefStable(precedence, false),
899             TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
900             TyKind::Never
901             | TyKind::Tup(_)
902             | TyKind::Path(_) => Position::DerefStable(
903                 precedence,
904                 cx.tcx
905                     .erase_late_bound_regions(Binder::bind_with_vars(
906                         cx.typeck_results().node_type(ty.ty.hir_id),
907                         binder_args,
908                     ))
909                     .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
910             ),
911             TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
912                 Position::ReborrowStable(precedence)
913             },
914         };
915     }
916 }
917
918 // Checks whether a type is inferred at some point.
919 // e.g. `_`, `Box<_>`, `[_]`
920 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
921     struct V(bool);
922     impl Visitor<'_> for V {
923         fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
924             if self.0
925                 || matches!(
926                     ty.kind,
927                     TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
928                 )
929             {
930                 self.0 = true;
931             } else {
932                 walk_ty(self, ty);
933             }
934         }
935
936         fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
937             if self.0 || matches!(arg, GenericArg::Infer(_)) {
938                 self.0 = true;
939             } else if let GenericArg::Type(ty) = arg {
940                 self.visit_ty(ty);
941             }
942         }
943     }
944     let mut v = V(false);
945     v.visit_ty(ty);
946     v.0
947 }
948
949 struct TyPosition<'tcx> {
950     position: Position,
951     ty: Option<Ty<'tcx>>,
952 }
953 impl From<Position> for TyPosition<'_> {
954     fn from(position: Position) -> Self {
955         Self { position, ty: None }
956     }
957 }
958 impl<'tcx> TyPosition<'tcx> {
959     fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
960         Self {
961             position: Position::ReborrowStable(precedence),
962             ty: Some(ty),
963         }
964     }
965     fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
966         match (self.position, self.ty) {
967             (Position::ReborrowStable(precedence), Some(ty)) => {
968                 Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
969             },
970             (position, _) => position,
971         }
972     }
973     fn position_for_arg(self) -> Position {
974         self.position
975     }
976 }
977
978 // Checks whether a type is stable when switching to auto dereferencing,
979 fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
980     let ty::Ref(_, mut ty, _) = *ty.kind() else {
981         return Position::Other(precedence).into();
982     };
983
984     loop {
985         break match *ty.kind() {
986             ty::Ref(_, ref_ty, _) => {
987                 ty = ref_ty;
988                 continue;
989             },
990             ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
991             ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
992                 Position::ReborrowStable(precedence).into()
993             },
994             ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
995                 Position::ReborrowStable(precedence).into()
996             },
997             ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
998                 TyPosition::new_deref_stable_for_result(precedence, ty)
999             },
1000             ty::Bool
1001             | ty::Char
1002             | ty::Int(_)
1003             | ty::Uint(_)
1004             | ty::Array(..)
1005             | ty::Float(_)
1006             | ty::RawPtr(..)
1007             | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
1008             ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
1009             ty::Adt(..)
1010             | ty::Foreign(_)
1011             | ty::FnDef(..)
1012             | ty::Generator(..)
1013             | ty::GeneratorWitness(..)
1014             | ty::Closure(..)
1015             | ty::Never
1016             | ty::Tuple(_)
1017             | ty::Projection(_) => Position::DerefStable(
1018                 precedence,
1019                 ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
1020             )
1021             .into(),
1022         };
1023     }
1024 }
1025
1026 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
1027     if let ty::Adt(adt, _) = *ty.kind() {
1028         adt.is_struct() && adt.all_fields().any(|f| f.name == name)
1029     } else {
1030         false
1031     }
1032 }
1033
1034 #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
1035 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
1036     match state {
1037         State::DerefMethod {
1038             ty_changed_count,
1039             is_final_ufcs,
1040             target_mut,
1041         } => {
1042             let mut app = Applicability::MachineApplicable;
1043             let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1044             let ty = cx.typeck_results().expr_ty(expr);
1045             let (_, ref_count) = peel_mid_ty_refs(ty);
1046             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
1047                 // a deref call changing &T -> &U requires two deref operators the first time
1048                 // this occurs. One to remove the reference, a second to call the deref impl.
1049                 "*".repeat(ty_changed_count + 1)
1050             } else {
1051                 "*".repeat(ty_changed_count)
1052             };
1053             let addr_of_str = if ty_changed_count < ref_count {
1054                 // Check if a reborrow from &mut T -> &T is required.
1055                 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
1056                     "&*"
1057                 } else {
1058                     ""
1059                 }
1060             } else if target_mut == Mutability::Mut {
1061                 "&mut "
1062             } else {
1063                 "&"
1064             };
1065
1066             let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
1067                 format!("({})", expr_str)
1068             } else {
1069                 expr_str.into_owned()
1070             };
1071
1072             span_lint_and_sugg(
1073                 cx,
1074                 EXPLICIT_DEREF_METHODS,
1075                 data.span,
1076                 match target_mut {
1077                     Mutability::Not => "explicit `deref` method call",
1078                     Mutability::Mut => "explicit `deref_mut` method call",
1079                 },
1080                 "try this",
1081                 format!("{}{}{}", addr_of_str, deref_str, expr_str),
1082                 app,
1083             );
1084         },
1085         State::DerefedBorrow(state) => {
1086             let mut app = Applicability::MachineApplicable;
1087             let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1088             span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
1089                 let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
1090                 let sugg = if !snip_is_macro
1091                     && !has_enclosing_paren(&snip)
1092                     && (expr.precedence().order() < data.position.precedence() || calls_field)
1093                 {
1094                     format!("({})", snip)
1095                 } else {
1096                     snip.into()
1097                 };
1098                 diag.span_suggestion(data.span, "change this to", sugg, app);
1099             });
1100         },
1101         State::ExplicitDeref { mutability } => {
1102             if matches!(
1103                 expr.kind,
1104                 ExprKind::Block(..)
1105                     | ExprKind::ConstBlock(_)
1106                     | ExprKind::If(..)
1107                     | ExprKind::Loop(..)
1108                     | ExprKind::Match(..)
1109             ) && matches!(data.position, Position::DerefStable(_, true))
1110             {
1111                 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
1112                 return;
1113             }
1114
1115             let (prefix, precedence) = if let Some(mutability) = mutability
1116                 && !cx.typeck_results().expr_ty(expr).is_ref()
1117             {
1118                 let prefix = match mutability {
1119                     Mutability::Not => "&",
1120                     Mutability::Mut => "&mut ",
1121                 };
1122                 (prefix, 0)
1123             } else {
1124                 ("", data.position.precedence())
1125             };
1126             span_lint_hir_and_then(
1127                 cx,
1128                 EXPLICIT_AUTO_DEREF,
1129                 data.hir_id,
1130                 data.span,
1131                 "deref which would be done by auto-deref",
1132                 |diag| {
1133                     let mut app = Applicability::MachineApplicable;
1134                     let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1135                     let sugg =
1136                         if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
1137                             format!("{}({})", prefix, snip)
1138                         } else {
1139                             format!("{}{}", prefix, snip)
1140                         };
1141                     diag.span_suggestion(data.span, "try this", sugg, app);
1142                 },
1143             );
1144         },
1145         State::ExplicitDerefField { .. } => {
1146             if matches!(
1147                 expr.kind,
1148                 ExprKind::Block(..)
1149                     | ExprKind::ConstBlock(_)
1150                     | ExprKind::If(..)
1151                     | ExprKind::Loop(..)
1152                     | ExprKind::Match(..)
1153             ) && matches!(data.position, Position::DerefStable(_, true))
1154             {
1155                 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
1156                 return;
1157             }
1158
1159             span_lint_hir_and_then(
1160                 cx,
1161                 EXPLICIT_AUTO_DEREF,
1162                 data.hir_id,
1163                 data.span,
1164                 "deref which would be done by auto-deref",
1165                 |diag| {
1166                     let mut app = Applicability::MachineApplicable;
1167                     let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
1168                     diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
1169                 },
1170             );
1171         },
1172         State::Borrow { .. } | State::Reborrow { .. } => (),
1173     }
1174 }
1175
1176 impl Dereferencing {
1177     fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1178         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1179             if let Some(pat) = outer_pat {
1180                 // Check for auto-deref
1181                 if !matches!(
1182                     cx.typeck_results().expr_adjustments(e),
1183                     [
1184                         Adjustment {
1185                             kind: Adjust::Deref(_),
1186                             ..
1187                         },
1188                         Adjustment {
1189                             kind: Adjust::Deref(_),
1190                             ..
1191                         },
1192                         ..
1193                     ]
1194                 ) {
1195                     match get_parent_expr(cx, e) {
1196                         // Field accesses are the same no matter the number of references.
1197                         Some(Expr {
1198                             kind: ExprKind::Field(..),
1199                             ..
1200                         }) => (),
1201                         Some(&Expr {
1202                             span,
1203                             kind: ExprKind::Unary(UnOp::Deref, _),
1204                             ..
1205                         }) if !span.from_expansion() => {
1206                             // Remove explicit deref.
1207                             let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1208                             pat.replacements.push((span, snip.into()));
1209                         },
1210                         Some(parent) if !parent.span.from_expansion() => {
1211                             // Double reference might be needed at this point.
1212                             if parent.precedence().order() == PREC_POSTFIX {
1213                                 // Parentheses would be needed here, don't lint.
1214                                 *outer_pat = None;
1215                             } else {
1216                                 pat.always_deref = false;
1217                                 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1218                                 pat.replacements.push((e.span, format!("&{}", snip)));
1219                             }
1220                         },
1221                         _ if !e.span.from_expansion() => {
1222                             // Double reference might be needed at this point.
1223                             pat.always_deref = false;
1224                             let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1225                             pat.replacements.push((e.span, format!("&{}", snip)));
1226                         },
1227                         // Edge case for macros. The span of the identifier will usually match the context of the
1228                         // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1229                         // macros
1230                         _ => *outer_pat = None,
1231                     }
1232                 }
1233             }
1234         }
1235     }
1236 }