invalid_value: factor finding dangerous inits into separate function

This commit is contained in:
Ralf Jung 2019-08-17 08:39:20 +02:00
parent bdfd698f37
commit 5f7716d11b

View File

@ -1876,8 +1876,34 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed]; #[derive(Debug)]
const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized]; enum InitKind { Zeroed, Uninit };
/// Determine if this expression is a "dangerous initialization".
fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option<InitKind> {
const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
if let hir::ExprKind::Path(ref qpath) = path_expr.node {
if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id)
.opt_def_id()
{
if cx.match_def_path(def_id, &ZEROED_PATH) {
return Some(InitKind::Zeroed);
}
if cx.match_def_path(def_id, &UININIT_PATH) {
return Some(InitKind::Uninit);
}
// FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and
// `MaybeUninit::uninit().assume_init()`.
// FIXME: Also detect `transmute` from 0.
}
}
}
None
}
/// Information about why a type cannot be initialized this way. /// Information about why a type cannot be initialized this way.
/// Contains an error message and optionally a span to point at. /// Contains an error message and optionally a span to point at.
@ -1933,42 +1959,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
} }
} }
if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node { if let Some(init) = is_dangerous_init(cx, expr) {
if let hir::ExprKind::Path(ref qpath) = path_expr.node { // This conjures an instance of a type out of nothing,
if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { // using zeroed or uninitialized memory.
if cx.match_def_path(def_id, &ZEROED_PATH) || // We are extremely conservative with what we warn about.
cx.match_def_path(def_id, &UININIT_PATH) let conjured_ty = cx.tables.expr_ty(expr);
{ if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) {
// This conjures an instance of a type out of nothing, let mut err = cx.struct_span_lint(
// using zeroed or uninitialized memory. INVALID_VALUE,
// We are extremely conservative with what we warn about. expr.span,
let conjured_ty = cx.tables.expr_ty(expr); &format!(
if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) { "the type `{}` does not permit {}",
let mut err = cx.struct_span_lint( conjured_ty,
INVALID_VALUE, match init {
expr.span, InitKind::Zeroed => "zero-initialization",
&format!( InitKind::Uninit => "being left uninitialized",
"the type `{}` does not permit {}", },
conjured_ty, ),
if cx.match_def_path(def_id, &ZEROED_PATH) { );
"zero-initialization" err.span_label(expr.span,
} else { "this code causes undefined behavior when executed");
"being left uninitialized" err.span_label(expr.span, "help: use `MaybeUninit<T>` instead");
} if let Some(span) = span {
), err.span_note(span, &msg);
); } else {
err.span_label(expr.span, err.note(&msg);
"this code causes undefined behavior when executed");
err.span_label(expr.span, "help: use `MaybeUninit<T>` instead");
if let Some(span) = span {
err.span_note(span, &msg);
} else {
err.note(&msg);
}
err.emit();
}
}
} }
err.emit();
} }
} }
} }