
To accurately reflect that RPITIT assoc items don't have a name. This avoids the use of `kw::Empty` to mean "no name", which is error prone. Helps with #137978.
77 lines
3.1 KiB
Rust
77 lines
3.1 KiB
Rust
use rustc_middle::mir::visit::Visitor;
|
|
use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind};
|
|
use rustc_middle::ty::{AssocItem, AssocKind, TyCtxt};
|
|
use rustc_session::lint::builtin::PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS;
|
|
use rustc_span::sym;
|
|
|
|
use crate::errors;
|
|
|
|
/// Check for transmutes that exhibit undefined behavior.
|
|
/// For example, transmuting pointers to integers in a const context.
|
|
pub(super) struct CheckUndefinedTransmutes;
|
|
|
|
impl<'tcx> crate::MirLint<'tcx> for CheckUndefinedTransmutes {
|
|
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
|
let mut checker = UndefinedTransmutesChecker { body, tcx };
|
|
checker.visit_body(body);
|
|
}
|
|
}
|
|
|
|
struct UndefinedTransmutesChecker<'a, 'tcx> {
|
|
body: &'a Body<'tcx>,
|
|
tcx: TyCtxt<'tcx>,
|
|
}
|
|
|
|
impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> {
|
|
// This functions checks two things:
|
|
// 1. `function` takes a raw pointer as input and returns an integer as output.
|
|
// 2. `function` is called from a const function or an associated constant.
|
|
//
|
|
// Why do we consider const functions and associated constants only?
|
|
//
|
|
// Generally, undefined behavior in const items are handled by the evaluator.
|
|
// But, const functions and associated constants are evaluated only when referenced.
|
|
// This can result in undefined behavior in a library going unnoticed until
|
|
// the function or constant is actually used.
|
|
//
|
|
// Therefore, we only consider const functions and associated constants here and leave
|
|
// other const items to be handled by the evaluator.
|
|
fn is_ptr_to_int_in_const(&self, function: &Operand<'tcx>) -> bool {
|
|
let def_id = self.body.source.def_id();
|
|
|
|
if self.tcx.is_const_fn(def_id)
|
|
|| matches!(
|
|
self.tcx.opt_associated_item(def_id),
|
|
Some(AssocItem { kind: AssocKind::Const { .. }, .. })
|
|
)
|
|
{
|
|
let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
|
|
if let [input] = fn_sig.inputs() {
|
|
return input.is_raw_ptr() && fn_sig.output().is_integral();
|
|
}
|
|
}
|
|
false
|
|
}
|
|
}
|
|
|
|
impl<'tcx> Visitor<'tcx> for UndefinedTransmutesChecker<'_, 'tcx> {
|
|
// Check each block's terminator for calls to pointer to integer transmutes
|
|
// in const functions or associated constants and emit a lint.
|
|
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
|
if let TerminatorKind::Call { func, .. } = &terminator.kind
|
|
&& let Some((func_def_id, _)) = func.const_fn_def()
|
|
&& self.tcx.is_intrinsic(func_def_id, sym::transmute)
|
|
&& self.is_ptr_to_int_in_const(func)
|
|
&& let Some(call_id) = self.body.source.def_id().as_local()
|
|
{
|
|
let hir_id = self.tcx.local_def_id_to_hir_id(call_id);
|
|
let span = self.body.source_info(location).span;
|
|
self.tcx.emit_node_span_lint(
|
|
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
|
|
hir_id,
|
|
span,
|
|
errors::UndefinedTransmute,
|
|
);
|
|
}
|
|
}
|
|
}
|