about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-05-07 17:39:58 +0200
committerSamuel Tardieu <sam@rfc1149.net>2025-07-31 15:53:09 +0200
commit7224dffc65e7082d2366aa2ca3f7620f93f5439b (patch)
treef5a3dabbcc97a98fc45ee5a1dce915b4f543fa55
parentff496ad34fc823bc063293edbf48f14261c6e28b (diff)
downloadrust-7224dffc65e7082d2366aa2ca3f7620f93f5439b.tar.gz
rust-7224dffc65e7082d2366aa2ca3f7620f93f5439b.zip
Do not lint data coming from macro
Suggesting to remove `*&` or `*&mut` in a macro may be incorrect, as it
would require tracking all possible macro usages. In some cases, it is
not possible to remove the dereference or the reference.

For example, in the following code, the `drmut!()` macro will be linted
against while called as `drmut!(d)`, and the suggestion would be to
remove the `*&mut`. That would make `drmut!(u.data).num = 1` invalid,
as a `ManuallyDrop` object coming through a union cannot be implicitely
dereferenced through `DerefMut`.

```rust
use std::mem::ManuallyDrop;

#[derive(Copy, Clone)]
struct Data {
    num: u64,
}

union Union {
    data: ManuallyDrop<Data>,
}

macro_rules! drmut {
    ($e:expr) => { *&mut $e };
}

fn f(mut u: Union, mut d: Data) {
    unsafe {
        drmut!(u.data).num = 1;
    }
    drmut!(d).num = 1;
}
```
-rw-r--r--clippy_lints/src/reference.rs88
-rw-r--r--tests/ui/deref_addrof.fixed32
-rw-r--r--tests/ui/deref_addrof.rs32
-rw-r--r--tests/ui/deref_addrof.stderr54
4 files changed, 73 insertions, 133 deletions
diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs
index 0b8993ee1e1..3bbcad12a31 100644
--- a/clippy_lints/src/reference.rs
+++ b/clippy_lints/src/reference.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{SpanRangeExt, snippet_with_applicability};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::{Sugg, has_enclosing_paren};
 use clippy_utils::ty::adjust_derefs_manually_drop;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp};
+use rustc_hir::{Expr, ExprKind, HirId, Node, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -40,79 +40,43 @@ declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
 
 impl LateLintPass<'_> for DerefAddrOf {
     fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
-        if let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind
-            && let ExprKind::AddrOf(_, mutability, addrof_target) = deref_target.kind
+        if !e.span.from_expansion()
+            && let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind
+            && !deref_target.span.from_expansion()
+            && let ExprKind::AddrOf(_, _, addrof_target) = deref_target.kind
             // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
             // See #12854 for details.
             && !matches!(addrof_target.kind, ExprKind::Array(_))
             && deref_target.span.eq_ctxt(e.span)
             && !addrof_target.span.from_expansion()
         {
+            let mut applicability = Applicability::MachineApplicable;
+            let mut sugg = || Sugg::hir_with_applicability(cx, addrof_target, "_", &mut applicability);
+
             // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a
             // union, we may remove the reference if we are at the point where the implicit
             // dereference would take place. Otherwise, we should not lint.
-            let keep_deref = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) {
-                ManuallyDropThroughUnion::Directly => true,
+            let sugg = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) {
+                ManuallyDropThroughUnion::Directly => sugg().deref(),
                 ManuallyDropThroughUnion::Indirect => return,
-                ManuallyDropThroughUnion::No => false,
+                ManuallyDropThroughUnion::No => sugg(),
             };
 
-            let mut applicability = Applicability::MachineApplicable;
-            let sugg = if e.span.from_expansion() {
-                if let Some(macro_source) = e.span.get_source_text(cx) {
-                    // Remove leading whitespace from the given span
-                    // e.g: ` $visitor` turns into `$visitor`
-                    let trim_leading_whitespaces = |span: Span| {
-                        span.get_source_text(cx)
-                            .and_then(|snip| {
-                                #[expect(clippy::cast_possible_truncation)]
-                                snip.find(|c: char| !c.is_whitespace())
-                                    .map(|pos| span.lo() + BytePos(pos as u32))
-                            })
-                            .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace))
-                    };
-
-                    let mut generate_snippet = |pattern: &str| {
-                        #[expect(clippy::cast_possible_truncation)]
-                        macro_source.rfind(pattern).map(|pattern_pos| {
-                            let rpos = pattern_pos + pattern.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)
-                        })
-                    };
-
-                    if mutability == Mutability::Mut {
-                        generate_snippet("mut")
-                    } else {
-                        generate_snippet("&")
-                    }
-                } else {
-                    Some(snippet_with_applicability(cx, e.span, "_", &mut applicability))
-                }
+            let sugg = if has_enclosing_paren(snippet(cx, e.span, "")) {
+                sugg.maybe_paren()
             } else {
-                Some(snippet_with_applicability(
-                    cx,
-                    addrof_target.span,
-                    "_",
-                    &mut applicability,
-                ))
+                sugg
             };
-            if let Some(sugg) = sugg {
-                span_lint_and_sugg(
-                    cx,
-                    DEREF_ADDROF,
-                    e.span,
-                    "immediately dereferencing a reference",
-                    "try",
-                    if keep_deref {
-                        format!("(*{sugg})")
-                    } else {
-                        sugg.to_string()
-                    },
-                    applicability,
-                );
-            }
+
+            span_lint_and_sugg(
+                cx,
+                DEREF_ADDROF,
+                e.span,
+                "immediately dereferencing a reference",
+                "try",
+                sugg.to_string(),
+                applicability,
+            );
         }
     }
 }
diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed
index ae8ed0dc114..ffe7f7d1440 100644
--- a/tests/ui/deref_addrof.fixed
+++ b/tests/ui/deref_addrof.fixed
@@ -1,5 +1,3 @@
-//@aux-build:proc_macros.rs
-
 #![allow(
     dangerous_implicit_autorefs,
     clippy::explicit_auto_deref,
@@ -8,9 +6,6 @@
 )]
 #![warn(clippy::deref_addrof)]
 
-extern crate proc_macros;
-use proc_macros::inline_macros;
-
 fn get_number() -> usize {
     10
 }
@@ -61,21 +56,22 @@ fn main() {
     //~^ deref_addrof
     // do NOT lint for array as semantic differences with/out `*&`.
     let _arr = *&[0, 1, 2, 3, 4];
-}
 
-#[derive(Copy, Clone)]
-pub struct S;
-#[inline_macros]
-impl S {
-    pub fn f(&self) -> &Self {
-        inline!($(@expr self))
-        //~^ deref_addrof
-    }
-    #[allow(unused_mut)] // mut will be unused, once the macro is fixed
-    pub fn f_mut(mut self) -> Self {
-        inline!($(@expr self))
-        //~^ deref_addrof
+    // Do not lint when text comes from macro
+    macro_rules! mac {
+        (dr) => {
+            *&0
+        };
+        (dr $e:expr) => {
+            *&$e
+        };
+        (r $e:expr) => {
+            &$e
+        };
     }
+    let b = mac!(dr);
+    let b = mac!(dr a);
+    let b = *mac!(r a);
 }
 
 fn issue14386() {
diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs
index 4ff01405916..bc253716aff 100644
--- a/tests/ui/deref_addrof.rs
+++ b/tests/ui/deref_addrof.rs
@@ -1,5 +1,3 @@
-//@aux-build:proc_macros.rs
-
 #![allow(
     dangerous_implicit_autorefs,
     clippy::explicit_auto_deref,
@@ -8,9 +6,6 @@
 )]
 #![warn(clippy::deref_addrof)]
 
-extern crate proc_macros;
-use proc_macros::inline_macros;
-
 fn get_number() -> usize {
     10
 }
@@ -61,21 +56,22 @@ fn main() {
     //~^ deref_addrof
     // do NOT lint for array as semantic differences with/out `*&`.
     let _arr = *&[0, 1, 2, 3, 4];
-}
 
-#[derive(Copy, Clone)]
-pub struct S;
-#[inline_macros]
-impl S {
-    pub fn f(&self) -> &Self {
-        inline!(*& $(@expr self))
-        //~^ deref_addrof
-    }
-    #[allow(unused_mut)] // mut will be unused, once the macro is fixed
-    pub fn f_mut(mut self) -> Self {
-        inline!(*&mut $(@expr self))
-        //~^ deref_addrof
+    // Do not lint when text comes from macro
+    macro_rules! mac {
+        (dr) => {
+            *&0
+        };
+        (dr $e:expr) => {
+            *&$e
+        };
+        (r $e:expr) => {
+            &$e
+        };
     }
+    let b = mac!(dr);
+    let b = mac!(dr a);
+    let b = *mac!(r a);
 }
 
 fn issue14386() {
diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr
index adfa542765c..65dd904a8f7 100644
--- a/tests/ui/deref_addrof.stderr
+++ b/tests/ui/deref_addrof.stderr
@@ -1,5 +1,5 @@
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:28:13
+  --> tests/ui/deref_addrof.rs:23:13
    |
 LL |     let b = *&a;
    |             ^^^ help: try: `a`
@@ -8,122 +8,106 @@ LL |     let b = *&a;
    = help: to override `-D warnings` add `#[allow(clippy::deref_addrof)]`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:31:13
+  --> tests/ui/deref_addrof.rs:26:13
    |
 LL |     let b = *&get_number();
    |             ^^^^^^^^^^^^^^ help: try: `get_number()`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:37:13
+  --> tests/ui/deref_addrof.rs:32:13
    |
 LL |     let b = *&bytes[1..2][0];
    |             ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:42:13
+  --> tests/ui/deref_addrof.rs:37:13
    |
 LL |     let b = *&(a);
    |             ^^^^^ help: try: `(a)`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:45:13
+  --> tests/ui/deref_addrof.rs:40:13
    |
 LL |     let b = *(&a);
    |             ^^^^^ help: try: `a`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:49:13
+  --> tests/ui/deref_addrof.rs:44:13
    |
 LL |     let b = *((&a));
    |             ^^^^^^^ help: try: `a`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:52:13
+  --> tests/ui/deref_addrof.rs:47:13
    |
 LL |     let b = *&&a;
    |             ^^^^ help: try: `&a`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:55:14
+  --> tests/ui/deref_addrof.rs:50:14
    |
 LL |     let b = **&aref;
    |              ^^^^^^ help: try: `aref`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:60:19
+  --> tests/ui/deref_addrof.rs:55:19
    |
 LL |     let _repeat = *&[0; 64];
    |                   ^^^^^^^^^ help: try: `[0; 64]`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:71:17
-   |
-LL |         inline!(*& $(@expr self))
-   |                 ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)`
-   |
-   = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:76:17
-   |
-LL |         inline!(*&mut $(@expr self))
-   |                 ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)`
-   |
-   = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:108:9
+  --> tests/ui/deref_addrof.rs:104:9
    |
 LL |         (*&mut a.padding) = [1; size_of::<DataWithPadding>()];
    |         ^^^^^^^^^^^^^^^^^ help: try: `a.padding`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:110:9
+  --> tests/ui/deref_addrof.rs:106:9
    |
 LL |         (*&mut a.tup).1 = ();
    |         ^^^^^^^^^^^^^ help: try: `a.tup`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:112:10
+  --> tests/ui/deref_addrof.rs:108:10
    |
 LL |         **&mut a.prim = 0;
    |          ^^^^^^^^^^^^ help: try: `a.prim`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:115:9
+  --> tests/ui/deref_addrof.rs:111:9
    |
 LL |         (*&mut a.data).num = 42;
    |         ^^^^^^^^^^^^^^ help: try: `(*a.data)`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:117:9
+  --> tests/ui/deref_addrof.rs:113:9
    |
 LL |         (*&mut a.indirect.md)[3] = 1;
    |         ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:119:9
+  --> tests/ui/deref_addrof.rs:115:9
    |
 LL |         (*&mut a.indirect_arr[1].md)[3] = 1;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:121:9
+  --> tests/ui/deref_addrof.rs:117:9
    |
 LL |         (*&mut a.indirect_ref.md)[3] = 1;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:125:10
+  --> tests/ui/deref_addrof.rs:121:10
    |
 LL |         **&raw mut a.prim = 0;
    |          ^^^^^^^^^^^^^^^^ help: try: `a.prim`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:127:9
+  --> tests/ui/deref_addrof.rs:123:9
    |
 LL |         (*&raw mut a.data).num = 42;
    |         ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)`
 
-error: aborting due to 20 previous errors
+error: aborting due to 18 previous errors