Skip to content

Commit

Permalink
Auto merge of #111567 - Urgau:uplift_cast_ref_to_mut, r=b-naber
Browse files Browse the repository at this point in the history
Uplift `clippy::cast_ref_to_mut` lint

This PR aims at uplifting the `clippy::cast_ref_to_mut` lint into rustc.

## `cast_ref_to_mut`

(deny-by-default)

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;
    }
}
```

### Explanation

Casting `&T` to `&mut T` without interior mutability is undefined behavior, as it's a violation of Rust reference aliasing requirements.

-----

Mostly followed the instructions for uplifting a clippy lint described here: #99696 (review)

`@rustbot` label: +I-lang-nominated
r? compiler

-----

For Clippy:

changelog: Moves: Uplifted `clippy::cast_ref_to_mut` into rustc
  • Loading branch information
bors committed Jun 1, 2023
2 parents 9af3865 + 32d4e1c commit ba1690b
Show file tree
Hide file tree
Showing 22 changed files with 276 additions and 170 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
Expand Down
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
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,8 @@ symbols! {
profiler_builtins,
profiler_runtime,
ptr,
ptr_cast_mut,
ptr_from_ref,
ptr_guaranteed_cmp,
ptr_mask,
ptr_null,
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
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
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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,
Expand Down
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
Expand Up @@ -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"),
Expand Down
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
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down
2 changes: 2 additions & 0 deletions src/tools/clippy/tests/ui/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down
Loading

0 comments on commit ba1690b

Please sign in to comment.