1 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
2 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
3 use clippy_utils::sugg::has_enclosing_paren;
4 use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
5 use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage};
6 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
7 use rustc_data_structures::fx::FxIndexMap;
8 use rustc_errors::Applicability;
9 use rustc_hir::intravisit::{walk_ty, Visitor};
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,
15 use rustc_infer::infer::TyCtxtInferExt;
16 use rustc_lint::{LateContext, LateLintPass};
17 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
18 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults};
19 use rustc_session::{declare_tool_lint, impl_lint_pass};
20 use rustc_span::{symbol::sym, Span, Symbol};
21 use rustc_trait_selection::infer::InferCtxtExt;
23 declare_clippy_lint! {
25 /// Checks for explicit `deref()` or `deref_mut()` method calls.
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.
33 /// use std::ops::Deref;
34 /// let a: &mut String = &mut String::from("foo");
35 /// let b: &str = a.deref();
40 /// let a: &mut String = &mut String::from("foo");
44 /// This lint excludes:
46 /// let _ = d.unwrap().deref();
48 #[clippy::version = "1.44.0"]
49 pub EXPLICIT_DEREF_METHODS,
51 "Explicit use of deref or deref_mut method while not in a method chain."
54 declare_clippy_lint! {
56 /// Checks for address of operations (`&`) that are going to
57 /// be dereferenced immediately by the compiler.
59 /// ### Why is this bad?
60 /// Suggests that the receiver of the expression borrows
65 /// fn fun(_a: &i32) {}
67 /// let x: &i32 = &&&&&&5;
73 /// # fn fun(_a: &i32) {}
77 #[clippy::version = "pre 1.29.0"]
80 "taking a reference that is going to be automatically dereferenced"
83 declare_clippy_lint! {
85 /// Checks for `ref` bindings which create a reference to a reference.
87 /// ### Why is this bad?
88 /// The address-of operator at the use site is clearer about the need for a reference.
93 /// if let Some(ref x) = x {
100 /// let x = Some("");
101 /// if let Some(x) = x {
105 #[clippy::version = "1.54.0"]
106 pub REF_BINDING_TO_REFERENCE,
108 "`ref` binding to a reference"
111 declare_clippy_lint! {
113 /// Checks for dereferencing expressions which would be covered by auto-deref.
115 /// ### Why is this bad?
116 /// This unnecessarily complicates the code.
120 /// let x = String::new();
121 /// let y: &str = &*x;
125 /// let x = String::new();
126 /// let y: &str = &x;
128 #[clippy::version = "1.60.0"]
129 pub EXPLICIT_AUTO_DEREF,
131 "dereferencing when the compiler would automatically dereference"
134 impl_lint_pass!(Dereferencing => [
135 EXPLICIT_DEREF_METHODS,
137 REF_BINDING_TO_REFERENCE,
142 pub struct Dereferencing {
143 state: Option<(State, StateData)>,
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>,
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
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
159 /// e.g. `m!(x) | Foo::Bar(ref x)`
160 ref_locals: FxIndexMap<HirId, Option<RefPat>>,
164 /// Span of the top level expression
170 struct DerefedBorrow {
176 // Any number of deref method calls.
178 // The number of calls in a sequence which changed the referenced type
179 ty_changed_count: usize,
181 /// The required mutability
182 target_mut: Mutability,
184 DerefedBorrow(DerefedBorrow),
186 // Span and id of the top-level deref expression if the parent expression is a borrow.
187 deref_span_id: Option<(Span, HirId)>,
199 // A reference operation considered by this lint pass
207 /// Whether every usage of the binding is dereferenced.
209 /// The spans of all the ref bindings for this local.
211 /// The applicability of this suggestion.
213 /// All the replacements which need to be made.
214 replacements: Vec<(Span, String)>,
215 /// The [`HirId`] that the lint should be emitted at.
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() {
227 if let Some(local) = path_to_local(expr) {
228 self.check_local_usage(cx, expr, local);
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);
239 let typeck = cx.typeck_results();
240 let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
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);
250 match (self.state.take(), kind) {
252 let expr_ty = typeck.expr_ty(expr);
253 let (position, adjustments) = walk_parents(cx, expr);
257 if let Position::FieldAccess(name) = position
258 && !ty_contains_field(typeck.expr_ty(sub_expr), name)
261 State::ExplicitDerefField { name },
262 StateData { span: expr.span, hir_id: expr.hir_id, position },
264 } else if position.is_deref_stable() {
266 State::ExplicitDeref { deref_span_id: None },
267 StateData { span: expr.span, hir_id: expr.hir_id, position },
271 RefOp::Method(target_mut)
272 if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
273 && position.lint_explicit_deref() =>
277 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
282 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
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 {
299 if !matches!(adjust.kind, Adjust::Deref(_)) {
301 } else if !adjust.target.is_ref() {
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
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
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.
324 // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
326 // // Removing the borrow will cause `x` to be moved
327 // Some(x) => &mut *x,
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";
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)
340 if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
350 if deref_count >= required_refs {
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,
358 StateData { span: expr.span, hir_id: expr.hir_id, position },
360 } else if position.is_deref_stable() {
371 RefOp::Method(..) => (),
387 ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
392 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
398 (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
400 State::DerefedBorrow(DerefedBorrow {
401 count: state.count - 1,
407 (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
408 let position = data.position;
409 report(cx, expr, State::DerefedBorrow(state), data);
410 if position.is_deref_stable() {
421 (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
422 let position = data.position;
423 report(cx, expr, State::DerefedBorrow(state), data);
424 if let Position::FieldAccess(name) = position
425 && !ty_contains_field(typeck.expr_ty(sub_expr), name)
428 State::ExplicitDerefField { name },
429 StateData { span: expr.span, hir_id: expr.hir_id, position },
431 } else if position.is_deref_stable() {
433 State::ExplicitDeref { deref_span_id: None },
434 StateData { span: expr.span, hir_id: expr.hir_id, position },
439 (Some((State::Borrow, data)), RefOp::Deref) => {
440 if typeck.expr_ty(sub_expr).is_ref() {
443 deref_span: expr.span,
444 deref_hir_id: expr.hir_id,
450 State::ExplicitDeref {
451 deref_span_id: Some((expr.span, expr.hir_id)),
468 State::ExplicitDeref {
469 deref_span_id: Some((deref_span, deref_hir_id)),
474 (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
477 (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
478 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
480 self.state = Some((State::ExplicitDerefField { name }, data));
483 (Some((state, data)), _) => report(cx, expr, state, data),
487 fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
488 if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
489 if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
490 // This binding id has been seen before. Add this pattern to the list of changes.
491 if let Some(prev_pat) = opt_prev_pat {
492 if pat.span.from_expansion() {
493 // Doesn't match the context of the previous pattern. Can't lint here.
494 *opt_prev_pat = None;
496 prev_pat.spans.push(pat.span);
497 prev_pat.replacements.push((
499 snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
509 if !pat.span.from_expansion();
510 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
511 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
512 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
514 let mut app = Applicability::MachineApplicable;
515 let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
516 self.current_body = self.current_body.or(cx.enclosing_body);
517 self.ref_locals.insert(
521 spans: vec![pat.span],
523 replacements: vec![(pat.span, snip.into())],
532 fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
533 if Some(body.id()) == self.current_body {
534 for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
535 let replacements = pat.replacements;
537 let lint = if pat.always_deref {
540 REF_BINDING_TO_REFERENCE
542 span_lint_hir_and_then(
547 "this pattern creates a reference to a reference",
549 diag.multipart_suggestion("try this", replacements, app);
553 self.current_body = None;
558 fn try_parse_ref_op<'tcx>(
560 typeck: &'tcx TypeckResults<'_>,
561 expr: &'tcx Expr<'_>,
562 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
563 let (def_id, arg) = match expr.kind {
564 ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
567 kind: ExprKind::Path(path),
572 ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
573 ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
574 return Some((RefOp::Deref, sub_expr));
576 ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
579 if tcx.is_diagnostic_item(sym::deref_method, def_id) {
580 Some((RefOp::Method(Mutability::Not), arg))
581 } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
582 Some((RefOp::Method(Mutability::Mut), arg))
588 // Checks whether the type for a deref call actually changed the type, not just the mutability of
590 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
591 match (result_ty.kind(), arg_ty.kind()) {
592 (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
594 // The result type for a deref method is always a reference
595 // Not matching the previous pattern means the argument type is not a reference
596 // This means that the type did change
601 /// The position of an expression relative to it's parent.
602 #[derive(Clone, Copy)]
605 /// The method is defined on a reference type. e.g. `impl Foo for &T`
606 MethodReceiverRefImpl,
611 /// Any other location which will trigger auto-deref to a specific time.
613 /// Any other location which will trigger auto-reborrowing.
618 fn is_deref_stable(self) -> bool {
619 matches!(self, Self::DerefStable(_))
622 fn is_reborrow_stable(self) -> bool {
623 matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
626 fn can_auto_borrow(self) -> bool {
627 matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
630 fn lint_explicit_deref(self) -> bool {
631 matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
634 fn precedence(self) -> i8 {
637 | Self::MethodReceiverRefImpl
639 | Self::FieldAccess(_)
640 | Self::Postfix => PREC_POSTFIX,
641 Self::Deref => PREC_PREFIX,
642 Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
647 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
648 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
649 /// locations as those follow different rules.
650 #[allow(clippy::too_many_lines)]
651 fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
652 let mut adjustments = [].as_slice();
653 let mut precedence = 0i8;
654 let ctxt = e.span.ctxt();
655 let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
656 // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
657 if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
658 adjustments = cx.typeck_results().expr_adjustments(e);
661 Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
662 Some(binding_ty_auto_deref_stability(ty, precedence))
665 kind: ItemKind::Static(..) | ItemKind::Const(..),
670 | Node::TraitItem(&TraitItem {
671 kind: TraitItemKind::Const(..),
676 | Node::ImplItem(&ImplItem {
677 kind: ImplItemKind::Const(..),
681 }) if span.ctxt() == ctxt => {
682 let ty = cx.tcx.type_of(def_id);
683 Some(if ty.is_ref() {
684 Position::DerefStable(precedence)
686 Position::Other(precedence)
691 kind: ItemKind::Fn(..),
696 | Node::TraitItem(&TraitItem {
697 kind: TraitItemKind::Fn(..),
702 | Node::ImplItem(&ImplItem {
703 kind: ImplItemKind::Fn(..),
707 }) if span.ctxt() == ctxt => {
708 let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
709 Some(if !output.is_ref() {
710 Position::Other(precedence)
711 } else if output.has_placeholders() || output.has_opaque_types() {
712 Position::ReborrowStable(precedence)
714 Position::DerefStable(precedence)
718 Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
719 ExprKind::Ret(_) => {
720 let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
722 if let Node::Expr(Expr {
723 kind: ExprKind::Closure(&Closure { fn_decl, .. }),
725 }) = cx.tcx.hir().get(owner_id)
727 match fn_decl.output {
728 FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
729 FnRetTy::DefaultReturn(_) => Position::Other(precedence),
734 .fn_sig(cx.tcx.hir().local_def_id(owner_id))
737 if !output.is_ref() {
738 Position::Other(precedence)
739 } else if output.has_placeholders() || output.has_opaque_types() {
740 Position::ReborrowStable(precedence)
742 Position::DerefStable(precedence)
747 ExprKind::Call(func, _) if func.hir_id == child_id => {
748 (child_id == e.hir_id).then_some(Position::Callee)
750 ExprKind::Call(func, args) => args
752 .position(|arg| arg.hir_id == child_id)
753 .zip(expr_sig(cx, func))
754 .and_then(|(i, sig)| sig.input_with_hir(i))
755 .map(|(hir_ty, ty)| match hir_ty {
756 // Type inference for closures can depend on how they're called. Only go by the explicit
758 Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
759 None => param_auto_deref_stability(ty.skip_binder(), precedence),
761 ExprKind::MethodCall(_, args, _) => {
762 let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
763 args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
765 // Check for calls to trait methods where the trait is implemented on a reference.
766 // Two cases need to be handled:
767 // * `self` methods on `&T` will never have auto-borrow
768 // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
770 if e.hir_id != child_id {
771 Position::ReborrowStable(precedence)
772 } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
773 && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
774 && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
775 && let subs = match cx
777 .node_substs_opt(parent.hir_id)
778 .and_then(|subs| subs.get(1..))
780 Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
781 None => cx.tcx.mk_substs([].iter()),
782 } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
783 // Trait methods taking `&self`
786 // Trait methods taking `self`
788 } && impl_ty.is_ref()
789 && cx.tcx.infer_ctxt().enter(|infcx|
791 .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
792 .must_apply_modulo_regions()
795 Position::MethodReceiverRefImpl
797 Position::MethodReceiver
800 param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
804 ExprKind::Struct(path, fields, _) => {
805 let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
808 .find(|f| f.expr.hir_id == child_id)
810 .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
811 .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
813 ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
814 ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
815 ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
816 | ExprKind::Index(child, _)
817 if child.hir_id == e.hir_id =>
819 Some(Position::Postfix)
821 _ if child_id == e.hir_id => {
822 precedence = parent.precedence().order();
830 .unwrap_or(Position::Other(precedence));
831 (position, adjustments)
834 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
837 // let x = Box::new(Box::new(0u32));
838 // let y1: &Box<_> = x.deref();
839 // let y2: &Box<_> = &x;
841 // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
842 // switching to auto-dereferencing.
843 fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
844 let TyKind::Rptr(_, ty) = &ty.kind else {
845 return Position::Other(precedence);
850 break match ty.ty.kind {
851 TyKind::Rptr(_, ref ref_ty) => {
856 QPath::TypeRelative(_, path)
860 segments: [.., path], ..
864 if let Some(args) = path.args
865 && args.args.iter().any(|arg| match arg {
866 GenericArg::Infer(_) => true,
867 GenericArg::Type(ty) => ty_contains_infer(ty),
871 Position::ReborrowStable(precedence)
873 Position::DerefStable(precedence)
882 | TyKind::TraitObject(..)
883 | TyKind::Path(_) => Position::DerefStable(precedence),
884 TyKind::OpaqueDef(..)
887 | TyKind::Err => Position::ReborrowStable(precedence),
892 // Checks whether a type is inferred at some point.
893 // e.g. `_`, `Box<_>`, `[_]`
894 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
896 impl Visitor<'_> for V {
897 fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
901 TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
910 fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
911 if self.0 || matches!(arg, GenericArg::Infer(_)) {
913 } else if let GenericArg::Type(ty) = arg {
918 let mut v = V(false);
923 // Checks whether a type is stable when switching to auto dereferencing,
924 fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
925 let ty::Ref(_, mut ty, _) = *ty.kind() else {
926 return Position::Other(precedence);
930 break match *ty.kind() {
931 ty::Ref(_, ref_ty, _) => {
941 | ty::Dynamic(..) => Position::ReborrowStable(precedence),
942 ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
943 Position::ReborrowStable(precedence)
960 | ty::GeneratorWitness(..)
963 | ty::Projection(_) => Position::DerefStable(precedence),
968 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
969 if let ty::Adt(adt, _) = *ty.kind() {
970 adt.is_struct() && adt.all_fields().any(|f| f.name == name)
976 #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
977 fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
984 let mut app = Applicability::MachineApplicable;
985 let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
986 let ty = cx.typeck_results().expr_ty(expr);
987 let (_, ref_count) = peel_mid_ty_refs(ty);
988 let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
989 // a deref call changing &T -> &U requires two deref operators the first time
990 // this occurs. One to remove the reference, a second to call the deref impl.
991 "*".repeat(ty_changed_count + 1)
993 "*".repeat(ty_changed_count)
995 let addr_of_str = if ty_changed_count < ref_count {
996 // Check if a reborrow from &mut T -> &T is required.
997 if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
1002 } else if target_mut == Mutability::Mut {
1008 let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
1009 format!("({})", expr_str)
1011 expr_str.into_owned()
1016 EXPLICIT_DEREF_METHODS,
1019 Mutability::Not => "explicit `deref` method call",
1020 Mutability::Mut => "explicit `deref_mut` method call",
1023 format!("{}{}{}", addr_of_str, deref_str, expr_str),
1027 State::DerefedBorrow(state) => {
1028 let mut app = Applicability::MachineApplicable;
1029 let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1030 span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
1031 let sugg = if !snip_is_macro
1032 && expr.precedence().order() < data.position.precedence()
1033 && !has_enclosing_paren(&snip)
1035 format!("({})", snip)
1039 diag.span_suggestion(data.span, "change this to", sugg, app);
1042 State::ExplicitDeref { deref_span_id } => {
1043 let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
1044 && !cx.typeck_results().expr_ty(expr).is_ref()
1046 (span, hir_id, PREC_PREFIX)
1048 (data.span, data.hir_id, data.position.precedence())
1050 span_lint_hir_and_then(
1052 EXPLICIT_AUTO_DEREF,
1055 "deref which would be done by auto-deref",
1057 let mut app = Applicability::MachineApplicable;
1058 let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
1060 if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
1061 format!("({})", snip)
1065 diag.span_suggestion(span, "try this", sugg, app);
1069 State::ExplicitDerefField { .. } => {
1070 span_lint_hir_and_then(
1072 EXPLICIT_AUTO_DEREF,
1075 "deref which would be done by auto-deref",
1077 let mut app = Applicability::MachineApplicable;
1078 let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
1079 diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
1083 State::Borrow | State::Reborrow { .. } => (),
1087 impl Dereferencing {
1088 fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
1089 if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
1090 if let Some(pat) = outer_pat {
1091 // Check for auto-deref
1093 cx.typeck_results().expr_adjustments(e),
1096 kind: Adjust::Deref(_),
1100 kind: Adjust::Deref(_),
1106 match get_parent_expr(cx, e) {
1107 // Field accesses are the same no matter the number of references.
1109 kind: ExprKind::Field(..),
1114 kind: ExprKind::Unary(UnOp::Deref, _),
1116 }) if !span.from_expansion() => {
1117 // Remove explicit deref.
1118 let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
1119 pat.replacements.push((span, snip.into()));
1121 Some(parent) if !parent.span.from_expansion() => {
1122 // Double reference might be needed at this point.
1123 if parent.precedence().order() == PREC_POSTFIX {
1124 // Parentheses would be needed here, don't lint.
1127 pat.always_deref = false;
1128 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
1129 pat.replacements.push((e.span, format!("&{}", snip)));
1132 _ if !e.span.from_expansion() => {
1133 // Double reference might be needed at this point.
1134 pat.always_deref = false;
1135 let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
1136 pat.replacements.push((e.span, format!("&{}", snip)));
1138 // Edge case for macros. The span of the identifier will usually match the context of the
1139 // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
1141 _ => *outer_pat = None,