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