+
+declare_lint! {
+ pub INVALID_VALUE,
+ Warn,
+ "an invalid value is being created (such as a NULL reference)"
+}
+
+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) {
+
+ const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
+ const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
+
+ /// Return `false` only if we are sure this type does *not*
+ /// allow zero initialization.
+ fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool {
+ use rustc::ty::TyKind::*;
+ match ty.sty {
+ // Primitive types that don't like 0 as a value.
+ Ref(..) | FnPtr(..) | Never => false,
+ // Conservative fallback.
+ _ => true,
+ }
+ }
+
+ 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) ||
+ cx.match_def_path(def_id, &UININIT_PATH)
+ {
+ // This conjures an instance of a type out of nothing,
+ // using zeroed or uninitialized memory.
+ // 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(
+ INVALID_VALUE,
+ expr.span,
+ &format!(
+ "the type `{}` does not permit {}",
+ conjured_ty,
+ if cx.match_def_path(def_id, &ZEROED_PATH) {
+ "zero-initialization"
+ } else {
+ "being left uninitialized"
+ }
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}