Skip to content

Commit 3d31306

Browse files
committed
Merge and reframe strict_provenance_lints
1 parent fd07dbf commit 3d31306

28 files changed

Lines changed: 253 additions & 291 deletions

compiler/rustc_lint/src/fuzzy_provenance_casts.rs

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use rustc_ast::util::parser::ExprPrecedence;
2+
use rustc_hir as hir;
3+
use rustc_middle::ty::Ty;
4+
use rustc_session::{declare_lint, declare_lint_pass};
5+
6+
use crate::lints::{
7+
ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion,
8+
Ptr2IntSuggestion,
9+
};
10+
use crate::{LateContext, LateLintPass};
11+
12+
declare_lint! {
13+
/// The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer
14+
/// casts.
15+
///
16+
/// ### Example
17+
///
18+
/// ```rust
19+
/// #![feature(strict_provenance_lints)]
20+
/// #![warn(implicit_provenance_casts)]
21+
///
22+
/// fn main() {
23+
/// let x: u8 = 37;
24+
/// let addr: usize = &x as *const u8 as usize;
25+
/// let _ptr = addr as *const u8;
26+
/// }
27+
/// ```
28+
///
29+
/// {{produces}}
30+
///
31+
/// ### Explanation
32+
///
33+
/// This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where
34+
/// possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit.
35+
/// For more information on pointer provenance, see the [`std::ptr` documentation][provenance].
36+
///
37+
/// Earlier versions of Rust did not have a clear answer how integer-to-pointer and
38+
/// pointer-to-integer casts interact with provenance. Such casts are now defined to use the
39+
/// exposed provenance model, but in many cases the code can be updated to strict provenance
40+
/// APIs, which is preferable as it enables more precise reasoning about unsafe code, both by
41+
/// humans and by tools like [Miri].
42+
///
43+
/// However, there are situations where exposed provenance is required or following the strict
44+
/// provenance model requires major refactorings. In those cases, it's still useful to replace
45+
/// the `as` casts with equivalent explicit use of exposed provenance APIs and a comment
46+
/// explaining why they are needed.
47+
///
48+
/// [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance
49+
/// [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance
50+
/// [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance
51+
/// [Miri]: https://github.com/rust-lang/miri
52+
pub IMPLICIT_PROVENANCE_CASTS,
53+
Allow,
54+
"an `as` cast relying on exposed provenance is used",
55+
@feature_gate = strict_provenance_lints;
56+
}
57+
58+
declare_lint_pass!(
59+
/// Lint for int2ptr and ptr2int `as` casts.
60+
ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS]
61+
);
62+
63+
impl<'tcx> LateLintPass<'tcx> for ImplicitProvenanceCasts {
64+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
65+
let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return };
66+
67+
let typeck_results = cx.typeck_results();
68+
let cast_from_ty = typeck_results.expr_ty(cast_from_expr);
69+
if cast_from_ty.is_raw_ptr() {
70+
let cast_to_ty = typeck_results.expr_ty(expr);
71+
if cast_to_ty.is_integral() {
72+
lint_ptr2int(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty)
73+
}
74+
} else if cast_from_ty.is_integral() {
75+
let cast_to_ty = typeck_results.expr_ty(expr);
76+
if cast_to_ty.is_raw_ptr() {
77+
lint_int2ptr(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty)
78+
}
79+
}
80+
}
81+
}
82+
83+
fn lint_ptr2int<'tcx>(
84+
cx: &LateContext<'tcx>,
85+
expr: &'tcx hir::Expr<'tcx>,
86+
cast_from_expr: &'tcx hir::Expr<'tcx>,
87+
cast_from_ty: Ty<'tcx>,
88+
cast_to_hir: &'tcx hir::Ty<'tcx>,
89+
cast_to_ty: Ty<'tcx>,
90+
) {
91+
let sugg = expr.span.can_be_used_for_suggestions().then(|| {
92+
let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous;
93+
let needs_cast = !cast_to_ty.is_usize();
94+
let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span);
95+
let expr_span = cast_from_expr.span.shrink_to_lo();
96+
match (needs_parens, needs_cast) {
97+
(true, true) => Ptr2IntSuggestion::NeedsParensCast { expr_span, cast_span, cast_to_ty },
98+
(true, false) => Ptr2IntSuggestion::NeedsParens { expr_span, cast_span },
99+
(false, true) => Ptr2IntSuggestion::NeedsCast { cast_span, cast_to_ty },
100+
(false, false) => Ptr2IntSuggestion::Other { cast_span },
101+
}
102+
});
103+
104+
let lint = ImplicitProvenanceCastsPtr2Int { cast_from_ty, cast_to_ty, sugg };
105+
cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint);
106+
}
107+
108+
fn lint_int2ptr<'tcx>(
109+
cx: &LateContext<'tcx>,
110+
expr: &'tcx hir::Expr<'tcx>,
111+
cast_from_expr: &'tcx hir::Expr<'tcx>,
112+
cast_from_ty: Ty<'tcx>,
113+
cast_to_hir: &'tcx hir::Ty<'tcx>,
114+
cast_to_ty: Ty<'tcx>,
115+
) {
116+
let sugg = expr.span.can_be_used_for_suggestions().then(|| Int2PtrSuggestion {
117+
lo: cast_from_expr.span.shrink_to_lo(),
118+
hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span),
119+
});
120+
let lint = ImplicitProvenanceCastsInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg };
121+
cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint)
122+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ mod expect;
4545
mod for_loops_over_fallibles;
4646
mod foreign_modules;
4747
mod function_cast_as_integer;
48-
mod fuzzy_provenance_casts;
4948
mod gpukernel_abi;
5049
mod if_let_rescope;
5150
mod impl_trait_overcaptures;
51+
mod implicit_provenance_casts;
5252
mod interior_mutable_consts;
5353
mod internal;
5454
mod invalid_from_utf8;
@@ -57,7 +57,6 @@ mod let_underscore;
5757
mod levels;
5858
pub mod lifetime_syntax;
5959
mod lints;
60-
mod lossy_provenance_casts;
6160
mod macro_expr_fragment_specifier_2024_migration;
6261
mod map_unit_fn;
6362
mod multiple_supertrait_upcastable;
@@ -95,16 +94,15 @@ use drop_forget_useless::*;
9594
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9695
use for_loops_over_fallibles::*;
9796
use function_cast_as_integer::*;
98-
use fuzzy_provenance_casts::FuzzyProvenanceCasts;
9997
use gpukernel_abi::*;
10098
use if_let_rescope::IfLetRescope;
10199
use impl_trait_overcaptures::ImplTraitOvercaptures;
100+
use implicit_provenance_casts::ImplicitProvenanceCasts;
102101
use interior_mutable_consts::*;
103102
use internal::*;
104103
use invalid_from_utf8::*;
105104
use let_underscore::*;
106105
use lifetime_syntax::*;
107-
use lossy_provenance_casts::LossyProvenanceCasts;
108106
use macro_expr_fragment_specifier_2024_migration::*;
109107
use map_unit_fn::*;
110108
use multiple_supertrait_upcastable::*;
@@ -270,8 +268,7 @@ late_lint_methods!(
270268
CheckTransmutes: CheckTransmutes,
271269
LifetimeSyntax: LifetimeSyntax,
272270
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
273-
FuzzyProvenanceCasts: FuzzyProvenanceCasts,
274-
LossyProvenanceCasts: LossyProvenanceCasts,
271+
ImplicitProvenanceCasts: ImplicitProvenanceCasts,
275272
]
276273
]
277274
);
@@ -410,6 +407,8 @@ fn register_builtins(store: &mut LintStore) {
410407
store.register_renamed("static_mut_ref", "static_mut_refs");
411408
store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries");
412409
store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes");
410+
store.register_renamed("fuzzy_provenance_casts", "implicit_provenance_casts");
411+
store.register_renamed("lossy_provenance_casts", "implicit_provenance_casts");
413412

414413
// These were moved to tool lints, but rustc still sees them when compiling normally, before
415414
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use

compiler/rustc_lint/src/lints.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,45 +2934,43 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
29342934
pub(crate) struct EqInternalMethodImplemented;
29352935

29362936
#[derive(Diagnostic)]
2937-
#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")]
2937+
#[diag("cast from `{$expr_ty}` to `{$cast_ty}` implicitly relies on exposed provenance")]
29382938
#[help(
2939-
"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"
2939+
"if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()`"
29402940
)]
2941-
pub(crate) struct LossyProvenanceInt2Ptr<'tcx> {
2941+
#[note("for more information, visit <https://doc.rust-lang.org/std/ptr/index.html#provenance>")]
2942+
pub(crate) struct ImplicitProvenanceCastsInt2Ptr<'tcx> {
29422943
pub expr_ty: Ty<'tcx>,
29432944
pub cast_ty: Ty<'tcx>,
29442945
#[subdiagnostic]
2945-
pub sugg: Option<LossyProvenanceInt2PtrSuggestion>,
2946+
pub sugg: Option<Int2PtrSuggestion>,
29462947
}
29472948

29482949
#[derive(Subdiagnostic)]
29492950
#[multipart_suggestion(
2950-
"use `.with_addr()` to adjust a valid pointer in the same allocation, to this address",
2951+
"use `.with_addr()` to adjust the address of a valid pointer in the same allocation",
29512952
applicability = "has-placeholders"
29522953
)]
2953-
pub(crate) struct LossyProvenanceInt2PtrSuggestion {
2954+
pub(crate) struct Int2PtrSuggestion {
29542955
#[suggestion_part(code = "(...).with_addr(")]
29552956
pub lo: Span,
29562957
#[suggestion_part(code = ")")]
29572958
pub hi: Span,
29582959
}
29592960

29602961
#[derive(Diagnostic)]
2961-
#[diag(
2962-
"under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`"
2963-
)]
2964-
#[help(
2965-
"if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead"
2966-
)]
2967-
pub(crate) struct LossyProvenancePtr2Int<'tcx> {
2962+
#[diag("cast from `{$cast_from_ty}` to `{$cast_to_ty}` implicitly exposes pointer provenance")]
2963+
#[help("if conforming to strict provenance is not possible, use `.expose_provenance()`")]
2964+
#[note("for more information, visit <https://doc.rust-lang.org/std/ptr/index.html#provenance>")]
2965+
pub(crate) struct ImplicitProvenanceCastsPtr2Int<'tcx> {
29682966
pub cast_from_ty: Ty<'tcx>,
29692967
pub cast_to_ty: Ty<'tcx>,
29702968
#[subdiagnostic]
2971-
pub sugg: Option<LossyProvenancePtr2IntSuggestion<'tcx>>,
2969+
pub sugg: Option<Ptr2IntSuggestion<'tcx>>,
29722970
}
29732971

29742972
#[derive(Subdiagnostic)]
2975-
pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> {
2973+
pub(crate) enum Ptr2IntSuggestion<'tcx> {
29762974
#[multipart_suggestion(
29772975
"use `.addr()` to obtain the address of a pointer",
29782976
applicability = "maybe-incorrect"

0 commit comments

Comments
 (0)