Skip to main content

rustc_lint/
implicit_provenance_casts.rs

1use rustc_ast::util::parser::ExprPrecedence;
2use rustc_hir as hir;
3use rustc_middle::ty::Ty;
4use rustc_session::{declare_lint, declare_lint_pass};
5
6use crate::lints::{
7    ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion,
8    Ptr2IntSuggestion,
9};
10use crate::{LateContext, LateLintPass};
11
12#[doc =
r" The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer"]
#[doc = r" casts."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" #![feature(strict_provenance_lints)]"]
#[doc = r" #![warn(implicit_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"     let _ptr = addr as *const u8;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where"]
#[doc =
r" possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit."]
#[doc =
r" For more information on pointer provenance, see the [`std::ptr` documentation][provenance]."]
#[doc = r""]
#[doc =
r" Earlier versions of Rust did not have a clear answer how integer-to-pointer and"]
#[doc =
r" pointer-to-integer casts interact with provenance. Such casts are now defined to use the"]
#[doc =
r" exposed provenance model, but in many cases the code can be updated to strict provenance"]
#[doc =
r" APIs, which is preferable as it enables more precise reasoning about unsafe code, both by"]
#[doc = r" humans and by tools like [Miri]."]
#[doc = r""]
#[doc =
r" However, there are situations where exposed provenance is required or following the strict"]
#[doc =
r" provenance model requires major refactorings. In those cases, it's still useful to replace"]
#[doc =
r" the `as` casts with equivalent explicit use of exposed provenance APIs and a comment"]
#[doc = r" explaining why they are needed."]
#[doc = r""]
#[doc =
r" [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance"]
#[doc =
r" [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance"]
#[doc =
r" [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance"]
#[doc = r" [Miri]: https://github.com/rust-lang/miri"]
pub static IMPLICIT_PROVENANCE_CASTS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "IMPLICIT_PROVENANCE_CASTS",
            default_level: ::rustc_lint_defs::Allow,
            desc: "an `as` cast relying on exposed provenance 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! {
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#[doc = r" Lint for int2ptr and ptr2int `as` casts."]
pub struct ImplicitProvenanceCasts;
#[automatically_derived]
impl ::core::marker::Copy for ImplicitProvenanceCasts { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ImplicitProvenanceCasts { }
#[automatically_derived]
impl ::core::clone::Clone for ImplicitProvenanceCasts {
    #[inline]
    fn clone(&self) -> ImplicitProvenanceCasts { *self }
}
impl ::rustc_lint_defs::LintPass for ImplicitProvenanceCasts {
    fn name(&self) -> &'static str { "ImplicitProvenanceCasts" }
    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(),
                [IMPLICIT_PROVENANCE_CASTS]))
    }
}
impl ImplicitProvenanceCasts {
    #[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(),
                [IMPLICIT_PROVENANCE_CASTS]))
    }
}declare_lint_pass!(
59    /// Lint for int2ptr and ptr2int `as` casts.
60    ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS]
61);
62
63impl<'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
83fn 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
108fn 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}