diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d3108e0be4f..39ca3be04468a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1273,4 +1273,5 @@ Released 2018-09-13 [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/README.md b/README.md index 922dbcd113807..c5106e074cbc5 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 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 334 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 c22693aee3b50..a2dcbb6a95c57 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -625,6 +625,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, &methods::WRONG_SELF_CONVENTION, + &methods::ZST_OFFSET, &minmax::MIN_MAX, &misc::CMP_NAN, &misc::CMP_OWNED, @@ -1177,6 +1178,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), + LintId::of(&methods::ZST_OFFSET), LintId::of(&minmax::MIN_MAX), LintId::of(&misc::CMP_NAN), LintId::of(&misc::CMP_OWNED), @@ -1498,6 +1500,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), LintId::of(&methods::UNINIT_ASSUMED_INIT), + LintId::of(&methods::ZST_OFFSET), LintId::of(&minmax::MIN_MAX), LintId::of(&misc::CMP_NAN), LintId::of(&misc::FLOAT_CMP), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9fcffc770050e..f20abeff06582 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1065,6 +1065,23 @@ declare_clippy_lint! { "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" } +declare_clippy_lint! { + /// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to + /// zero-sized types + /// + /// **Why is this bad?** This is a no-op, and likely unintended + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```ignore + /// unsafe { (&() as *const ()).offest(1) }; + /// ``` + pub ZST_OFFSET, + correctness, + "Check for offset calculations on raw pointers to zero-sized types" +} + declare_lint_pass!(Methods => [ OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, @@ -1109,6 +1126,7 @@ declare_lint_pass!(Methods => [ SUSPICIOUS_MAP, UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, + ZST_OFFSET, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { @@ -1167,6 +1185,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { | ["unwrap_or", arith @ "checked_mul"] => { manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) }, + ["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => { + check_pointer_offset(cx, expr, arg_lists[0]) + }, _ => {}, } @@ -3063,3 +3084,15 @@ fn contains_return(expr: &hir::Expr) -> bool { visitor.visit_expr(expr); visitor.found } + +fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) { + if_chain! { + if args.len() == 2; + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind; + if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); + if layout.is_zst(); + then { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0b301b6be963a..8d046aa2b22e6 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; 333] = [ +pub const ALL_LINTS: [Lint; 334] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -2338,5 +2338,12 @@ pub const ALL_LINTS: [Lint; 333] = [ deprecation: None, module: "unicode", }, + Lint { + name: "zst_offset", + group: "correctness", + desc: "Check for offset calculations on raw pointers to zero-sized types", + deprecation: None, + module: "methods", + }, ]; // end lint list, do not remove this comment, it’s used in `update_lints` diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs new file mode 100644 index 0000000000000..2de904376ad45 --- /dev/null +++ b/tests/ui/zero_offset.rs @@ -0,0 +1,12 @@ +fn main() { + unsafe { + let x = &() as *const (); + x.offset(0); + x.wrapping_add(0); + x.sub(0); + x.wrapping_sub(0); + + let y = &1 as *const u8; + y.offset(0); + } +} diff --git a/tests/ui/zero_offset.stderr b/tests/ui/zero_offset.stderr new file mode 100644 index 0000000000000..cfcd7de2b3d2c --- /dev/null +++ b/tests/ui/zero_offset.stderr @@ -0,0 +1,9 @@ +error[E0606]: casting `&i32` as `*const u8` is invalid + --> $DIR/zero_offset.rs:9:17 + | +LL | let y = &1 as *const u8; + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0606`.