From 603d40018397b042e489f6ae2dedc5a949be4d8d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 6 Dec 2019 19:45:33 +0100 Subject: [PATCH] new lint: mutable_key_type --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/mut_key.rs | 108 ++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 9 ++- 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/mut_key.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a9448a57f7fd..8c774f209a07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1107,6 +1107,7 @@ Released 2018-09-13 [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount diff --git a/README.md b/README.md index 97a7c97b49a2..6133fa4c3a57 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 339 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 340 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 d14f946c8eb8..ac551089340f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -240,6 +240,7 @@ pub mod missing_doc; pub mod missing_inline; pub mod mul_add; pub mod multiple_crate_versions; +pub mod mut_key; pub mod mut_mut; pub mod mut_reference; pub mod mutable_debug_assertion; @@ -662,6 +663,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, &mul_add::MANUAL_MUL_ADD, &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, + &mut_key::MUTABLE_KEY_TYPE, &mut_mut::MUT_MUT, &mut_reference::UNNECESSARY_MUT_PASSED, &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, @@ -936,6 +938,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_late_pass(|| box trait_bounds::TraitBounds); store.register_late_pass(|| box comparison_chain::ComparisonChain); store.register_late_pass(|| box mul_add::MulAddCheck); + store.register_late_pass(|| box mut_key::MutableKeyType); store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); @@ -1217,6 +1220,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(&mutex_atomic::MUTEX_ATOMIC), @@ -1528,6 +1532,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&misc::CMP_NAN), LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), + LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs new file mode 100644 index 000000000000..3161469c1cc3 --- /dev/null +++ b/clippy_lints/src/mut_key.rs @@ -0,0 +1,108 @@ +use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty}; +use rustc::declare_lint_pass; +use rustc::hir; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use rustc::ty::{Adt, Ty}; +use rustc_session::declare_tool_lint; +use syntax::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for sets/maps with mutable key types. + /// + /// **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and + /// `BtreeSet` rely on either the hash or the order of keys be unchanging, + /// so having types with interior mutability is a bad idea. + /// + /// **Known problems:** We don't currently account for `Rc` or `Arc`, so + /// this may yield false positives. + /// + /// **Example:** + /// ```rust + /// use std::cmp::{PartialEq, Eq}; + /// use std::collections::HashSet; + /// use std::hash::{Hash, Hasher}; + /// use std::sync::atomic::AtomicUsize; + ///# #[allow(unused)] + /// + /// struct Bad(AtomicUsize); + /// impl PartialEq for Bad { + /// fn eq(&self, rhs: &Self) -> bool { + /// .. + /// ; unimplemented!(); + /// } + /// } + /// + /// impl Eq for Bad {} + /// + /// impl Hash for Bad { + /// fn hash(&self, h: &mut H) { + /// .. + /// ; unimplemented!(); + /// } + /// } + /// + /// fn main() { + /// let _: HashSet = HashSet::new(); + /// } + /// ``` + pub MUTABLE_KEY_TYPE, + correctness, + "Check for mutable Map/Set key type" +} + +declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) { + if let hir::ItemKind::Fn(ref sig, ..) = item.kind { + check_sig(cx, item.hir_id, &sig.decl); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) { + if let hir::ImplItemKind::Method(ref sig, ..) = item.kind { + if trait_ref_of_method(cx, item.hir_id).is_none() { + check_sig(cx, item.hir_id, &sig.decl); + } + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) { + if let hir::TraitItemKind::Method(ref sig, ..) = item.kind { + check_sig(cx, item.hir_id, &sig.decl); + } + } + + fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &hir::Local) { + check_ty(cx, local.span, cx.tables.pat_ty(&*local.pat)); + } +} + +fn check_sig<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl) { + let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); + let fn_sig = cx.tcx.fn_sig(fn_def_id); + check_ty( + cx, + decl.output.span(), + cx.tcx.erase_late_bound_regions(&fn_sig.output()), + ); + for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) { + check_ty(cx, hir_ty.span, ty); + } +} + +// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased +// generics (because the compiler cannot ensure immutability for unknown types). +fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) { + let ty = walk_ptrs_ty(ty); + if let Adt(def, substs) = ty.kind { + if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] + .iter() + .any(|path| match_def_path(cx, def.did, &**path)) + && !substs.type_at(0).is_freeze(cx.tcx, cx.param_env, span) + && substs.non_erasable_generics().next().is_none() + { + span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1f1c24b2c303..156f51119e2f 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; 339] = [ +pub const ALL_LINTS: [Lint; 340] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -1232,6 +1232,13 @@ pub const ALL_LINTS: [Lint; 339] = [ deprecation: None, module: "loops", }, + Lint { + name: "mutable_key_type", + group: "correctness", + desc: "Check for mutable Map/Set key type", + deprecation: None, + module: "mut_key", + }, Lint { name: "mutex_atomic", group: "perf",