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};
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, 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;
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 mutability: Option<Mutability>,
192 mutability: Mutability,
195 mutability: Mutability,
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 { mutability: 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(..)),
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 {
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()
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(_)))
366 State::Borrow { mutability },
375 RefOp::Method(..) => (),
391 ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
396 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
402 (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
404 State::DerefedBorrow(DerefedBorrow {
405 count: state.count - 1,
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() {
416 State::Borrow { mutability },
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)
432 State::ExplicitDerefField { name },
433 StateData { span: expr.span, hir_id: expr.hir_id, position },
435 } else if position.is_deref_stable() {
437 State::ExplicitDeref { mutability: None },
438 StateData { span: expr.span, hir_id: expr.hir_id, position },
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));
448 State::ExplicitDeref {
449 mutability: Some(mutability),
455 (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
457 State::ExplicitDeref {
458 mutability: Some(mutability),
463 (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
466 (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
467 if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
469 self.state = Some((State::ExplicitDerefField { name }, data));
472 (Some((state, data)), _) => report(cx, expr, state, data),
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;
485 prev_pat.spans.push(pat.span);
486 prev_pat.replacements.push((
488 snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
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();
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(
510 spans: vec![pat.span],
512 replacements: vec![(pat.span, snip.into())],
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;
526 let lint = if pat.always_deref {
529 REF_BINDING_TO_REFERENCE
531 span_lint_hir_and_then(
536 "this pattern creates a reference to a reference",
538 diag.multipart_suggestion("try this", replacements, app);
542 self.current_body = None;
547 fn try_parse_ref_op<'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),
556 kind: ExprKind::Path(path),
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));
565 ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
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))
577 // Checks whether the type for a deref call actually changed the type, not just the mutability of
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,
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
590 /// The position of an expression relative to it's parent.
591 #[derive(Clone, Copy)]
594 /// The method is defined on a reference type. e.g. `impl Foo for &T`
595 MethodReceiverRefImpl,
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.
606 /// Contains the precedence of the parent expression.
610 fn is_deref_stable(self) -> bool {
611 matches!(self, Self::DerefStable(..))
614 fn is_reborrow_stable(self) -> bool {
615 matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
618 fn can_auto_borrow(self) -> bool {
619 matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
622 fn lint_explicit_deref(self) -> bool {
623 matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
626 fn precedence(self) -> i8 {
629 | Self::MethodReceiverRefImpl
631 | Self::FieldAccess(_)
632 | Self::Postfix => PREC_POSTFIX,
633 Self::Deref => PREC_PREFIX,
634 Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
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);
653 Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
654 Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
657 kind: ItemKind::Static(..) | ItemKind::Const(..),
662 | Node::TraitItem(&TraitItem {
663 kind: TraitItemKind::Const(..),
668 | Node::ImplItem(&ImplItem {
669 kind: ImplItemKind::Const(..),
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))
679 kind: ItemKind::Fn(..),
684 | Node::TraitItem(&TraitItem {
685 kind: TraitItemKind::Fn(..),
690 | Node::ImplItem(&ImplItem {
691 kind: ImplItemKind::Fn(..),
695 }) if span.ctxt() == ctxt => {
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))
702 Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
705 kind: ExprKind::Struct(path, ..),
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))
710 ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
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());
720 closure_expr @ Expr {
721 kind: ExprKind::Closure(closure),
724 ) = cx.tcx.hir().get(owner_id)
726 closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
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)
735 ExprKind::Closure(closure) => Some(closure_result_position(
738 cx.typeck_results().expr_ty(parent),
741 ExprKind::Call(func, _) if func.hir_id == child_id => {
742 (child_id == e.hir_id).then_some(Position::Callee)
744 ExprKind::Call(func, args) => args
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
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)
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| {
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
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
772 .node_substs_opt(parent.hir_id)
773 .and_then(|subs| subs.get(1..))
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`
781 // Trait methods taking `self`
783 } && impl_ty.is_ref()
784 && cx.tcx.infer_ctxt().enter(|infcx|
786 .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
787 .must_apply_modulo_regions()
790 Position::MethodReceiverRefImpl
792 Position::MethodReceiver
795 ty_auto_deref_stability(
797 cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
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 =>
810 Some(Position::Postfix)
812 _ if child_id == e.hir_id => {
813 precedence = parent.precedence().order();
821 .unwrap_or(Position::Other(precedence));
822 (position, adjustments)
825 fn closure_result_position<'tcx>(
826 cx: &LateContext<'tcx>,
827 closure: &'tcx Closure<'_>,
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()
836 binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
838 Position::Other(precedence)
841 FnRetTy::DefaultReturn(_) => Position::Other(precedence),
845 // Checks the stability of auto-deref when assigned to a binding with the given explicit type.
848 // let x = Box::new(Box::new(0u32));
849 // let y1: &Box<_> = x.deref();
850 // let y2: &Box<_> = &x;
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<'_>,
858 binder_args: &'tcx List<BoundVariableKind>,
860 let TyKind::Rptr(_, ty) = &ty.kind else {
861 return Position::Other(precedence);
866 break match ty.ty.kind {
867 TyKind::Rptr(_, ref ref_ty) => {
872 QPath::TypeRelative(_, path)
876 segments: [.., path], ..
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),
887 Position::ReborrowStable(precedence)
889 Position::DerefStable(
892 .erase_late_bound_regions(Binder::bind_with_vars(
893 cx.typeck_results().node_type(ty.ty.hir_id),
896 .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
900 TyKind::Slice(_) => Position::DerefStable(precedence, false),
901 TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
904 | TyKind::Path(_) => Position::DerefStable(
907 .erase_late_bound_regions(Binder::bind_with_vars(
908 cx.typeck_results().node_type(ty.ty.hir_id),
911 .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
913 TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
914 Position::ReborrowStable(precedence)
920 // Checks whether a type is inferred at some point.
921 // e.g. `_`, `Box<_>`, `[_]`
922 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
924 impl Visitor<'_> for V {
925 fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
929 TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
938 fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
939 if self.0 || matches!(arg, GenericArg::Infer(_)) {
941 } else if let GenericArg::Type(ty) = arg {
946 let mut v = V(false);
951 struct TyPosition<'tcx> {
953 ty: Option<Ty<'tcx>>,
955 impl From<Position> for TyPosition<'_> {
956 fn from(position: Position) -> Self {
957 Self { position, ty: None }
960 impl<'tcx> TyPosition<'tcx> {
961 fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
963 position: Position::ReborrowStable(precedence),
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))
972 (position, _) => position,
975 fn position_for_arg(self) -> Position {
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();
987 break match *ty.kind() {
988 ty::Ref(_, ref_ty, _) => {
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()
996 ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
997 Position::ReborrowStable(precedence).into()
999 ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
1000 TyPosition::new_deref_stable_for_result(precedence, ty)
1009 | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
1010 ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
1015 | ty::GeneratorWitness(..)
1019 | ty::Projection(_) => Position::DerefStable(
1021 ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
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)
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) {
1039 State::DerefMethod {
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)
1053 "*".repeat(ty_changed_count)
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)) {
1062 } else if target_mut == Mutability::Mut {
1068 let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
1069 format!("({})", expr_str)
1071 expr_str.into_owned()
1076 EXPLICIT_DEREF_METHODS,
1079 Mutability::Not => "explicit `deref` method call",
1080 Mutability::Mut => "explicit `deref_mut` method call",
1083 format!("{}{}{}", addr_of_str, deref_str, expr_str),
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)
1096 format!("({})", snip)
1100 diag.span_suggestion(data.span, "change this to", sugg, app);
1103 State::ExplicitDeref { mutability } => {
1107 | ExprKind::ConstBlock(_)
1109 | ExprKind::Loop(..)
1110 | ExprKind::Match(..)
1111 ) && matches!(data.position, Position::DerefStable(_, true))
1113 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
1117 let (prefix, precedence) = if let Some(mutability) = mutability
1118 && !cx.typeck_results().expr_ty(expr).is_ref()
1120 let prefix = match mutability {
1121 Mutability::Not => "&",
1122 Mutability::Mut => "&mut ",
1126 ("", data.position.precedence())
1128 span_lint_hir_and_then(
1130 EXPLICIT_AUTO_DEREF,
1133 "deref which would be done by auto-deref",
1135 let mut app = Applicability::MachineApplicable;
1136 let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
1138 if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
1139 format!("{}({})", prefix, snip)
1141 format!("{}{}", prefix, snip)
1143 diag.span_suggestion(data.span, "try this", sugg, app);
1147 State::ExplicitDerefField { .. } => {
1151 | ExprKind::ConstBlock(_)
1153 | ExprKind::Loop(..)
1154 | ExprKind::Match(..)
1155 ) && matches!(data.position, Position::DerefStable(_, true))
1157 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
1161 span_lint_hir_and_then(
1163 EXPLICIT_AUTO_DEREF,
1166 "deref which would be done by auto-deref",
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);
1174 State::Borrow { .. } | State::Reborrow { .. } => (),
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
1184 cx.typeck_results().expr_adjustments(e),
1187 kind: Adjust::Deref(_),
1191 kind: Adjust::Deref(_),
1197 match get_parent_expr(cx, e) {
1198 // Field accesses are the same no matter the number of references.
1200 kind: ExprKind::Field(..),
1205 kind: ExprKind::Unary(UnOp::Deref, _),
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()));
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.
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)));
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)));
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
1232 _ => *outer_pat = None,