From 0e70271c73c9b90994b1516b505a40e160b14fa4 Mon Sep 17 00:00:00 2001 From: relaxcn Date: Wed, 17 Sep 2025 01:51:56 +0800 Subject: [PATCH] allow explicit `deref` or `deref_mut` method calls in impl of `Deref` or `DerefMut` trait --- clippy_lints/src/dereference.rs | 436 +++++++++++++------------ tests/ui/explicit_deref_methods.fixed | 62 ++++ tests/ui/explicit_deref_methods.rs | 62 ++++ tests/ui/explicit_deref_methods.stderr | 26 +- 4 files changed, 373 insertions(+), 213 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9ebb8e6e15d9..e6d4d5d9924a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -12,8 +12,8 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, - Pat, PatKind, Path, QPath, TyKind, UnOp, + self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Impl, Item, ItemKind, + MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -25,7 +25,7 @@ use std::borrow::Cow; declare_clippy_lint! { /// ### What it does - /// Checks for explicit `deref()` or `deref_mut()` method calls. + /// Checks for explicit `deref()` or `deref_mut()` method calls that occur outside the implementation of the `Deref` or `DerefMut` traits. /// /// ### Why is this bad? /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise, @@ -169,6 +169,11 @@ pub struct Dereferencing<'tcx> { /// /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + + /// Tracks the nesting depth of `Deref` or `DerefMut` trait implementations. + /// This counter is incremented when entering such an impl block and decremented when exiting. + /// A non-zero value indicates we are currently inside one or more nested impl blocks. + deref_impl_nesting_depth: u32, } #[derive(Debug)] @@ -246,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Stop processing sub expressions when a macro call is seen if expr.span.from_expansion() { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, cx.typeck_results()); + self.report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -255,7 +260,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, typeck); + self.report(cx, expr, state, data, typeck); } return; }; @@ -263,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { if is_from_proc_macro(cx, expr) { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data, cx.typeck_results()); + self.report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -515,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { let adjusted_ty = data.adjusted_ty; let stability = state.stability; - report(cx, expr, State::DerefedBorrow(state), data, typeck); + self.report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { self.state = Some(( State::Borrow { mutability }, @@ -530,7 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let adjusted_ty = data.adjusted_ty; let stability = state.stability; let for_field_access = state.for_field_access; - report(cx, expr, State::DerefedBorrow(state), data, typeck); + self.report(cx, expr, State::DerefedBorrow(state), data, typeck); if let Some(name) = for_field_access && let sub_expr_ty = typeck.expr_ty(sub_expr) && !ty_contains_field(sub_expr_ty, name) @@ -602,7 +607,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, - (Some((state, data)), _) => report(cx, expr, state, data, typeck), + (Some((state, data)), _) => self.report(cx, expr, state, data, typeck), } } @@ -673,6 +678,31 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.current_body = None; } } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_deref_or_deref_mut_impl(cx, item) { + self.deref_impl_nesting_depth += 1; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_deref_or_deref_mut_impl(cx, item) { + self.deref_impl_nesting_depth -= 1; + } + } +} + +fn is_deref_or_deref_mut_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if let ItemKind::Impl(Impl { + of_trait: Some(of_trait), + .. + }) = &item.kind + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() + { + cx.tcx.lang_items().deref_trait() == Some(trait_id) || cx.tcx.lang_items().deref_mut_trait() == Some(trait_id) + } else { + false + } } fn try_parse_ref_op<'tcx>( @@ -931,209 +961,215 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { } } -#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn report<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - state: State, - data: StateData<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, -) { - match state { - State::DerefMethod { - ty_changed_count, - is_ufcs, - mutbl, - } => { - let mut app = Applicability::MachineApplicable; - let (expr_str, expr_is_macro_call) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - let ty = typeck.expr_ty(expr); - let (_, ref_count, _) = peel_and_count_ty_refs(ty); - let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { - // a deref call changing &T -> &U requires two deref operators the first time - // this occurs. One to remove the reference, a second to call the deref impl. - "*".repeat(ty_changed_count + 1) - } else { - "*".repeat(ty_changed_count) - }; - let addr_of_str = if ty_changed_count < ref_count { - // Check if a reborrow from &mut T -> &T is required. - if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - "&*" - } else { - "" +impl<'tcx> Dereferencing<'tcx> { + #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] + fn report( + &self, + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + state: State, + data: StateData<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + ) { + match state { + State::DerefMethod { + ty_changed_count, + is_ufcs, + mutbl, + } => { + // Skip when inside implementation of the `Deref` or `DerefMut` trait. + if self.deref_impl_nesting_depth > 0 { + return; } - } else if mutbl == Mutability::Mut { - "&mut " - } else { - "&" - }; - - let expr_str = if !expr_is_macro_call && is_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix { - Cow::Owned(format!("({expr_str})")) - } else { - expr_str - }; - - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - data.first_expr.span, - match mutbl { - Mutability::Not => "explicit `deref` method call", - Mutability::Mut => "explicit `deref_mut` method call", - }, - "try", - format!("{addr_of_str}{deref_str}{expr_str}"), - app, - ); - }, - State::DerefedBorrow(state) => { - // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, - // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. - if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind - && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind - && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() - { - return; - } - - let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - span_lint_hir_and_then( - cx, - NEEDLESS_BORROW, - data.first_expr.hir_id, - data.first_expr.span, - state.msg, - |diag| { - let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { - Node::Expr(e) => match e.kind { - ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, - ExprKind::Call(..) => { - cx.precedence(expr) < ExprPrecedence::Unambiguous - || matches!(expr.kind, ExprKind::Field(..)) - }, - _ => cx.precedence(expr) < cx.precedence(e), - }, - _ => false, - }; - let is_in_tuple = matches!( - get_parent_expr(cx, data.first_expr), - Some(Expr { - kind: ExprKind::Tup(..), - .. - }) - ); - let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { - format!("({snip})") + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + let ty = typeck.expr_ty(expr); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" } else { - snip.into() - }; - diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); - }, - ); - }, - State::ExplicitDeref { mutability } => { - if is_block_like(expr) - && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() - && ty.is_sized(cx.tcx, cx.typing_env()) - { - // Rustc bug: auto deref doesn't work on block expression when targeting sized types. - return; - } + "" + } + } else if mutbl == Mutability::Mut { + "&mut " + } else { + "&" + }; - let ty = typeck.expr_ty(expr); + let expr_str = if !expr_is_macro_call && is_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix { + Cow::Owned(format!("({expr_str})")) + } else { + expr_str + }; - // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst). - if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() - && dst.is_slice() - { - let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); - if n_src_refs >= 2 && src.is_array() { + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.first_expr.span, + match mutbl { + Mutability::Not => "explicit `deref` method call", + Mutability::Mut => "explicit `deref_mut` method call", + }, + "try", + format!("{addr_of_str}{deref_str}{expr_str}"), + app, + ); + }, + State::DerefedBorrow(state) => { + // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, + // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. + if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind + && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind + && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() + { return; } - } - let (prefix, needs_paren) = match mutability { - Some(mutability) if !ty.is_ref() => { - let prefix = match mutability { - Mutability::Not => "&", - Mutability::Mut => "&mut ", - }; - (prefix, cx.precedence(expr) < ExprPrecedence::Prefix) - }, - None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), - _ => ("", false), - }; - span_lint_hir_and_then( - cx, - EXPLICIT_AUTO_DEREF, - data.first_expr.hir_id, - data.first_expr.span, - "deref which would be done by auto-deref", - |diag| { - let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = - snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) { - format!("{prefix}({snip})") - } else { - format!("{prefix}{snip}") - }; - diag.span_suggestion(data.first_expr.span, "try", sugg, app); - }, - ); - }, - State::ExplicitDerefField { - derefs_manually_drop, .. - } => { - let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) - && (derefs_manually_drop - || adjust_derefs_manually_drop( - typeck.expr_adjustments(data.first_expr), - typeck.expr_ty(data.first_expr), - )) { - // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` - // field expressions when the base type is a union and the parent - // expression is also a field access. - // - // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a - // deref through `ManuallyDrop<_>` will not compile. - let parent_id = cx.tcx.parent_hir_id(expr.hir_id); - if parent_id == data.first_expr.hir_id { + let mut app = Applicability::MachineApplicable; + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + span_lint_hir_and_then( + cx, + NEEDLESS_BORROW, + data.first_expr.hir_id, + data.first_expr.span, + state.msg, + |diag| { + let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { + Node::Expr(e) => match e.kind { + ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, + ExprKind::Call(..) => { + cx.precedence(expr) < ExprPrecedence::Unambiguous + || matches!(expr.kind, ExprKind::Field(..)) + }, + _ => cx.precedence(expr) < cx.precedence(e), + }, + _ => false, + }; + let is_in_tuple = matches!( + get_parent_expr(cx, data.first_expr), + Some(Expr { + kind: ExprKind::Tup(..), + .. + }) + ); + + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { + format!("({snip})") + } else { + snip.into() + }; + diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); + }, + ); + }, + State::ExplicitDeref { mutability } => { + if is_block_like(expr) + && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() + && ty.is_sized(cx.tcx, cx.typing_env()) + { + // Rustc bug: auto deref doesn't work on block expression when targeting sized types. return; } - (cx.tcx.hir_node(parent_id).expect_expr().span, true) - } else { - (expr.span, false) - }; - span_lint_hir_and_then( - cx, - EXPLICIT_AUTO_DEREF, - data.first_expr.hir_id, - data.first_expr.span, - "deref which would be done by auto-deref", - |diag| { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; - let sugg = if needs_parens { - format!("({snip})") - } else { - snip.into_owned() - }; - diag.span_suggestion(data.first_expr.span, "try", sugg, app); - }, - ); - }, - State::Borrow { .. } | State::Reborrow { .. } => (), + + let ty = typeck.expr_ty(expr); + + // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst). + if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() + && dst.is_slice() + { + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); + if n_src_refs >= 2 && src.is_array() { + return; + } + } + + let (prefix, needs_paren) = match mutability { + Some(mutability) if !ty.is_ref() => { + let prefix = match mutability { + Mutability::Not => "&", + Mutability::Mut => "&mut ", + }; + (prefix, cx.precedence(expr) < ExprPrecedence::Prefix) + }, + None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), + _ => ("", false), + }; + span_lint_hir_and_then( + cx, + EXPLICIT_AUTO_DEREF, + data.first_expr.hir_id, + data.first_expr.span, + "deref which would be done by auto-deref", + |diag| { + let mut app = Applicability::MachineApplicable; + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) { + format!("{prefix}({snip})") + } else { + format!("{prefix}{snip}") + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); + }, + ); + }, + State::ExplicitDerefField { + derefs_manually_drop, .. + } => { + let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) + && (derefs_manually_drop + || adjust_derefs_manually_drop( + typeck.expr_adjustments(data.first_expr), + typeck.expr_ty(data.first_expr), + )) { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + let parent_id = cx.tcx.parent_hir_id(expr.hir_id); + if parent_id == data.first_expr.hir_id { + return; + } + (cx.tcx.hir_node(parent_id).expect_expr().span, true) + } else { + (expr.span, false) + }; + span_lint_hir_and_then( + cx, + EXPLICIT_AUTO_DEREF, + data.first_expr.hir_id, + data.first_expr.span, + "deref which would be done by auto-deref", + |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; + let sugg = if needs_parens { + format!("({snip})") + } else { + snip.into_owned() + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); + }, + ); + }, + State::Borrow { .. } | State::Reborrow { .. } => (), + } } -} -impl<'tcx> Dereferencing<'tcx> { fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) && let Some(pat) = outer_pat diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 97e8e0bafe4f..97f98691fa79 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -50,6 +50,68 @@ impl DerefMut for Aaa { } } +mod issue_15392 { + use std::ops::{Deref, DerefMut}; + + struct Inner; + + struct WrapperA(Inner); + + impl Deref for WrapperA { + type Target = Inner; + fn deref(&self) -> &Inner { + &self.0 + } + } + + impl DerefMut for WrapperA { + fn deref_mut(&mut self) -> &mut Inner { + &mut self.0 + } + } + + enum MyEnum { + A(WrapperA), + } + + impl Deref for MyEnum { + type Target = Inner; + + fn deref(&self) -> &Inner { + // forwarding is ok + match self { + MyEnum::A(wrap_a) => Deref::deref(wrap_a), + } + } + } + + impl DerefMut for MyEnum { + fn deref_mut(&mut self) -> &mut Inner { + // forwarding is ok + match self { + MyEnum::A(wrap_a) => DerefMut::deref_mut(wrap_a), + } + } + } + + struct S(String); + struct T(S); + + impl Deref for T { + type Target = str; + fn deref(&self) -> &Self::Target { + #[allow(non_local_definitions)] + impl Deref for S { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0.deref() // OK + } + } + self.0.deref() // OK + } + } +} + fn main() { let a: &mut String = &mut String::from("foo"); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index b689649d49dd..b741ab51034c 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -50,6 +50,68 @@ impl DerefMut for Aaa { } } +mod issue_15392 { + use std::ops::{Deref, DerefMut}; + + struct Inner; + + struct WrapperA(Inner); + + impl Deref for WrapperA { + type Target = Inner; + fn deref(&self) -> &Inner { + &self.0 + } + } + + impl DerefMut for WrapperA { + fn deref_mut(&mut self) -> &mut Inner { + &mut self.0 + } + } + + enum MyEnum { + A(WrapperA), + } + + impl Deref for MyEnum { + type Target = Inner; + + fn deref(&self) -> &Inner { + // forwarding is ok + match self { + MyEnum::A(wrap_a) => Deref::deref(wrap_a), + } + } + } + + impl DerefMut for MyEnum { + fn deref_mut(&mut self) -> &mut Inner { + // forwarding is ok + match self { + MyEnum::A(wrap_a) => DerefMut::deref_mut(wrap_a), + } + } + } + + struct S(String); + struct T(S); + + impl Deref for T { + type Target = str; + fn deref(&self) -> &Self::Target { + #[allow(non_local_definitions)] + impl Deref for S { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0.deref() // OK + } + } + self.0.deref() // OK + } + } +} + fn main() { let a: &mut String = &mut String::from("foo"); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index e2f2e68720b1..fb7f622413b3 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:58:19 + --> tests/ui/explicit_deref_methods.rs:120:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,73 +8,73 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:61:23 + --> tests/ui/explicit_deref_methods.rs:123:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:39 + --> tests/ui/explicit_deref_methods.rs:127:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:50 + --> tests/ui/explicit_deref_methods.rs:127:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:20 + --> tests/ui/explicit_deref_methods.rs:131:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:73:11 + --> tests/ui/explicit_deref_methods.rs:135:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:28 + --> tests/ui/explicit_deref_methods.rs:140:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:13 + --> tests/ui/explicit_deref_methods.rs:143:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:84:28 + --> tests/ui/explicit_deref_methods.rs:146:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:124:31 + --> tests/ui/explicit_deref_methods.rs:186:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:154:14 + --> tests/ui/explicit_deref_methods.rs:216:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:156:14 + --> tests/ui/explicit_deref_methods.rs:218:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:157:14 + --> tests/ui/explicit_deref_methods.rs:219:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)`