diff --git a/CHANGELOG.md b/CHANGELOG.md index 768751b2f08..9ad6514ee10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -744,6 +744,7 @@ All notable changes to this project will be documented in this file. [`match_same_arms`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_same_arms [`match_wild_err_arm`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#match_wild_err_arm [`maybe_infinite_iter`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#maybe_infinite_iter +[`mem_discriminant_non_enum`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_forget`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_forget [`mem_replace_option_with_none`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mem_replace_option_with_none [`min_max`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#min_max diff --git a/README.md b/README.md index 94fb8c6c02f..a13d8ecef66 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 281 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html) +[There are 282 lints included in this crate!](https://rust-lang-nursery.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 8b2070561d2..7a91890633a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -144,6 +144,7 @@ pub mod loops; pub mod map_clone; pub mod map_unit_fn; pub mod matches; +pub mod mem_discriminant; pub mod mem_forget; pub mod mem_replace; pub mod methods; @@ -398,6 +399,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_early_lint_pass(box doc::Doc::new(conf.doc_valid_idents.clone())); reg.register_late_lint_pass(box neg_multiply::NegMultiply); reg.register_early_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval); + reg.register_late_lint_pass(box mem_discriminant::MemDiscriminant); reg.register_late_lint_pass(box mem_forget::MemForget); reg.register_late_lint_pass(box mem_replace::MemReplace); reg.register_late_lint_pass(box arithmetic::Arithmetic::default()); @@ -612,6 +614,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { matches::MATCH_REF_PATS, matches::MATCH_WILD_ERR_ARM, matches::SINGLE_MATCH, + mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, mem_replace::MEM_REPLACE_OPTION_WITH_NONE, methods::CHARS_LAST_CMP, methods::CHARS_NEXT_CMP, @@ -924,6 +927,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { loops::NEVER_LOOP, loops::REVERSE_RANGE_LOOP, loops::WHILE_IMMUTABLE_CONDITION, + mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, methods::CLONE_DOUBLE_REF, methods::TEMPORARY_CSTRING_AS_PTR, minmax::MIN_MAX, diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs new file mode 100644 index 00000000000..356162fd6f4 --- /dev/null +++ b/clippy_lints/src/mem_discriminant.rs @@ -0,0 +1,93 @@ +// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use crate::rustc::hir::{Expr, ExprKind}; +use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use crate::rustc::{declare_tool_lint, lint_array}; +use crate::utils::{match_def_path, opt_def_id, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth}; +use if_chain::if_chain; + +use std::iter; + +/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type. +/// +/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types +/// is unspecified. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// mem::discriminant(&"hello"); +/// mem::discriminant(&&Some(2)); +/// ``` +declare_clippy_lint! { + pub MEM_DISCRIMINANT_NON_ENUM, + correctness, + "calling mem::descriminant on non-enum type" +} + +pub struct MemDiscriminant; + +impl LintPass for MemDiscriminant { + fn get_lints(&self) -> LintArray { + lint_array![MEM_DISCRIMINANT_NON_ENUM] + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + if_chain! { + if let ExprKind::Call(ref func, ref func_args) = expr.node; + // is `mem::discriminant` + if let ExprKind::Path(ref func_qpath) = func.node; + if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id)); + if match_def_path(cx.tcx, def_id, &paths::MEM_DISCRIMINANT); + // type is non-enum + let ty_param = cx.tables.node_substs(func.hir_id).type_at(0); + if !ty_param.is_enum(); + + then { + span_lint_and_then( + cx, + MEM_DISCRIMINANT_NON_ENUM, + expr.span, + &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param), + |db| { + // if this is a reference to an enum, suggest dereferencing + let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param); + if ptr_depth >= 1 && base_ty.is_enum() { + let param = &func_args[0]; + + // cancel out '&'s first + let mut derefs_needed = ptr_depth; + let mut cur_expr = param; + while derefs_needed > 0 { + if let ExprKind::AddrOf(_, ref inner_expr) = cur_expr.node { + derefs_needed -= 1; + cur_expr = inner_expr; + } else { + break; + } + } + + let derefs: String = iter::repeat('*').take(derefs_needed).collect(); + db.span_suggestion( + param.span, + "try dereferencing", + format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")), + ); + } + }, + ) + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 12ef2f51d8c..474f16679a7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -55,6 +55,7 @@ pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"]; pub const LINT_ARRAY: [&str; 3] = ["rustc", "lint", "LintArray"]; +pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"]; diff --git a/tests/ui/mem_discriminant.rs b/tests/ui/mem_discriminant.rs new file mode 100644 index 00000000000..a7176fb7985 --- /dev/null +++ b/tests/ui/mem_discriminant.rs @@ -0,0 +1,44 @@ +// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +struct A(Foo); + +fn main() { + // bad + mem::discriminant(&"hello"); + mem::discriminant(&&Some(2)); + mem::discriminant(&&None::<u8>); + mem::discriminant(&&Foo::One(5)); + mem::discriminant(&&Foo::Two(5)); + mem::discriminant(&A(Foo::One(0))); + + let ro = &Some(3); + let rro = &ro; + mem::discriminant(&ro); + mem::discriminant(rro); + mem::discriminant(&rro); + + + // ok + mem::discriminant(&Some(2)); + mem::discriminant(&None::<u8>); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + mem::discriminant(ro); +} diff --git a/tests/ui/mem_discriminant.stderr b/tests/ui/mem_discriminant.stderr new file mode 100644 index 00000000000..5255458d8f8 --- /dev/null +++ b/tests/ui/mem_discriminant.stderr @@ -0,0 +1,76 @@ +error: calling `mem::discriminant` on non-enum type `&str` + --> $DIR/mem_discriminant.rs:24:5 + | +24 | mem::discriminant(&"hello"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/mem_discriminant.rs:11:9 + | +11 | #![deny(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:25:5 + | +25 | mem::discriminant(&&Some(2)); + | ^^^^^^^^^^^^^^^^^^---------^ + | | + | help: try dereferencing: `&Some(2)` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>` + --> $DIR/mem_discriminant.rs:26:5 + | +26 | mem::discriminant(&&None::<u8>); + | ^^^^^^^^^^^^^^^^^^------------^ + | | + | help: try dereferencing: `&None::<u8>` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:27:5 + | +27 | mem::discriminant(&&Foo::One(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::One(5)` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:28:5 + | +28 | mem::discriminant(&&Foo::Two(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::Two(5)` + +error: calling `mem::discriminant` on non-enum type `A` + --> $DIR/mem_discriminant.rs:29:5 + | +29 | mem::discriminant(&A(Foo::One(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:33:5 + | +33 | mem::discriminant(&ro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `ro` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:34:5 + | +34 | mem::discriminant(rro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `*rro` + +error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:35:5 + | +35 | mem::discriminant(&rro); + | ^^^^^^^^^^^^^^^^^^----^ + | | + | help: try dereferencing: `*rro` + +error: aborting due to 9 previous errors +