Skip to content
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

Unnecessary references lint #138230

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
@@ -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
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -78,6 +78,7 @@ mod static_mut_refs;
mod traits;
mod types;
mod unit_bindings;
mod unnecessary_refs;
mod unqualified_local_imports;
mod unused;

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

@@ -244,6 +246,7 @@ late_lint_methods!(
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
UnqualifiedLocalImports: UnqualifiedLocalImports,
UnecessaryRefs: UnecessaryRefs,
]
]
);
17 changes: 17 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
@@ -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
@@ -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,
@@ -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,
1 change: 1 addition & 0 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
@@ -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();
///
1 change: 1 addition & 0 deletions library/alloctests/tests/boxed.rs
Original file line number Diff line number Diff line change
@@ -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 _,
1 change: 1 addition & 0 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions library/alloctests/tests/sync.rs
Original file line number Diff line number Diff line change
@@ -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));
@@ -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));
1 change: 1 addition & 0 deletions library/alloctests/tests/vec.rs
Original file line number Diff line number Diff line change
@@ -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();
1 change: 1 addition & 0 deletions library/alloctests/tests/vec_deque.rs
Original file line number Diff line number Diff line change
@@ -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));
3 changes: 1 addition & 2 deletions library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}

2 changes: 1 addition & 1 deletion library/core/src/hash/sip.rs
Original file line number Diff line number Diff line change
@@ -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()
1 change: 1 addition & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
@@ -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 {
1 change: 1 addition & 0 deletions library/core/src/pin.rs
Original file line number Diff line number Diff line change
@@ -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 {
9 changes: 9 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
@@ -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;
///
@@ -266,6 +267,7 @@ impl<T: ?Sized> *const T {
/// # Examples
///
/// ```
/// #![allow(unnecessary_refs)]
/// let ptr: *const u8 = &10u8 as *const u8;
///
/// unsafe {
@@ -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 {
@@ -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 {
@@ -351,6 +355,7 @@ impl<T: ?Sized> *const T {
///
/// ```
/// #![feature(ptr_as_uninit)]
/// #![allow(unnecessary_refs)]
///
/// let ptr: *const u8 = &10u8 as *const u8;
///
@@ -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);
@@ -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))]
@@ -1564,6 +1571,7 @@ impl<T> *const [T] {
///
/// ```
/// #![feature(slice_ptr_get)]
/// #![allow(unnecessary_refs)]
///
/// let x = &[1, 2, 4] as *const [i32];
///
@@ -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();
8 changes: 8 additions & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
///
@@ -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,
@@ -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,
@@ -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;
///
@@ -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;
@@ -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;
///
@@ -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;
Loading