From ee9f65976d9f2a69d6263b162cb1314abb71e0c4 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Thu, 25 Jun 2026 01:12:01 +0300 Subject: [PATCH 1/2] Add `unnecessary_refs` lint --- compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 26 ++++++++ compiler/rustc_lint/src/unnecessary_refs.rs | 74 +++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 compiler/rustc_lint/src/unnecessary_refs.rs diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0e96b9f118790..80665c0e1824d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -79,6 +79,7 @@ mod traits; mod transmute; mod types; mod unit_bindings; +mod unnecessary_refs; mod unqualified_local_imports; pub mod unused; mod utils; @@ -130,6 +131,7 @@ use traits::*; use transmute::CheckTransmutes; use types::*; use unit_bindings::*; +use unnecessary_refs::*; use unqualified_local_imports::*; use unused::must_use::*; use unused::*; @@ -272,6 +274,7 @@ late_lint_methods!( InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ImplicitProvenanceCasts: ImplicitProvenanceCasts, CVoidReturns: CVoidReturns, + UnnecessaryRefs: UnnecessaryRefs, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index eb82afab13186..53676aff74fd8 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3039,3 +3039,29 @@ pub(crate) enum Ptr2IntSuggestion<'tcx> { cast_span: Span, }, } + +#[derive(Diagnostic)] +#[diag( + "creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers" +)] +pub(crate) struct UnnecessaryRef<'a> { + #[subdiagnostic] + pub suggestion: UnnecessaryRefSuggestion<'a>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnnecessaryRefSuggestion<'a> { + #[multipart_suggestion( + "consider using `&raw {$mutbl}` for a safer and more explicit raw pointer", + applicability = "machine-applicable" + )] + Spanful { + #[suggestion_part(code = "&raw {mutbl} ")] + left: Span, + #[suggestion_part(code = "")] + right: Span, + mutbl: &'a str, + }, + #[help("consider using `&raw {$mutbl}` for a safer and more explicit raw pointer")] + Spanless { mutbl: &'a str }, +} diff --git a/compiler/rustc_lint/src/unnecessary_refs.rs b/compiler/rustc_lint/src/unnecessary_refs.rs new file mode 100644 index 0000000000000..e5c11808e0e7a --- /dev/null +++ b/compiler/rustc_lint/src/unnecessary_refs.rs @@ -0,0 +1,74 @@ +use rustc_ast::BorrowKind; +use rustc_hir::{Expr, ExprKind, TyKind}; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{UnnecessaryRef, UnnecessaryRefSuggestion}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `unnecessary_refs` lint checks for unnecessary references. + /// + /// ### Example + /// + /// ```rust + /// #![warn(unnecessary_refs)] + /// + /// fn via_ref(x: *const (i32, i32)) -> *const i32 { + /// unsafe { &(*x).0 as *const i32 } + /// } + /// + /// fn main() { + /// let x = (0, 1); + /// let _r = via_ref(&x); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Creating unnecessary references is discouraged because it makes code + /// less explicit and can lead to undefined behavior. Creating a reference + /// induces aliasing assumptions that the compiler relies on, so an + /// otherwise-pointless reference can cause undefined behavior; it is also + /// UB if the reference is unaligned. Avoiding them keeps the code more + /// explicit and easier to reason about. + /// + /// This lint is "allow" by default because promoting it would require a + /// large amount of churn across the standard library to remove the many + /// unnecessary references it currently creates. + pub UNNECESSARY_REFS, + // FIXME: This should eventually be `Warn`, see the "Explanation" above. + Allow, + "creating unnecessary reference is discouraged" +} + +declare_lint_pass!(UnnecessaryRefs => [UNNECESSARY_REFS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryRefs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if let ExprKind::Cast(exp, ty) = expr.kind + && let ExprKind::AddrOf(BorrowKind::Ref, mutbl, addr_of_exp) = exp.kind + && let TyKind::Ptr(_) = ty.kind + && addr_of_exp.is_syntactic_place_expr() + { + let suggestion = if let Some(addr_of_span) = + addr_of_exp.span.find_ancestor_in_same_ctxt(expr.span) + && let Some(ty_span) = ty.span.find_ancestor_in_same_ctxt(expr.span) + && expr.span.can_be_used_for_suggestions() + && addr_of_span.can_be_used_for_suggestions() + && ty_span.can_be_used_for_suggestions() + { + UnnecessaryRefSuggestion::Spanful { + left: expr.span.until(addr_of_span), + right: addr_of_span.shrink_to_hi().until(ty_span.shrink_to_hi()), + mutbl: mutbl.ptr_str(), + } + } else { + UnnecessaryRefSuggestion::Spanless { mutbl: mutbl.ptr_str() } + }; + + cx.emit_span_lint(UNNECESSARY_REFS, expr.span, UnnecessaryRef { suggestion }); + } + } +} From b6eda97c9eb3115b177b2c8641234dd5477f3de3 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Sat, 27 Jun 2026 11:47:47 +0300 Subject: [PATCH 2/2] Add test for `unnecessary_refs` lint --- tests/ui/lint/lint-unnecessary-refs.fixed | 87 ++++++++++++++ tests/ui/lint/lint-unnecessary-refs.rs | 87 ++++++++++++++ tests/ui/lint/lint-unnecessary-refs.stderr | 127 +++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 tests/ui/lint/lint-unnecessary-refs.fixed create mode 100644 tests/ui/lint/lint-unnecessary-refs.rs create mode 100644 tests/ui/lint/lint-unnecessary-refs.stderr diff --git a/tests/ui/lint/lint-unnecessary-refs.fixed b/tests/ui/lint/lint-unnecessary-refs.fixed new file mode 100644 index 0000000000000..8e29b7c7f78e4 --- /dev/null +++ b/tests/ui/lint/lint-unnecessary-refs.fixed @@ -0,0 +1,87 @@ +//@ check-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#![allow(dead_code, unused_variables)] +#![warn(unnecessary_refs)] + +struct A { + a: i32, + b: u8, +} + +fn via_ref(x: *const (i32, i32)) -> *const i32 { + unsafe { &raw const (*x).0 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_struct(x: *const A) -> *const u8 { + unsafe { &raw const (*x).b } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_mut(x: *mut (i32, i32)) -> *mut i32 { + unsafe { &raw mut (*x).0 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_struct_mut(x: *mut A) -> *mut i32 { + unsafe { &raw mut (*x).a } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn multiple_casts(x: *const (i32, i32)) -> *const u8 { + unsafe { &raw const (*x).0 as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn ret_i32() -> i32 { + 0 +} + +fn temporaries() { + let _ = &1 as *const i32; + let _ = &(1 + 2) as *const i32; + let _ = &ret_i32() as *const i32; + let _ = &(1, 2) as *const (i32, i32); + let _ = &[1, 2, 3] as *const [i32; 3]; + let _ = &A { a: 0, b: 0 } as *const A; + let _ = &mut 4 as *mut i32; +} + +fn inner_blocks() { + let x = 0; + let _ = { &raw const x }; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let _ = { + let y = 0; + { &raw const y } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + }; + + let _ = { &1 as *const i32 }; +} + +macro_rules! ref_cast { + ($e:expr) => { + &$e as *const i32 + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + }; +} + +fn from_macro(x: *const i32) -> *const i32 { + unsafe { ref_cast!(*x) } +} + +fn main() { + let a = 0; + let a = &raw const a; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let mut b = 0; + let b = &raw mut b; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let i = &1 as *const i32; +} diff --git a/tests/ui/lint/lint-unnecessary-refs.rs b/tests/ui/lint/lint-unnecessary-refs.rs new file mode 100644 index 0000000000000..ec22dc8753675 --- /dev/null +++ b/tests/ui/lint/lint-unnecessary-refs.rs @@ -0,0 +1,87 @@ +//@ check-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#![allow(dead_code, unused_variables)] +#![warn(unnecessary_refs)] + +struct A { + a: i32, + b: u8, +} + +fn via_ref(x: *const (i32, i32)) -> *const i32 { + unsafe { &(*x).0 as *const i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_struct(x: *const A) -> *const u8 { + unsafe { &(*x).b as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_mut(x: *mut (i32, i32)) -> *mut i32 { + unsafe { &mut (*x).0 as *mut i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn via_ref_struct_mut(x: *mut A) -> *mut i32 { + unsafe { &mut (*x).a as *mut i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn multiple_casts(x: *const (i32, i32)) -> *const u8 { + unsafe { &(*x).0 as *const i32 as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] +} + +fn ret_i32() -> i32 { + 0 +} + +fn temporaries() { + let _ = &1 as *const i32; + let _ = &(1 + 2) as *const i32; + let _ = &ret_i32() as *const i32; + let _ = &(1, 2) as *const (i32, i32); + let _ = &[1, 2, 3] as *const [i32; 3]; + let _ = &A { a: 0, b: 0 } as *const A; + let _ = &mut 4 as *mut i32; +} + +fn inner_blocks() { + let x = 0; + let _ = { &x as *const i32 }; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let _ = { + let y = 0; + { &y as *const i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + }; + + let _ = { &1 as *const i32 }; +} + +macro_rules! ref_cast { + ($e:expr) => { + &$e as *const i32 + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + }; +} + +fn from_macro(x: *const i32) -> *const i32 { + unsafe { ref_cast!(*x) } +} + +fn main() { + let a = 0; + let a = &a as *const i32; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let mut b = 0; + let b = &mut b as *mut i32; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers [unnecessary_refs] + + let i = &1 as *const i32; +} diff --git a/tests/ui/lint/lint-unnecessary-refs.stderr b/tests/ui/lint/lint-unnecessary-refs.stderr new file mode 100644 index 0000000000000..2b3a7b6cda4aa --- /dev/null +++ b/tests/ui/lint/lint-unnecessary-refs.stderr @@ -0,0 +1,127 @@ +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:14:14 + | +LL | unsafe { &(*x).0 as *const i32 } + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-unnecessary-refs.rs:6:9 + | +LL | #![warn(unnecessary_refs)] + | ^^^^^^^^^^^^^^^^ +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).0 as *const i32 } +LL + unsafe { &raw const (*x).0 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:19:14 + | +LL | unsafe { &(*x).b as *const u8 } + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).b as *const u8 } +LL + unsafe { &raw const (*x).b } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:24:14 + | +LL | unsafe { &mut (*x).0 as *mut i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - unsafe { &mut (*x).0 as *mut i32 } +LL + unsafe { &raw mut (*x).0 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:29:14 + | +LL | unsafe { &mut (*x).a as *mut i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - unsafe { &mut (*x).a as *mut i32 } +LL + unsafe { &raw mut (*x).a } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:34:14 + | +LL | unsafe { &(*x).0 as *const i32 as *const u8 } + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).0 as *const i32 as *const u8 } +LL + unsafe { &raw const (*x).0 as *const u8 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:54:15 + | +LL | let _ = { &x as *const i32 }; + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - let _ = { &x as *const i32 }; +LL + let _ = { &raw const x }; + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:59:11 + | +LL | { &y as *const i32 } + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - { &y as *const i32 } +LL + { &raw const y } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:68:10 + | +LL | &$e as *const i32 + | ^^^^^^^^^^^^^^^^ +... +LL | unsafe { ref_cast!(*x) } + | ------------- in this macro invocation + | + = help: consider using `&raw const` for a safer and more explicit raw pointer + = note: this warning originates in the macro `ref_cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:79:13 + | +LL | let a = &a as *const i32; + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - let a = &a as *const i32; +LL + let a = &raw const a; + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately casting to raw pointers + --> $DIR/lint-unnecessary-refs.rs:83:13 + | +LL | let b = &mut b as *mut i32; + | ^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - let b = &mut b as *mut i32; +LL + let b = &raw mut b; + | + +warning: 10 warnings emitted +