diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c2be3010de..5a550e8d84f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1176,6 +1176,7 @@ Released 2018-09-13 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints diff --git a/README.md b/README.md index e5da763607bd..bd97910f5974 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 311 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 312 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0ae93c6147ab..4383b8b733a5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -804,6 +804,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con methods::STRING_EXTEND_CHARS, methods::SUSPICIOUS_MAP, methods::TEMPORARY_CSTRING_AS_PTR, + methods::UNINIT_ASSUMED_INIT, methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FOLD, methods::USELESS_ASREF, @@ -1116,6 +1117,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con methods::CLONE_DOUBLE_REF, methods::INTO_ITER_ON_ARRAY, methods::TEMPORARY_CSTRING_AS_PTR, + methods::UNINIT_ASSUMED_INIT, minmax::MIN_MAX, misc::CMP_NAN, misc::FLOAT_CMP, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 110d161471f0..989b8cabf414 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -951,6 +951,38 @@ declare_clippy_lint! { "suspicious usage of map" } +declare_clippy_lint! { + /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`. + /// + /// **Why is this bad?** For most types, this is undefined behavior. + /// + /// **Known problems:** For now, we accept empty tuples and tuples / arrays + /// of `MaybeUninit`. There may be other types that allow uninitialized + /// data, but those are not yet rigorously defined. + /// + /// **Example:** + /// + /// ```rust + /// // Beware the UB + /// use std::mem::MaybeUninit; + /// + /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + /// ``` + /// + /// Note that the following is OK: + /// + /// ```rust + /// use std::mem::MaybeUninit; + /// + /// let _: [MaybeUninit; 5] = unsafe { + /// MaybeUninit::uninit().assume_init() + /// }; + /// ``` + pub UNINIT_ASSUMED_INIT, + correctness, + "`MaybeUninit::uninit().assume_init()`" +} + declare_lint_pass!(Methods => [ OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, @@ -991,6 +1023,7 @@ declare_lint_pass!(Methods => [ INTO_ITER_ON_ARRAY, INTO_ITER_ON_REF, SUSPICIOUS_MAP, + UNINIT_ASSUMED_INIT, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { @@ -1038,6 +1071,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0]), ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]), ["count", "map"] => lint_suspicious_map(cx, expr), + ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr), _ => {}, } @@ -2662,6 +2696,37 @@ fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr, self_ref_ty: Ty<'_ } } +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) +fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr, outer: &hir::Expr) { + if_chain! { + if let hir::ExprKind::Call(ref callee, ref args) = expr.node; + if args.is_empty(); + if let hir::ExprKind::Path(ref path) = callee.node; + if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if !check_maybe_uninit_ty(cx, cx.tables.expr_ty_adjusted(outer)); + then { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + outer.span, + "this call for this type may be undefined behavior" + ); + } + } +} + +fn check_maybe_uninit_ty(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool { + match ty.sty { + ty::Array(ref component, _) => check_maybe_uninit_ty(cx, component), + ty::Tuple(ref types) => types.types().all(|ty| check_maybe_uninit_ty(cx, ty)), + ty::Adt(ref adt, _) => { + // needs to be a MaybeUninit + match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT) + }, + _ => false, + } +} + fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr) { span_help_and_lint( cx, diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 55e1387fe99e..9b88a0d3089b 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -49,6 +49,8 @@ pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"]; pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; +pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b7a6e486f0bf..8d2363238259 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 311] = [ +pub const ALL_LINTS: [Lint; 312] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -1890,6 +1890,13 @@ pub const ALL_LINTS: [Lint; 311] = [ deprecation: None, module: "panic_unimplemented", }, + Lint { + name: "uninit_assumed_init", + group: "correctness", + desc: "`MaybeUninit::uninit().assume_init()`", + deprecation: None, + module: "methods", + }, Lint { name: "unit_arg", group: "complexity", diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs new file mode 100644 index 000000000000..a4424c490e70 --- /dev/null +++ b/tests/ui/uninit.rs @@ -0,0 +1,23 @@ +#![feature(stmt_expr_attributes)] + +use std::mem::MaybeUninit; + +#[allow(clippy::let_unit_value)] +fn main() { + let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we lint on empty arrays + let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we accept unit tuples + let _: () = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because `MaybeUninit` allows uninitialized data. + let _: MaybeUninit = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit, MaybeUninit) = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; +} diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr new file mode 100644 index 000000000000..f4c45354aefe --- /dev/null +++ b/tests/ui/uninit.stderr @@ -0,0 +1,16 @@ +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:7:29 + | +LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::uninit_assumed_init)]` on by default + +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:10:31 + | +LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +