Skip to content
Closed
49 changes: 31 additions & 18 deletions compiler/rustc_lint/src/reference_casting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)

fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
// <expr> as *mut ...
let e = if let ExprKind::Cast(e, t) = e.kind
let mut e = if let ExprKind::Cast(e, t) = e.kind
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
e
// <expr>.cast_mut()
Expand All @@ -112,23 +112,36 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
return None;
};

let e = e.peel_blocks();

// <expr> as *const ...
let e = if let ExprKind::Cast(e, t) = e.kind
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
e
// ptr::from_ref(<expr>)
} 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 None;
};

Some(e)
let mut had_at_least_one_cast = false;
loop {
e = e.peel_blocks();
// <expr> as *mut/const ... or <expr> as <uint>
e = if let ExprKind::Cast(expr, t) = e.kind
&& matches!(cx.typeck_results().node_type(t.hir_id).kind(), ty::RawPtr(_) | ty::Uint(_)) {
had_at_least_one_cast = true;
expr
// <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& matches!(
cx.tcx.get_diagnostic_name(def_id),
Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
)
{
had_at_least_one_cast = true;
expr
// ptr::from_ref(<expr>)
} 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) {
return Some(arg);
} else if had_at_least_one_cast {
return Some(e);
} else {
return None;
};
}
}

fn from_transmute<'tcx>(
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 @@ -543,6 +543,7 @@ symbols! {
const_panic_fmt,
const_param_ty,
const_precise_live_drops,
const_ptr_cast,
const_raw_ptr_deref,
const_raw_ptr_to_usize_cast,
const_refs_to_cell,
Expand Down Expand Up @@ -1160,6 +1161,7 @@ symbols! {
profiler_runtime,
ptr,
ptr_cast,
ptr_cast_const,
ptr_cast_mut,
ptr_const_is_null,
ptr_from_mut,
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 @@ -55,6 +55,7 @@ impl<T: ?Sized> *const T {
/// Casts to a pointer of another type.
#[stable(feature = "ptr_cast", since = "1.38.0")]
#[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
#[rustc_diagnostic_item = "const_ptr_cast"]
#[inline(always)]
pub const fn cast<U>(self) -> *const U {
self as _
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl<T: ?Sized> *mut T {
/// [`cast_mut`]: #method.cast_mut
#[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_const"]
#[inline(always)]
pub const fn cast_const(self) -> *const T {
self as _
Expand Down
38 changes: 25 additions & 13 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,21 +916,34 @@ impl !Send for Punct {}
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
impl !Sync for Punct {}

/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or
/// by a different token or whitespace ([`Spacing::Alone`]).
/// Indicates whether a `Punct` token can join with the following token
/// to form a multi-character operator.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub enum Spacing {
/// A `Punct` is not immediately followed by another `Punct`.
/// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
Alone,
/// A `Punct` is immediately followed by another `Punct`.
/// E.g. `+` is `Joint` in `+=` and `++`.
/// A `Punct` token can join with the following token to form a multi-character operator.
///
/// In token streams constructed using proc macro interfaces `Joint` punctuation tokens can be
/// followed by any other tokens. \
/// However, in token streams parsed from source code compiler will only set spacing to `Joint`
/// in the following cases:
/// - A `Punct` is immediately followed by another `Punct` without a whitespace. \
/// E.g. `+` is `Joint` in `+=` and `++`.
/// - A single quote `'` is immediately followed by an identifier without a whitespace. \
/// E.g. `'` is `Joint` in `'lifetime`.
///
/// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`.
/// This list may be extended in the future to enable more token combinations.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
Joint,
/// A `Punct` token cannot join with the following token to form a multi-character operator.
///
/// `Alone` punctuation tokens can be followed by any other tokens. \
/// In token streams parsed from source code compiler will set spacing to `Alone` in all cases
/// not covered by the conditions for `Joint` above. \
/// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`.
/// In particular, token not followed by anything will also be marked as `Alone`.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
Alone,
}

impl Punct {
Expand Down Expand Up @@ -962,10 +975,9 @@ impl Punct {
self.0.ch as char
}

/// Returns the spacing of this punctuation character, indicating whether it's immediately
/// followed by another `Punct` in the token stream, so they can potentially be combined into
/// a multi-character operator (`Joint`), or it's followed by some other token or whitespace
/// (`Alone`) so the operator has certainly ended.
/// Returns the spacing of this punctuation character, indicating whether it can be potentially
/// combined into a multi-character operator with the following token (`Joint`), or the operator
/// has certainly ended (`Alone`).
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub fn spacing(&self) -> Spacing {
if self.0.joint { Spacing::Joint } else { Spacing::Alone }
Expand Down
3 changes: 3 additions & 0 deletions src/doc/rustc/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ title = "The rustc book"
git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc"
edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}"

[output.html.search]
use-boolean-and = true

[output.html.playground]
runnable = false
2 changes: 1 addition & 1 deletion src/librustdoc/html/templates/STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Askama templates support quite sophisticated control flow. To keep our templates
simple and understandable, we use only a subset: `if` and `for`. In particular
we avoid [assignments in the template logic][assignments] and [Askama
macros][macros]. This also may make things easier if we switch to a different
Jinja-style template system, like Askama, in the future.
Jinja-style template system in the future.

[assignments]: https://djc.github.io/askama/template_syntax.html#assignments
[macros]: https://djc.github.io/askama/template_syntax.html#macros
29 changes: 29 additions & 0 deletions tests/ui/lint/reference_casting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ extern "C" {
fn int_ffi(c: *mut i32);
}

fn static_u8() -> &'static u8 {
&8
}

unsafe fn ref_to_mut() {
let num = &3i32;

Expand All @@ -24,12 +28,28 @@ unsafe fn ref_to_mut() {
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const i32).cast::<i32>().cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_const().cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior

let deferred = num as *const i32 as *mut i32;
let _num = &mut *deferred;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
let _num = &mut *deferred;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const _ as usize as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior

unsafe fn generic_ref_cast_mut<T>(this: &T) -> &mut T {
&mut *((this as *const _) as *mut _)
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
}
}

unsafe fn assign_to_ref() {
Expand All @@ -55,6 +75,15 @@ unsafe fn assign_to_ref() {
let value = num as *const i32 as *mut i32;
*value = 1;
//~^ ERROR assigning to `&T` is undefined behavior
*(num as *const i32).cast::<i32>().cast_mut() = 2;
//~^ ERROR assigning to `&T` is undefined behavior
*(num as *const _ as usize as *mut i32) = 2;
//~^ ERROR assigning to `&T` is undefined behavior

unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
*(this as *const _ as *mut _) = a;
//~^ ERROR assigning to `&T` is undefined behavior
}
}

unsafe fn no_warn() {
Expand Down
Loading