From 25d8a0a3514c3d8dd8362d0eb68272dc50168af3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2019 09:39:25 +0200 Subject: [PATCH] warn about uninit bools and chars --- src/librustc_lint/builtin.rs | 21 ++++++++++----- src/test/ui/lint/uninitialized-zeroed.rs | 3 +++ src/test/ui/lint/uninitialized-zeroed.stderr | 28 +++++++++++++++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index afe81b3123d..14bd9a381f0 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1876,7 +1876,7 @@ declare_lint_pass!(InvalidValue => [INVALID_VALUE]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) { - #[derive(Debug)] + #[derive(Debug, Copy, Clone, PartialEq)] enum InitKind { Zeroed, Uninit }; /// Determine if this expression is a "dangerous initialization". @@ -1911,7 +1911,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { /// Return `Some` only if we are sure this type does *not* /// allow zero initialization. - fn ty_find_init_error<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + fn ty_find_init_error<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + init: InitKind, + ) -> Option { use rustc::ty::TyKind::*; match ty.sty { // Primitive types that don't like 0 as a value. @@ -1919,6 +1923,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some((format!("`Box` must be non-null"), None)), FnPtr(..) => Some((format!("Function pointers must be non-null"), None)), Never => Some((format!("The never type (`!`) has no valid value"), None)), + // Primitive types with other constraints + Bool if init == InitKind::Uninit => + Some((format!("Booleans must be `true` or `false`"), None)), + Char if init == InitKind::Uninit => + Some((format!("Characters must be a valid unicode codepoint"), None)), // Recurse for some compound types. Adt(adt_def, substs) if !adt_def.is_union() => { match adt_def.variants.len() { @@ -1931,6 +1940,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { ty_find_init_error( tcx, field.ty(tcx, substs), + init, ).map(|(mut msg, span)| if span.is_none() { // Point to this field, should be helpful for figuring // out where the source of the error is. @@ -1949,11 +1959,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } Tuple(..) => { // Proceed recursively, check all fields. - ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field)) + ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) } // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`. - // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`, - // `char`, and any multivariant enum. + // FIXME: *Only for `mem::uninitialized`*, we could also warn for multivariant enum. // Conservative fallback. _ => None, } @@ -1964,7 +1973,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // using zeroed or uninitialized memory. // We are extremely conservative with what we warn about. let conjured_ty = cx.tables.expr_ty(expr); - if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty) { + if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { let mut err = cx.struct_span_lint( INVALID_VALUE, expr.span, diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index d816479bbbb..aafc8507af6 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -56,6 +56,9 @@ fn main() { let _val: Wrap<(RefPair, i32)> = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: Wrap = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + // Some types that should work just fine. let _val: Option<&'static i32> = mem::zeroed(); let _val: Option = mem::zeroed(); diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 1b15fc21525..d8a4bf0b049 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -285,5 +285,31 @@ note: References must be non-null (in this struct field) LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: the type `bool` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:59:26 + | +LL | let _val: bool = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: Booleans must be `true` or `false` + +error: the type `Wrap` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:60:32 + | +LL | let _val: Wrap = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | +note: Characters must be a valid unicode codepoint (in this struct field) + --> $DIR/uninitialized-zeroed.rs:16:18 + | +LL | struct Wrap { wrapped: T } + | ^^^^^^^^^^ + +error: aborting due to 24 previous errors