1use rustc_ast::util::parser::ExprPrecedence;
2use rustc_hiras hir;
3use rustc_session::{declare_lint, declare_lint_pass};
45use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion};
6use crate::{LateContext, LateLintPass};
78#[doc =
r" The `lossy_provenance_casts` lint detects an `as` cast between a pointer"]
#[doc = r" and an integer."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" #![feature(strict_provenance_lints)]"]
#[doc = r" #![warn(lossy_provenance_casts)]"]
#[doc = r""]
#[doc = r" fn main() {"]
#[doc = r" let x: u8 = 37;"]
#[doc = r" let _addr: usize = &x as *const u8 as usize;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" This lint is part of the strict provenance effort, see [issue #95228]."]
#[doc =
r" Casting a pointer to an integer is a lossy operation, because beyond"]
#[doc = r" just an *address* a pointer may be associated with a particular"]
#[doc =
r" *provenance*. This information is used by the optimiser and for dynamic"]
#[doc =
r" analysis/dynamic program verification (e.g. Miri or CHERI platforms)."]
#[doc = r""]
#[doc = r" Since this cast is lossy, it is considered good style to use the"]
#[doc =
r" [`ptr::addr`] method instead, which has a similar effect, but doesn't"]
#[doc =
r#" "expose" the pointer provenance. This improves optimisation potential."#]
#[doc =
r" See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information"]
#[doc = r" about exposing pointer provenance."]
#[doc = r""]
#[doc =
r" If your code can't comply with strict provenance and needs to expose"]
#[doc =
r" the provenance, then there is [`ptr::expose_provenance`] as an escape hatch,"]
#[doc =
r" which preserves the behaviour of `as usize` casts while being explicit"]
#[doc = r" about the semantics."]
#[doc = r""]
#[doc = r" [issue #95228]: https://github.com/rust-lang/rust/issues/95228"]
#[doc =
r" [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr"]
#[doc =
r" [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance"]
pub static LOSSY_PROVENANCE_CASTS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "LOSSY_PROVENANCE_CASTS",
default_level: ::rustc_lint_defs::Allow,
desc: "a lossy pointer to integer cast is used",
is_externally_loaded: false,
feature_gate: Some(rustc_span::sym::strict_provenance_lints),
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
9/// The `lossy_provenance_casts` lint detects an `as` cast between a pointer
10 /// and an integer.
11 ///
12 /// ### Example
13 ///
14 /// ```rust
15 /// #![feature(strict_provenance_lints)]
16 /// #![warn(lossy_provenance_casts)]
17 ///
18 /// fn main() {
19 /// let x: u8 = 37;
20 /// let _addr: usize = &x as *const u8 as usize;
21 /// }
22 /// ```
23 ///
24 /// {{produces}}
25 ///
26 /// ### Explanation
27 ///
28 /// This lint is part of the strict provenance effort, see [issue #95228].
29 /// Casting a pointer to an integer is a lossy operation, because beyond
30 /// just an *address* a pointer may be associated with a particular
31 /// *provenance*. This information is used by the optimiser and for dynamic
32 /// analysis/dynamic program verification (e.g. Miri or CHERI platforms).
33 ///
34 /// Since this cast is lossy, it is considered good style to use the
35 /// [`ptr::addr`] method instead, which has a similar effect, but doesn't
36 /// "expose" the pointer provenance. This improves optimisation potential.
37 /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information
38 /// about exposing pointer provenance.
39 ///
40 /// If your code can't comply with strict provenance and needs to expose
41 /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch,
42 /// which preserves the behaviour of `as usize` casts while being explicit
43 /// about the semantics.
44 ///
45 /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
46 /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr
47 /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance
48pub LOSSY_PROVENANCE_CASTS,
49 Allow,
50"a lossy pointer to integer cast is used",
51 @feature_gate = strict_provenance_lints;
52}5354#[doc = r" Lint for `as` casts between a pointer and an integer."]
pub struct LossyProvenanceCasts;
#[automatically_derived]
impl ::core::marker::Copy for LossyProvenanceCasts { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LossyProvenanceCasts { }
#[automatically_derived]
impl ::core::clone::Clone for LossyProvenanceCasts {
#[inline]
fn clone(&self) -> LossyProvenanceCasts { *self }
}
impl ::rustc_lint_defs::LintPass for LossyProvenanceCasts {
fn name(&self) -> &'static str { "LossyProvenanceCasts" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[LOSSY_PROVENANCE_CASTS]))
}
}
impl LossyProvenanceCasts {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[LOSSY_PROVENANCE_CASTS]))
}
}declare_lint_pass!(
55/// Lint for `as` casts between a pointer and an integer.
56LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS]
57);
5859impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts {
60fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
61let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return };
6263let typeck_results = cx.typeck_results();
64// Only lint casts from pointer to integer
65let cast_from_ty = typeck_results.expr_ty(cast_from_expr);
66if !cast_from_ty.is_raw_ptr() {
67return;
68 }
69let cast_to_ty = typeck_results.expr_ty(expr);
70if !cast_to_ty.is_integral() {
71return;
72 }
7374let sugg = expr.span.can_be_used_for_suggestions().then(|| {
75let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous;
76let needs_cast = !cast_to_ty.is_usize();
77let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span);
78let expr_span = cast_from_expr.span.shrink_to_lo();
79match (needs_parens, needs_cast) {
80 (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast {
81expr_span,
82cast_span,
83cast_to_ty,
84 },
85 (true, false) => {
86 LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span }
87 }
88 (false, true) => {
89 LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty }
90 }
91 (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span },
92 }
93 });
9495let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg };
96cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint);
97 }
98}