Skip to content

Uplift clippy::cast_ref_to_mut lint #111567

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

Merged
merged 4 commits into from
Jun 1, 2023
Merged
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
2 changes: 2 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
@@ -155,6 +155,8 @@ lint_builtin_unused_doc_comment = unused doc comment
lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}`
.suggestion = use `loop`

lint_cast_ref_to_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`

lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}

lint_check_name_unknown = unknown lint: `{$lint_name}`
72 changes: 72 additions & 0 deletions compiler/rustc_lint/src/cast_ref_to_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp};
use rustc_middle::ty;
use rustc_span::sym;

use crate::{lints::CastRefToMutDiag, LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `cast_ref_to_mut` lint checks for casts of `&T` to `&mut T`
/// without using interior mutability.
///
/// ### Example
///
/// ```rust,compile_fail
/// fn x(r: &i32) {
/// unsafe {
/// *(r as *const i32 as *mut i32) += 1;
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Casting `&T` to `&mut T` without using interior mutability is undefined behavior,
/// as it's a violation of Rust reference aliasing requirements.
///
/// `UnsafeCell` is the only way to obtain aliasable data that is considered
/// mutable.
CAST_REF_TO_MUT,
Deny,
"casts of `&T` to `&mut T` without interior mutability"
}

declare_lint_pass!(CastRefToMut => [CAST_REF_TO_MUT]);

impl<'tcx> LateLintPass<'tcx> for CastRefToMut {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; };

let e = e.peel_blocks();
let e = if let ExprKind::Cast(e, t) = e.kind
&& let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind {
e
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
expr
} else {
return;
};

let e = e.peel_blocks();
let e = if let ExprKind::Cast(e, t) = e.kind
&& let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind {
e
} else if let ExprKind::Call(path, [arg]) = e.kind
&& let ExprKind::Path(ref qpath) = path.kind
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
arg
} else {
return;
};

let e = e.peel_blocks();
if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() {
cx.emit_spanned_lint(CAST_REF_TO_MUT, expr.span, CastRefToMutDiag);
}
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@ extern crate tracing;

mod array_into_iter;
pub mod builtin;
mod cast_ref_to_mut;
mod context;
mod deref_into_dyn_supertrait;
mod drop_forget_useless;
@@ -97,6 +98,7 @@ use rustc_span::Span;

use array_into_iter::ArrayIntoIter;
use builtin::*;
use cast_ref_to_mut::*;
use deref_into_dyn_supertrait::*;
use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@@ -214,6 +216,7 @@ late_lint_methods!(
BoxPointers: BoxPointers,
PathStatements: PathStatements,
LetUnderscore: LetUnderscore,
CastRefToMut: CastRefToMut,
// Depends on referenced function signatures in expressions
UnusedResults: UnusedResults,
NonUpperCaseGlobals: NonUpperCaseGlobals,
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
@@ -718,6 +718,11 @@ pub enum InvalidFromUtf8Diag {
},
}

// cast_ref_to_mut.rs
#[derive(LintDiagnostic)]
#[diag(lint_cast_ref_to_mut)]
pub struct CastRefToMutDiag;

// hidden_unicode_codepoints.rs
#[derive(LintDiagnostic)]
#[diag(lint_hidden_unicode_codepoints)]
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -1146,6 +1146,8 @@ symbols! {
profiler_builtins,
profiler_runtime,
ptr,
ptr_cast_mut,
ptr_from_ref,
ptr_guaranteed_cmp,
ptr_mask,
ptr_null,
1 change: 1 addition & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
@@ -104,6 +104,7 @@ impl<T: ?Sized> *const T {
/// refactored.
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
#[rustc_diagnostic_item = "ptr_cast_mut"]
#[inline(always)]
pub const fn cast_mut(self) -> *mut T {
self as _
1 change: 1 addition & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
@@ -698,6 +698,7 @@ where
#[inline(always)]
#[must_use]
#[unstable(feature = "ptr_from_ref", issue = "106116")]
#[rustc_diagnostic_item = "ptr_from_ref"]
pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
r
}
26 changes: 0 additions & 26 deletions src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs

This file was deleted.

38 changes: 0 additions & 38 deletions src/tools/clippy/clippy_lints/src/casts/mod.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ mod cast_possible_truncation;
mod cast_possible_wrap;
mod cast_precision_loss;
mod cast_ptr_alignment;
mod cast_ref_to_mut;
mod cast_sign_loss;
mod cast_slice_different_sizes;
mod cast_slice_from_raw_parts;
@@ -330,41 +329,6 @@ declare_clippy_lint! {
"casting a function pointer to any integer type"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for casts of `&T` to `&mut T` anywhere in the code.
///
/// ### Why is this bad?
/// It’s basically guaranteed to be undefined behavior.
/// `UnsafeCell` is the only way to obtain aliasable data that is considered
/// mutable.
///
/// ### Example
/// ```rust,ignore
/// fn x(r: &i32) {
/// unsafe {
/// *(r as *const _ as *mut _) += 1;
/// }
/// }
/// ```
///
/// Instead consider using interior mutability types.
///
/// ```rust
/// use std::cell::UnsafeCell;
///
/// fn x(r: &UnsafeCell<i32>) {
/// unsafe {
/// *r.get() += 1;
/// }
/// }
/// ```
#[clippy::version = "1.33.0"]
pub CAST_REF_TO_MUT,
correctness,
"a cast of reference to a mutable pointer"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for expressions where a character literal is cast
@@ -680,7 +644,6 @@ impl_lint_pass!(Casts => [
CAST_POSSIBLE_TRUNCATION,
CAST_POSSIBLE_WRAP,
CAST_LOSSLESS,
CAST_REF_TO_MUT,
CAST_PTR_ALIGNMENT,
CAST_SLICE_DIFFERENT_SIZES,
UNNECESSARY_CAST,
@@ -747,7 +710,6 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
}
}

cast_ref_to_mut::check(cx, expr);
cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr);
ptr_as_ptr::check(cx, expr, &self.msrv);
1 change: 0 additions & 1 deletion src/tools/clippy/clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
@@ -81,7 +81,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::casts::CAST_POSSIBLE_WRAP_INFO,
crate::casts::CAST_PRECISION_LOSS_INFO,
crate::casts::CAST_PTR_ALIGNMENT_INFO,
crate::casts::CAST_REF_TO_MUT_INFO,
crate::casts::CAST_SIGN_LOSS_INFO,
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_lints/src/renamed_lints.rs
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::stutter", "clippy::module_name_repetitions"),
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
("clippy::zero_width_space", "clippy::invisible_characters"),
("clippy::cast_ref_to_mut", "cast_ref_to_mut"),
("clippy::clone_double_ref", "suspicious_double_ref_op"),
("clippy::drop_bounds", "drop_bounds"),
("clippy::drop_copy", "dropping_copy_types"),
31 changes: 0 additions & 31 deletions src/tools/clippy/tests/ui/cast_ref_to_mut.rs

This file was deleted.

22 changes: 0 additions & 22 deletions src/tools/clippy/tests/ui/cast_ref_to_mut.stderr

This file was deleted.

2 changes: 2 additions & 0 deletions src/tools/clippy/tests/ui/rename.fixed
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
#![allow(clippy::recursive_format_impl)]
#![allow(clippy::invisible_characters)]
#![allow(suspicious_double_ref_op)]
#![allow(cast_ref_to_mut)]
#![allow(drop_bounds)]
#![allow(dropping_copy_types)]
#![allow(dropping_references)]
@@ -76,6 +77,7 @@
#![warn(clippy::module_name_repetitions)]
#![warn(clippy::recursive_format_impl)]
#![warn(clippy::invisible_characters)]
#![warn(cast_ref_to_mut)]
#![warn(suspicious_double_ref_op)]
#![warn(drop_bounds)]
#![warn(dropping_copy_types)]
2 changes: 2 additions & 0 deletions src/tools/clippy/tests/ui/rename.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
#![allow(clippy::recursive_format_impl)]
#![allow(clippy::invisible_characters)]
#![allow(suspicious_double_ref_op)]
#![allow(cast_ref_to_mut)]
#![allow(drop_bounds)]
#![allow(dropping_copy_types)]
#![allow(dropping_references)]
@@ -76,6 +77,7 @@
#![warn(clippy::stutter)]
#![warn(clippy::to_string_in_display)]
#![warn(clippy::zero_width_space)]
#![warn(clippy::cast_ref_to_mut)]
#![warn(clippy::clone_double_ref)]
#![warn(clippy::drop_bounds)]
#![warn(clippy::drop_copy)]
Loading