diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5271217593e62..310cbb430bbe7 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -71,6 +71,7 @@ mod opaque_hidden_inferred_bound; mod passes; mod precedence; mod ptr_nulls; +mod raw_borrows_via_references; mod redundant_semicolon; mod reference_casting; mod runtime_symbols; @@ -117,6 +118,7 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use precedence::*; use ptr_nulls::*; +use raw_borrows_via_references::*; use redundant_semicolon::*; use reference_casting::*; use runtime_symbols::*; @@ -273,6 +275,7 @@ late_lint_methods!( InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ImplicitProvenanceCasts: ImplicitProvenanceCasts, CVoidReturns: CVoidReturns, + RawBorrowsViaReferences: RawBorrowsViaReferences, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7ea5635034a27..0a0dce2e9d2fc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3037,3 +3037,29 @@ pub(crate) enum Ptr2IntSuggestion<'tcx> { cast_span: Span, }, } + +#[derive(Diagnostic)] +#[diag( + "creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers" +)] +pub(crate) struct RawBorrowViaReference<'a> { + #[subdiagnostic] + pub suggestion: RawBorrowViaReferenceSuggestion<'a>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum RawBorrowViaReferenceSuggestion<'a> { + #[multipart_suggestion( + "consider using `&raw {$mutbl}` for a safer and more explicit raw pointer", + applicability = "machine-applicable" + )] + Spanful { + #[suggestion_part(code = "&raw {mutbl} ")] + left: Span, + #[suggestion_part(code = "")] + right: Span, + mutbl: &'a str, + }, + #[help("consider using `&raw {$mutbl}` for a safer and more explicit raw pointer")] + Spanless { mutbl: &'a str }, +} diff --git a/compiler/rustc_lint/src/raw_borrows_via_references.rs b/compiler/rustc_lint/src/raw_borrows_via_references.rs new file mode 100644 index 0000000000000..69ae1b61e094c --- /dev/null +++ b/compiler/rustc_lint/src/raw_borrows_via_references.rs @@ -0,0 +1,83 @@ +use rustc_ast::BorrowKind; +use rustc_hir::{Expr, ExprKind, TyKind}; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{RawBorrowViaReference, RawBorrowViaReferenceSuggestion}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `raw_borrows_via_references` lint checks for references that decay immediately into raw borrows. + /// + /// ### Example + /// + /// ```rust + /// #![warn(raw_borrows_via_references)] + /// + /// 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 makes code + /// less explicit and can lead to undefined behavior. Creating a reference + /// induces aliasing assumptions that the compiler relies on, so an + /// otherwise-pointless reference can cause undefined behavior even when the + /// reference is never read through. Avoiding them keeps the code more + /// explicit and easier to reason about. + /// + /// See the [Reference] for the full set of validity requirements that + /// references must uphold. + /// + /// [Reference]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// This lint is "allow" by default because it will trigger for a large + /// amount of existing Rust code. + /// Eventually it is desired for this to become warn-by-default. + pub RAW_BORROWS_VIA_REFERENCES, + // FIXME: This should eventually be `Warn`, see the "Explanation" above. + Allow, + "creating raw borrows via references is discouraged" +} + +declare_lint_pass!(RawBorrowsViaReferences => [RAW_BORROWS_VIA_REFERENCES]); + +impl<'tcx> LateLintPass<'tcx> for RawBorrowsViaReferences { + 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 + && addr_of_exp.is_syntactic_place_expr() + { + let suggestion = if let Some(addr_of_span) = + addr_of_exp.span.find_ancestor_in_same_ctxt(expr.span) + && let Some(ty_span) = ty.span.find_ancestor_in_same_ctxt(expr.span) + && expr.span.can_be_used_for_suggestions() + && addr_of_span.can_be_used_for_suggestions() + && ty_span.can_be_used_for_suggestions() + { + RawBorrowViaReferenceSuggestion::Spanful { + left: expr.span.until(addr_of_span), + right: addr_of_span.shrink_to_hi().until(ty_span.shrink_to_hi()), + mutbl: mutbl.ptr_str(), + } + } else { + RawBorrowViaReferenceSuggestion::Spanless { mutbl: mutbl.ptr_str() } + }; + + cx.emit_span_lint( + RAW_BORROWS_VIA_REFERENCES, + expr.span, + RawBorrowViaReference { suggestion }, + ); + } + } +} diff --git a/tests/ui/lint/lint-raw-borrows-via-references.fixed b/tests/ui/lint/lint-raw-borrows-via-references.fixed new file mode 100644 index 0000000000000..8b81164f60375 --- /dev/null +++ b/tests/ui/lint/lint-raw-borrows-via-references.fixed @@ -0,0 +1,87 @@ +//@ check-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#![allow(dead_code, unused_variables)] +#![warn(raw_borrows_via_references)] + +struct A { + a: i32, + b: u8, +} + +fn via_ref(x: *const (i32, i32)) -> *const i32 { + unsafe { &raw const (*x).0 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_struct(x: *const A) -> *const u8 { + unsafe { &raw const (*x).b } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_mut(x: *mut (i32, i32)) -> *mut i32 { + unsafe { &raw mut (*x).0 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_struct_mut(x: *mut A) -> *mut i32 { + unsafe { &raw mut (*x).a } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn multiple_casts(x: *const (i32, i32)) -> *const u8 { + unsafe { &raw const (*x).0 as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn ret_i32() -> i32 { + 0 +} + +fn temporaries() { + let _ = &1 as *const i32; + let _ = &(1 + 2) as *const i32; + let _ = &ret_i32() as *const i32; + let _ = &(1, 2) as *const (i32, i32); + let _ = &[1, 2, 3] as *const [i32; 3]; + let _ = &A { a: 0, b: 0 } as *const A; + let _ = &mut 4 as *mut i32; +} + +fn inner_blocks() { + let x = 0; + let _ = { &raw const x }; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let _ = { + let y = 0; + { &raw const y } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + }; + + let _ = { &1 as *const i32 }; +} + +macro_rules! ref_cast { + ($e:expr) => { + &$e as *const i32 + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + }; +} + +fn from_macro(x: *const i32) -> *const i32 { + unsafe { ref_cast!(*x) } +} + +fn main() { + let a = 0; + let a = &raw const a; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let mut b = 0; + let b = &raw mut b; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let i = &1 as *const i32; +} diff --git a/tests/ui/lint/lint-raw-borrows-via-references.rs b/tests/ui/lint/lint-raw-borrows-via-references.rs new file mode 100644 index 0000000000000..621907bd15df0 --- /dev/null +++ b/tests/ui/lint/lint-raw-borrows-via-references.rs @@ -0,0 +1,87 @@ +//@ check-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#![allow(dead_code, unused_variables)] +#![warn(raw_borrows_via_references)] + +struct A { + a: i32, + b: u8, +} + +fn via_ref(x: *const (i32, i32)) -> *const i32 { + unsafe { &(*x).0 as *const i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_struct(x: *const A) -> *const u8 { + unsafe { &(*x).b as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_mut(x: *mut (i32, i32)) -> *mut i32 { + unsafe { &mut (*x).0 as *mut i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn via_ref_struct_mut(x: *mut A) -> *mut i32 { + unsafe { &mut (*x).a as *mut i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn multiple_casts(x: *const (i32, i32)) -> *const u8 { + unsafe { &(*x).0 as *const i32 as *const u8 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] +} + +fn ret_i32() -> i32 { + 0 +} + +fn temporaries() { + let _ = &1 as *const i32; + let _ = &(1 + 2) as *const i32; + let _ = &ret_i32() as *const i32; + let _ = &(1, 2) as *const (i32, i32); + let _ = &[1, 2, 3] as *const [i32; 3]; + let _ = &A { a: 0, b: 0 } as *const A; + let _ = &mut 4 as *mut i32; +} + +fn inner_blocks() { + let x = 0; + let _ = { &x as *const i32 }; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let _ = { + let y = 0; + { &y as *const i32 } + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + }; + + let _ = { &1 as *const i32 }; +} + +macro_rules! ref_cast { + ($e:expr) => { + &$e as *const i32 + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + }; +} + +fn from_macro(x: *const i32) -> *const i32 { + unsafe { ref_cast!(*x) } +} + +fn main() { + let a = 0; + let a = &a as *const i32; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let mut b = 0; + let b = &mut b as *mut i32; + //~^ WARN creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers [raw_borrows_via_references] + + let i = &1 as *const i32; +} diff --git a/tests/ui/lint/lint-raw-borrows-via-references.stderr b/tests/ui/lint/lint-raw-borrows-via-references.stderr new file mode 100644 index 0000000000000..f04922b52f05a --- /dev/null +++ b/tests/ui/lint/lint-raw-borrows-via-references.stderr @@ -0,0 +1,127 @@ +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:14:14 + | +LL | unsafe { &(*x).0 as *const i32 } + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-raw-borrows-via-references.rs:6:9 + | +LL | #![warn(raw_borrows_via_references)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).0 as *const i32 } +LL + unsafe { &raw const (*x).0 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:19:14 + | +LL | unsafe { &(*x).b as *const u8 } + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).b as *const u8 } +LL + unsafe { &raw const (*x).b } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:24:14 + | +LL | unsafe { &mut (*x).0 as *mut i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - unsafe { &mut (*x).0 as *mut i32 } +LL + unsafe { &raw mut (*x).0 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:29:14 + | +LL | unsafe { &mut (*x).a as *mut i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - unsafe { &mut (*x).a as *mut i32 } +LL + unsafe { &raw mut (*x).a } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:34:14 + | +LL | unsafe { &(*x).0 as *const i32 as *const u8 } + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - unsafe { &(*x).0 as *const i32 as *const u8 } +LL + unsafe { &raw const (*x).0 as *const u8 } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:54:15 + | +LL | let _ = { &x as *const i32 }; + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - let _ = { &x as *const i32 }; +LL + let _ = { &raw const x }; + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:59:11 + | +LL | { &y as *const i32 } + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - { &y as *const i32 } +LL + { &raw const y } + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:68:10 + | +LL | &$e as *const i32 + | ^^^^^^^^^^^^^^^^ +... +LL | unsafe { ref_cast!(*x) } + | ------------- in this macro invocation + | + = help: consider using `&raw const` for a safer and more explicit raw pointer + = note: this warning originates in the macro `ref_cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:79:13 + | +LL | let a = &a as *const i32; + | ^^^^^^^^^^^^^^^^ + | +help: consider using `&raw const` for a safer and more explicit raw pointer + | +LL - let a = &a as *const i32; +LL + let a = &raw const a; + | + +warning: creating an intermediate reference implies aliasing requirements even when immediately cast to a raw pointers + --> $DIR/lint-raw-borrows-via-references.rs:83:13 + | +LL | let b = &mut b as *mut i32; + | ^^^^^^^^^^^^^^^^^^ + | +help: consider using `&raw mut` for a safer and more explicit raw pointer + | +LL - let b = &mut b as *mut i32; +LL + let b = &raw mut b; + | + +warning: 10 warnings emitted +