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