Skip to content

Unnecessary references lint #138230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,9 @@ lint_unnameable_test_items = cannot test inner items
lint_unnecessary_qualification = unnecessary qualification
.suggestion = remove the unnecessary path segments

lint_unnecessary_refs = creating a intermediate reference implies aliasing requirements even when immediately casting to raw pointers
.suggestion = consider using `&raw {$mutbl}` for a safer and more explicit raw pointer

lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
.note_duplicated_fn = the address of the same function can vary between different codegen units
.note_deduplicated_fn = furthermore, different functions could have the same address after being merged together
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ mod static_mut_refs;
mod traits;
mod types;
mod unit_bindings;
mod unnecessary_refs;
mod unqualified_local_imports;
mod unused;

Expand Down Expand Up @@ -119,6 +120,7 @@ use static_mut_refs::*;
use traits::*;
use types::*;
use unit_bindings::*;
use unnecessary_refs::*;
use unqualified_local_imports::*;
use unused::*;

Expand Down Expand Up @@ -244,6 +246,7 @@ late_lint_methods!(
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
UnqualifiedLocalImports: UnqualifiedLocalImports,
UnecessaryRefs: UnecessaryRefs,
]
]
);
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3163,3 +3163,20 @@ pub(crate) struct ReservedMultihash {
#[suggestion(code = " ", applicability = "machine-applicable")]
pub suggestion: Span,
}

#[derive(LintDiagnostic)]
#[diag(lint_unnecessary_refs)]
pub(crate) struct UnnecessaryRefs<'a> {
#[subdiagnostic]
pub suggestion: UnnecessaryRefsSuggestion<'a>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
pub(crate) struct UnnecessaryRefsSuggestion<'a> {
#[suggestion_part(code = "&raw {mutbl} ")]
pub left: Span,
#[suggestion_part(code = "")]
pub right: Span,
pub mutbl: &'a str,
}
58 changes: 58 additions & 0 deletions compiler/rustc_lint/src/unnecessary_refs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use rustc_ast::BorrowKind;
use rustc_hir::{Expr, ExprKind, TyKind};
use rustc_session::{declare_lint, declare_lint_pass};

use crate::lints::{UnnecessaryRefs, UnnecessaryRefsSuggestion};
use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `unnecessary_refs` lint checks for unnecessary references.
///
/// ### Example
///
/// ```rust
/// 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 can reduce
/// readability, introduce performance overhead, and lead to undefined
/// behavior if the reference is unaligned or uninitialized. Avoiding them
/// ensures safer and more efficient code.
pub UNNECESSARY_REFS,
Warn,
"creating unecessary reference is discouraged"
}

declare_lint_pass!(UnecessaryRefs => [UNNECESSARY_REFS]);

impl<'tcx> LateLintPass<'tcx> for UnecessaryRefs {
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
{
cx.emit_span_lint(
UNNECESSARY_REFS,
expr.span,
UnnecessaryRefs {
suggestion: UnnecessaryRefsSuggestion {
left: expr.span.until(addr_of_exp.span),
right: addr_of_exp.span.shrink_to_hi().until(ty.span.shrink_to_hi()),
mutbl: mutbl.ptr_str(),
},
},
);
}
}
}
2 changes: 2 additions & 0 deletions compiler/rustc_query_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ where
}

#[inline(always)]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key>
where
QueryCtxt<'tcx>: 'a,
Expand All @@ -98,6 +99,7 @@ where
}

#[inline(always)]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
where
'tcx: 'a,
Expand Down
1 change: 1 addition & 0 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> {
/// # Examples
///
/// ```
/// #![allow(unnecessary_refs)]
/// let x = Box::new(5);
/// let y = x.clone();
///
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::ptr::NonNull;

#[test]
#[expect(dangling_pointers_from_temporaries)]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn uninitialized_zero_size_box() {
assert_eq!(
&*Box::<()>::new_uninit() as *const _,
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,7 @@ fn test_str_escapes() {
}

#[test]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn const_str_ptr() {
const A: [u8; 2] = ['h' as u8, 'i' as u8];
const B: &'static [u8; 2] = &A;
Expand Down
2 changes: 2 additions & 0 deletions library/alloctests/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ fn weak_self_cyclic() {
}

#[test]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn drop_arc() {
let mut canary = AtomicUsize::new(0);
let x = Arc::new(Canary(&mut canary as *mut AtomicUsize));
Expand All @@ -338,6 +339,7 @@ fn drop_arc() {
}

#[test]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn drop_arc_weak() {
let mut canary = AtomicUsize::new(0);
let arc = Arc::new(Canary(&mut canary as *mut AtomicUsize));
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,7 @@ fn assert_covariance() {
}

#[test]
#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn from_into_inner() {
let vec = vec![1, 2, 3];
let ptr = vec.as_ptr();
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,7 @@ fn test_collect_from_into_iter_keeps_allocation() {
v.extend(0..7);
check(&v[0], &v[v.len() - 1], v.into_iter());

#[cfg_attr(not(bootstrap), allow(unnecessary_refs))]
fn check(buf: *const i32, last: *const i32, mut it: impl Iterator<Item = i32>) {
assert_eq!(it.next(), Some(0));
assert_eq!(it.next(), Some(1));
Expand Down
3 changes: 1 addition & 2 deletions library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,7 @@ impl CStr {
const fn as_non_null_ptr(&self) -> NonNull<c_char> {
// FIXME(const_trait_impl) replace with `NonNull::from`
// SAFETY: a reference is never null
unsafe { NonNull::new_unchecked(&self.inner as *const [c_char] as *mut [c_char]) }
.as_non_null_ptr()
unsafe { NonNull::new_unchecked(&raw const self.inner as *mut [c_char]) }.as_non_null_ptr()
}

/// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator.
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2881,7 +2881,7 @@ impl<T: ?Sized> Pointer for &T {
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Pointer for &mut T {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
Pointer::fmt(&(&**self as *const T), f)
Pointer::fmt(&(&raw const **self), f)
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/core/src/hash/sip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ macro_rules! load_int_le {
let mut data = 0 as $int_ty;
ptr::copy_nonoverlapping(
$buf.as_ptr().add($i),
&mut data as *mut _ as *mut u8,
&raw mut data as *mut u8,
size_of::<$int_ty>(),
);
data.to_le()
Expand Down
1 change: 1 addition & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3879,6 +3879,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
/// following is an **incorrect** use of this function:
///
/// ```rust,no_run
/// #![allow(unnecessary_refs)]
/// unsafe {
/// let mut value: u8 = 0;
/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool;
Expand Down
1 change: 1 addition & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ impl<T> MaybeUninit<T> {
/// Nor can you use direct field access to do field-by-field gradual initialization:
///
/// ```rust,no_run
/// #![allow(unnecessary_refs)]
/// use std::{mem::MaybeUninit, ptr};
///
/// struct Foo {
Expand Down
1 change: 1 addition & 0 deletions library/core/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
//! }
//!
//! impl AddrTracker {
//! #[allow(unnecessary_refs)]
//! fn check_for_move(self: Pin<&mut Self>) {
//! let current_addr = &*self as *const Self as usize;
//! match self.prev_addr {
Expand Down
9 changes: 9 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl<T: ?Sized> *const T {
///
/// ```rust,no_run
/// #![feature(set_ptr_value)]
/// #![allow(unnecessary_refs)]
/// let x = 0u32;
/// let y = 1u32;
///
Expand Down Expand Up @@ -266,6 +267,7 @@ impl<T: ?Sized> *const T {
/// # Examples
///
/// ```
/// #![allow(unnecessary_refs)]
/// let ptr: *const u8 = &10u8 as *const u8;
///
/// unsafe {
Expand All @@ -282,6 +284,7 @@ impl<T: ?Sized> *const T {
/// dereference the pointer directly.
///
/// ```
/// #![allow(unnecessary_refs)]
/// let ptr: *const u8 = &10u8 as *const u8;
///
/// unsafe {
Expand Down Expand Up @@ -314,6 +317,7 @@ impl<T: ?Sized> *const T {
///
/// ```
/// #![feature(ptr_as_ref_unchecked)]
/// #![allow(unnecessary_refs)]
/// let ptr: *const u8 = &10u8 as *const u8;
///
/// unsafe {
Expand Down Expand Up @@ -351,6 +355,7 @@ impl<T: ?Sized> *const T {
///
/// ```
/// #![feature(ptr_as_uninit)]
/// #![allow(unnecessary_refs)]
///
/// let ptr: *const u8 = &10u8 as *const u8;
///
Expand Down Expand Up @@ -1416,6 +1421,7 @@ impl<T: ?Sized> *const T {
/// # Examples
///
/// ```
/// #![allow(unnecessary_refs)]
/// // On some platforms, the alignment of i32 is less than 4.
/// #[repr(align(4))]
/// struct AlignedI32(i32);
Expand Down Expand Up @@ -1449,6 +1455,7 @@ impl<T: ?Sized> *const T {
///
/// ```
/// #![feature(pointer_is_aligned_to)]
/// #![allow(unnecessary_refs)]
///
/// // On some platforms, the alignment of i32 is less than 4.
/// #[repr(align(4))]
Expand Down Expand Up @@ -1564,6 +1571,7 @@ impl<T> *const [T] {
///
/// ```
/// #![feature(slice_ptr_get)]
/// #![allow(unnecessary_refs)]
///
/// let x = &[1, 2, 4] as *const [i32];
///
Expand Down Expand Up @@ -1663,6 +1671,7 @@ impl<T, const N: usize> *const [T; N] {
///
/// ```
/// #![feature(array_ptr_get)]
/// #![allow(unnecessary_refs)]
///
/// let arr: *const [i32; 3] = &[1, 2, 4] as *const [i32; 3];
/// let slice: *const [i32] = arr.as_slice();
Expand Down
8 changes: 8 additions & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
//! represent the tagged pointer as an actual pointer and not a `usize`*. For instance:
//!
//! ```
//! #![allow(unnecessary_refs)]
//! unsafe {
//! // A flag we want to pack into our pointer
//! static HAS_DATA: usize = 0x1;
Expand Down Expand Up @@ -492,6 +493,7 @@ mod mut_ptr;
/// Manually remove the last item from a vector:
///
/// ```
/// #![allow(unnecessary_refs)]
/// use std::ptr;
/// use std::rc::Rc;
///
Expand Down Expand Up @@ -759,6 +761,7 @@ pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in
/// tail expressions. This code is valid, albeit in a non-obvious way:
/// ```rust
/// #![allow(unnecessary_refs)]
/// # type T = i32;
/// # fn foo() -> T { 42 }
/// // The temporary holding the return value of `foo` has its lifetime extended,
Expand Down Expand Up @@ -810,6 +813,7 @@ pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in
/// tail expressions. This code is valid, albeit in a non-obvious way:
/// ```rust
/// #![allow(unnecessary_refs)]
/// # type T = i32;
/// # fn foo() -> T { 42 }
/// // The temporary holding the return value of `foo` has its lifetime extended,
Expand Down Expand Up @@ -1249,6 +1253,7 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
/// Basic usage:
///
/// ```
/// #![allow(unnecessary_refs)]
/// let x = 12;
/// let y = &x as *const i32;
///
Expand Down Expand Up @@ -1501,6 +1506,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
/// Basic usage:
///
/// ```
/// #![allow(unnecessary_refs)]
/// let mut x = 0;
/// let y = &mut x as *mut i32;
/// let z = 12;
Expand Down Expand Up @@ -1722,6 +1728,7 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
/// Basic usage:
///
/// ```
/// #![allow(unnecessary_refs)]
/// let x = 12;
/// let y = &x as *const i32;
///
Expand Down Expand Up @@ -1800,6 +1807,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
/// Basic usage:
///
/// ```
/// #![allow(unnecessary_refs)]
/// let mut x = 0;
/// let y = &mut x as *mut i32;
/// let z = 12;
Expand Down
Loading
Loading