rustc_lint/
implicit_provenance_casts.rs1use 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 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 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}