diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f1555ab3ec1..8646d616735 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,9 +1,11 @@ -use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::BytePos; +// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -42,13 +44,37 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind; + if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); - snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()).map(|pos| { + span.lo() + BytePos(pos as u32) + }) + }) { + e.span.with_lo(start_no_whitespace) + } else { + span + } + }; + + let rpos = if *mutability == Mutability::Mut { + macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() + } else { + macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + }; + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + } else { + snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + } } else { snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() }; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 8cdec4201fe..689e9d55223 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -44,9 +44,19 @@ macro_rules! m { }; } +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + $visitor + }; +} + pub struct S; impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 6433e63ca59..57effce5d12 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -40,7 +40,14 @@ fn main() { macro_rules! m { ($visitor: expr) => { - *&$visitor + *& $visitor + }; +} + +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + *& mut $visitor }; } @@ -49,4 +56,7 @@ impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 1fe49ad49cd..52c1f7d1da8 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -51,13 +51,24 @@ LL | let b = **&aref; error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:43:9 | -LL | *&$visitor - | ^^^^^^^^^^ help: try this: `$visitor` +LL | *& $visitor + | ^^^^^^^^^^^ help: try this: `$visitor` ... LL | m!(self) | -------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:50:9 + | +LL | *& mut $visitor + | ^^^^^^^^^^^^^^^ help: try this: `$visitor` +... +LL | m_mut!(self) + | ------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors