warn for more cases
This commit is contained in:
parent
da6fbb1895
commit
ca1e94b131
3 changed files with 204 additions and 16 deletions
|
@ -23,7 +23,7 @@
|
|||
|
||||
use rustc::hir::def::{Res, DefKind};
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx};
|
||||
use rustc::{lint, util};
|
||||
use hir::Node;
|
||||
use util::nodemap::HirIdSet;
|
||||
|
@ -1879,11 +1879,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
|
|||
|
||||
/// Return `false` only if we are sure this type does *not*
|
||||
/// allow zero initialization.
|
||||
fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool {
|
||||
fn ty_maybe_allows_zero_init<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
use rustc::ty::TyKind::*;
|
||||
match ty.sty {
|
||||
// Primitive types that don't like 0 as a value.
|
||||
Ref(..) | FnPtr(..) | Never => false,
|
||||
Adt(..) if ty.is_box() => false,
|
||||
// Recurse for some compound types.
|
||||
Adt(adt_def, substs) if !adt_def.is_union() => {
|
||||
match adt_def.variants.len() {
|
||||
0 => false, // Uninhabited enum!
|
||||
1 => {
|
||||
// Struct, or enum with exactly one variant.
|
||||
// Proceed recursively, check all fields.
|
||||
let variant = &adt_def.variants[VariantIdx::from_u32(0)];
|
||||
variant.fields.iter().all(|field| {
|
||||
ty_maybe_allows_zero_init(
|
||||
tcx,
|
||||
field.ty(tcx, substs),
|
||||
)
|
||||
})
|
||||
}
|
||||
_ => true, // Conservative fallback for multi-variant enum.
|
||||
}
|
||||
}
|
||||
Tuple(substs) => {
|
||||
// Proceed recursively, check all fields.
|
||||
substs.iter().all(|field| {
|
||||
ty_maybe_allows_zero_init(
|
||||
tcx,
|
||||
field.expect_ty(),
|
||||
)
|
||||
})
|
||||
}
|
||||
// FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
|
||||
// Conservative fallback.
|
||||
_ => true,
|
||||
}
|
||||
|
@ -1900,8 +1929,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
|
|||
// We are extremely conservative with what we warn about.
|
||||
let conjured_ty = cx.tables.expr_ty(expr);
|
||||
|
||||
if !ty_maybe_allows_zero_init(conjured_ty) {
|
||||
cx.span_lint(
|
||||
if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) {
|
||||
cx.struct_span_lint(
|
||||
INVALID_VALUE,
|
||||
expr.span,
|
||||
&format!(
|
||||
|
@ -1913,7 +1942,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
|
|||
"being left uninitialized"
|
||||
}
|
||||
),
|
||||
);
|
||||
)
|
||||
.note("this means that this code causes undefined behavior \
|
||||
when executed")
|
||||
.help("use `MaybeUninit` instead")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,52 @@
|
|||
#![allow(deprecated)]
|
||||
#![deny(invalid_value)]
|
||||
|
||||
use std::mem;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
|
||||
enum Void {}
|
||||
|
||||
struct Ref(&'static i32);
|
||||
|
||||
struct Wrap<T> { wrapped: T }
|
||||
|
||||
#[allow(unused)]
|
||||
fn generic<T: 'static>() {
|
||||
unsafe {
|
||||
let _val: &'static T = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: &'static T = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: Wrap<&'static T> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: Wrap<&'static T> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: (i32, !) = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: (i32, !) = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: Void = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: Void = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: Ref = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: Ref = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
|
||||
|
||||
let _val: Wrap<fn()> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
|
||||
let _val: Wrap<fn()> = 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<fn()> = mem::zeroed();
|
||||
let _val: MaybeUninit<&'static i32> = mem::zeroed();
|
||||
let _val: bool = mem::zeroed();
|
||||
let _val: i32 = mem::zeroed();
|
||||
}
|
||||
|
|
|
@ -1,44 +1,169 @@
|
|||
error: the type `!` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:15:23
|
||||
error: the type `&'static T` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:20:32
|
||||
|
|
||||
LL | let _val: ! = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
LL | let _val: &'static T = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/uninitialized-zeroed.rs:7:9
|
||||
|
|
||||
LL | #![deny(invalid_value)]
|
||||
| ^^^^^^^^^^^^^
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `&'static T` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:21:32
|
||||
|
|
||||
LL | let _val: &'static T = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Wrap<&'static T>` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:23:38
|
||||
|
|
||||
LL | let _val: Wrap<&'static T> = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Wrap<&'static T>` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:24:38
|
||||
|
|
||||
LL | let _val: Wrap<&'static T> = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `!` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:30:23
|
||||
|
|
||||
LL | let _val: ! = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `!` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:16:23
|
||||
--> $DIR/uninitialized-zeroed.rs:31:23
|
||||
|
|
||||
LL | let _val: ! = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `(i32, !)` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:33:30
|
||||
|
|
||||
LL | let _val: (i32, !) = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `(i32, !)` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:34:30
|
||||
|
|
||||
LL | let _val: (i32, !) = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Void` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:36:26
|
||||
|
|
||||
LL | let _val: Void = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Void` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:37:26
|
||||
|
|
||||
LL | let _val: Void = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `&'static i32` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:21:34
|
||||
--> $DIR/uninitialized-zeroed.rs:39:34
|
||||
|
|
||||
LL | let _val: &'static i32 = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `&'static i32` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:22:34
|
||||
--> $DIR/uninitialized-zeroed.rs:40:34
|
||||
|
|
||||
LL | let _val: &'static i32 = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Ref` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:42:25
|
||||
|
|
||||
LL | let _val: Ref = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Ref` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:43:25
|
||||
|
|
||||
LL | let _val: Ref = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `fn()` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:24:26
|
||||
--> $DIR/uninitialized-zeroed.rs:45:26
|
||||
|
|
||||
LL | let _val: fn() = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `fn()` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:25:26
|
||||
--> $DIR/uninitialized-zeroed.rs:46:26
|
||||
|
|
||||
LL | let _val: fn() = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: the type `Wrap<fn()>` does not permit zero-initialization
|
||||
--> $DIR/uninitialized-zeroed.rs:48:32
|
||||
|
|
||||
LL | let _val: Wrap<fn()> = mem::zeroed();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: the type `Wrap<fn()>` does not permit being left uninitialized
|
||||
--> $DIR/uninitialized-zeroed.rs:49:32
|
||||
|
|
||||
LL | let _val: Wrap<fn()> = mem::uninitialized();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this means that this code causes undefined behavior when executed
|
||||
= help: use `MaybeUninit` instead
|
||||
|
||||
error: aborting due to 18 previous errors
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue