From 4539a7571630137ac39f474f964b670bfe720722 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sat, 23 May 2026 20:10:55 +0200 Subject: [PATCH 1/2] convert fuzzy_provenance_casts to late lint --- compiler/rustc_hir_typeck/src/cast.rs | 23 +---- compiler/rustc_hir_typeck/src/errors.rs | 24 ----- .../rustc_lint/src/fuzzy_provenance_casts.rs | 79 ++++++++++++++++ compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 24 +++++ compiler/rustc_lint_defs/src/builtin.rs | 45 --------- ...-fuzzy-provenance-casts-with-inner-attr.rs | 24 ----- ...zy-provenance-casts-with-inner-attr.stderr | 93 ------------------- 8 files changed, 107 insertions(+), 208 deletions(-) create mode 100644 compiler/rustc_lint/src/fuzzy_provenance_casts.rs delete mode 100644 tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs delete mode 100644 tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 12429218a6fc1..94dac64c5071b 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -869,10 +869,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => { - self.fuzzy_provenance_int2ptr_lint(fcx); - self.check_addr_ptr_cast(fcx, mt) - } + (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), @@ -1168,24 +1165,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } - fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let sugg = self.span.can_be_used_for_suggestions().then(|| { - errors::LossyProvenanceInt2PtrSuggestion { - lo: self.expr_span.shrink_to_lo(), - hi: self.expr_span.shrink_to_hi().to(self.cast_span), - } - }); - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::FUZZY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index fcbb8a75f9567..a5c76f1e7d0f9 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -377,18 +377,6 @@ impl Subdiagnostic for TypeMismatchFruTypo { } } -#[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] -#[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" -)] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: Option, -} - #[derive(Diagnostic)] #[diag("cannot add {$traits_len -> [1] auto trait {$traits} @@ -404,18 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject { pub traits: DiagSymbolList, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", - applicability = "has-placeholders" -)] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { - #[suggestion_part(code = "(...).with_addr(")] - pub lo: Span, - #[suggestion_part(code = ")")] - pub hi: Span, -} - #[derive(Diagnostic)] #[diag( "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs new file mode 100644 index 0000000000000..699004a3b37e0 --- /dev/null +++ b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs @@ -0,0 +1,79 @@ +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer + /// and a pointer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(fuzzy_provenance_casts)] + /// + /// fn main() { + /// let _dangling = 16_usize as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting an integer to a pointer is considered bad style, as a pointer + /// contains, besides the *address* also a *provenance*, indicating what + /// memory the pointer is allowed to read/write. Casting an integer, which + /// doesn't have provenance, to a pointer requires the compiler to assign + /// (guess) provenance. The compiler assigns "all exposed valid" (see the + /// docs of [`ptr::with_exposed_provenance`] for more information about this + /// "exposing"). This penalizes the optimiser and is not well suited for + /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI + /// platforms). + /// + /// It is much better to use [`ptr::with_addr`] instead to specify the + /// provenance you want. If using this function is not possible because the + /// code relies on exposed provenance then there is as an escape hatch + /// [`ptr::with_exposed_provenance`]. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html + pub FUZZY_PROVENANCE_CASTS, + Allow, + "a fuzzy integer to pointer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between an integer and a pointer. + FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from integer to pointer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_integral() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_raw_ptr() { + return; + } + + let sugg = + expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2ae338d34b1f3..e5cbc8ad00c1b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,6 +45,7 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; +mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; @@ -92,6 +93,7 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; +use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; @@ -250,6 +252,7 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, + FuzzyProvenanceCasts: FuzzyProvenanceCasts, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 091b7ac228b54..d64d19c97a8c7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2932,3 +2932,27 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { #[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] #[note("this method was used to add checks to the `Eq` derive macro")] pub(crate) struct EqInternalMethodImplemented; + +#[derive(Diagnostic)] +#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[help( + "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" +)] +pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + applicability = "has-placeholders" +)] +pub(crate) struct LossyProvenanceInt2PtrSuggestion { + #[suggestion_part(code = "(...).with_addr(")] + pub lo: Span, + #[suggestion_part(code = ")")] + pub hi: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 46069a5868a91..92e67c5242ced 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -51,7 +51,6 @@ declare_lint_pass! { FLOAT_LITERAL_F32_FALLBACK, FORBIDDEN_LINT_GROUPS, FUNCTION_ITEM_REFERENCES, - FUZZY_PROVENANCE_CASTS, HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, @@ -2593,50 +2592,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - declare_lint! { /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer /// and an integer. diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs deleted file mode 100644 index b8deb6ab3c64a..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Regression test for #137588. -// The compiler used to ICE when emitting a `fuzzy_provenance_casts` lint -// diagnostic for code with an inner attribute spanning the entire file, -// causing `draw_code_line` to panic on an empty `file_lines` from a dummy span. - -//@ edition:2024 -//@ compile-flags: -Wfuzzy-provenance-casts - -#![feature(strict_provenance_lints)] -//~^ ERROR too many leading `super` keywords [E0433] -//~| ERROR cannot find type `Ts` in this scope [E0425] -//~| ERROR `#[prelude_import]` is for use by rustc only [E0658] -//~| WARN strict provenance disallows casting integer `usize` to pointer `*const u32` -#![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -//~^ ERROR use of unstable library feature `contracts` [E0658] -//~| ERROR inner macro attributes are unstable [E0658] -//~| ERROR cannot find type `Stars` in this scope [E0433] - -pub(super) fn foo() -> *const Ts { - unsafe { - let p2 = 0x52 as *const u32; - } -} -//~^ ERROR `main` function not found in crate diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr deleted file mode 100644 index 342f39d2450d8..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error[E0658]: use of unstable library feature `contracts` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #128044 for more information - = help: add `#![feature(contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: inner macro attributes are unstable - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54726 for more information - = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0433]: too many leading `super` keywords - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ there are too many leading `super` keywords - -error[E0425]: cannot find type `Ts` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ not found in this scope - -error[E0658]: `#[prelude_import]` is for use by rustc only - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: add `#![feature(prelude_import)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0601]: `main` function not found in crate `ice_fuzzy_provenance_casts_with_inner_attr` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:23:2 - | -LL | } - | ^ consider adding a `main` function to `$DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs` - -error[E0433]: cannot find type `Stars` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:50 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^ use of undeclared type `Stars` - -warning: strict provenance disallows casting integer `usize` to pointer `*const u32` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead - = note: requested on the command line with `-W fuzzy-provenance-casts` -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - | -LL - #![feature(strict_provenance_lints)] -LL - -LL - -LL - -LL - -LL - #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -LL - -LL - -LL - -LL - -LL - pub(super) fn foo() -> *const Ts { -LL - unsafe { -LL - let p2 = 0x52 as *const u32; -LL - } -LL - } -LL + (...).with_addr() - | - -error: aborting due to 7 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0425, E0433, E0601, E0658. -For more information about an error, try `rustc --explain E0425`. From f8f740baf0f04da9bd805732e796df0bebd9d454 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sat, 23 May 2026 21:23:51 +0200 Subject: [PATCH 2/2] convert lossy_provenance_casts to late lint --- compiler/rustc_hir_typeck/src/cast.rs | 42 +------- compiler/rustc_hir_typeck/src/errors.rs | 58 ----------- compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 58 +++++++++++ .../rustc_lint/src/lossy_provenance_casts.rs | 98 +++++++++++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 47 --------- 6 files changed, 161 insertions(+), 145 deletions(-) create mode 100644 compiler/rustc_lint/src/lossy_provenance_casts.rs diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 94dac64c5071b..8902505c11762 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -28,7 +28,6 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). -use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; @@ -860,10 +859,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(t_c)) => { - self.lossy_provenance_ptr2int_lint(fcx, t_c); - self.check_ptr_addr_cast(fcx, m_expr) - } + (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (FnPtr, Int(_)) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) @@ -1130,41 +1127,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - - let sugg = self.span.can_be_used_for_suggestions().then(|| { - let expr_prec = fcx.precedence(self.expr); - let needs_parens = expr_prec < ExprPrecedence::Unambiguous; - let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); - let expr_span = self.expr_span.shrink_to_lo(); - match (needs_parens, needs_cast) { - (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_ty, - }, - (true, false) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } - } - (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, - } - }); - - let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::LOSSY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a5c76f1e7d0f9..3457cc373413a 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -392,64 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject { pub traits: DiagSymbolList, } -#[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: Option>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParensCast { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr() as {cast_ty}")] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParens { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr()")] - cast_span: Span, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr() as {cast_ty}", - applicability = "maybe-incorrect" - )] - NeedsCast { - #[primary_span] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr()", - applicability = "maybe-incorrect" - )] - Other { - #[primary_span] - cast_span: Span, - }, -} - #[derive(Subdiagnostic)] pub(crate) enum HelpUseLatestEdition { #[help("set `edition = \"{$edition}\"` in `Cargo.toml`")] diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index e5cbc8ad00c1b..1efc8b70ef22d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -57,6 +57,7 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; +mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -102,6 +103,7 @@ use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; +use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -253,6 +255,7 @@ late_lint_methods!( LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, FuzzyProvenanceCasts: FuzzyProvenanceCasts, + LossyProvenanceCasts: LossyProvenanceCasts, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d64d19c97a8c7..c77e70dcbe9bb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2956,3 +2956,61 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { #[suggestion_part(code = ")")] pub hi: Span, } + +#[derive(Diagnostic)] +#[diag( + "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" +)] +#[help( + "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" +)] +pub(crate) struct LossyProvenancePtr2Int<'tcx> { + pub cast_from_ty: Ty<'tcx>, + pub cast_to_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParensCast { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr() as {cast_to_ty}")] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParens { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr()")] + cast_span: Span, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr() as {cast_to_ty}", + applicability = "maybe-incorrect" + )] + NeedsCast { + #[primary_span] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr()", + applicability = "maybe-incorrect" + )] + Other { + #[primary_span] + cast_span: Span, + }, +} diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs new file mode 100644 index 0000000000000..bdb5bdf6bb0e3 --- /dev/null +++ b/compiler/rustc_lint/src/lossy_provenance_casts.rs @@ -0,0 +1,98 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer + /// and an integer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(lossy_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let _addr: usize = &x as *const u8 as usize; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting a pointer to an integer is a lossy operation, because beyond + /// just an *address* a pointer may be associated with a particular + /// *provenance*. This information is used by the optimiser and for dynamic + /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). + /// + /// Since this cast is lossy, it is considered good style to use the + /// [`ptr::addr`] method instead, which has a similar effect, but doesn't + /// "expose" the pointer provenance. This improves optimisation potential. + /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information + /// about exposing pointer provenance. + /// + /// If your code can't comply with strict provenance and needs to expose + /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, + /// which preserves the behaviour of `as usize` casts while being explicit + /// about the semantics. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr + /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance + pub LOSSY_PROVENANCE_CASTS, + Allow, + "a lossy pointer to integer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between a pointer and an integer. + LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from pointer to integer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_raw_ptr() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_integral() { + return; + } + + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { + expr_span, + cast_span, + cast_to_ty, + }, + (true, false) => { + LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } + } + (false, true) => { + LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } + } + (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 92e67c5242ced..caa41fd0f6ab3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -66,7 +66,6 @@ declare_lint_pass! { LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, - LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_USE_EXTERN_CRATE, MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -2592,52 +2591,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - declare_lint! { /// The `const_evaluatable_unchecked` lint detects a generic constant used /// in a type.