diff --git a/Cargo.toml b/Cargo.toml index 792326d6c1d..db5944ab9b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,5 +29,4 @@ rustc-serialize = "0.3" [features] -structured_logging = [] debugging = [] diff --git a/README.md b/README.md index 13105087706..631c1d7f1d2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ name [explicit_iter_loop](https://github.com/Manishearth/rust-clippy/wiki#explicit_iter_loop) | warn | for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do [filter_next](https://github.com/Manishearth/rust-clippy/wiki#filter_next) | warn | using `filter(p).next()`, which is more succinctly expressed as `.find(p)` [float_cmp](https://github.com/Manishearth/rust-clippy/wiki#float_cmp) | warn | using `==` or `!=` on float values (as floating-point operations usually involve rounding errors, it is always better to check for approximate equality within small bounds) -[hashmap_entry](https://github.com/Manishearth/rust-clippy/wiki#hashmap_entry) | warn | use of `contains_key` followed by `insert` on a `HashMap` [identity_op](https://github.com/Manishearth/rust-clippy/wiki#identity_op) | warn | using identity operations, e.g. `x + 0` or `y / 1` [ineffective_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#ineffective_bit_mask) | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` [inline_always](https://github.com/Manishearth/rust-clippy/wiki#inline_always) | warn | `#[inline(always)]` is a bad idea in most cases @@ -43,6 +42,7 @@ name [let_unit_value](https://github.com/Manishearth/rust-clippy/wiki#let_unit_value) | warn | creating a let binding to a value of unit type, which usually can't be used afterwards [linkedlist](https://github.com/Manishearth/rust-clippy/wiki#linkedlist) | warn | usage of LinkedList, usually a vector is faster, or a more specialized data structure like a VecDeque [map_clone](https://github.com/Manishearth/rust-clippy/wiki#map_clone) | warn | using `.map(|x| x.clone())` to clone an iterator or option's contents (recommends `.cloned()` instead) +[map_entry](https://github.com/Manishearth/rust-clippy/wiki#map_entry) | warn | use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap` [match_bool](https://github.com/Manishearth/rust-clippy/wiki#match_bool) | warn | a match on boolean expression; recommends `if..else` block instead [match_overlapping_arm](https://github.com/Manishearth/rust-clippy/wiki#match_overlapping_arm) | warn | a match has overlapping arms [match_ref_pats](https://github.com/Manishearth/rust-clippy/wiki#match_ref_pats) | warn | a match or `if let` has all arms prefixed with `&`; the match expression can be dereferenced instead diff --git a/src/hashmap.rs b/src/entry.rs similarity index 52% rename from src/hashmap.rs rename to src/entry.rs index 095a00e3777..d9fb7269be6 100644 --- a/src/hashmap.rs +++ b/src/entry.rs @@ -1,12 +1,13 @@ use rustc::lint::*; use rustc_front::hir::*; use syntax::codemap::Span; -use utils::{get_item_name, is_exp_equal, match_type, snippet, span_help_and_lint, walk_ptrs_ty}; -use utils::HASHMAP_PATH; +use utils::{get_item_name, is_exp_equal, match_type, snippet, span_lint_and_then, walk_ptrs_ty}; +use utils::{BTREEMAP_PATH, HASHMAP_PATH}; -/// **What it does:** This lint checks for uses of `contains_key` + `insert` on `HashMap`. +/// **What it does:** This lint checks for uses of `contains_key` + `insert` on `HashMap` or +/// `BTreeMap`. /// -/// **Why is this bad?** Using `HashMap::entry` is more efficient. +/// **Why is this bad?** Using `entry` is more efficient. /// /// **Known problems:** Some false negatives, eg.: /// ``` @@ -23,9 +24,9 @@ use utils::HASHMAP_PATH; /// m.entry(k).or_insert(v); /// ``` declare_lint! { - pub HASHMAP_ENTRY, + pub MAP_ENTRY, Warn, - "use of `contains_key` followed by `insert` on a `HashMap`" + "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`" } #[derive(Copy,Clone)] @@ -33,7 +34,7 @@ pub struct HashMapLint; impl LintPass for HashMapLint { fn get_lints(&self) -> LintArray { - lint_array!(HASHMAP_ENTRY) + lint_array!(MAP_ENTRY) } } @@ -55,17 +56,25 @@ impl LateLintPass for HashMapLint { let map = ¶ms[0]; let obj_ty = walk_ptrs_ty(cx.tcx.expr_ty(map)); - if match_type(cx, obj_ty, &HASHMAP_PATH) { - let sole_expr = if then.expr.is_some() { 1 } else { 0 } + then.stmts.len() == 1; + let kind = if match_type(cx, obj_ty, &BTREEMAP_PATH) { + "BTreeMap" + } + else if match_type(cx, obj_ty, &HASHMAP_PATH) { + "HashMap" + } + else { + return + }; - if let Some(ref then) = then.expr { - check_for_insert(cx, expr.span, map, key, then, sole_expr); - } + let sole_expr = if then.expr.is_some() { 1 } else { 0 } + then.stmts.len() == 1; - for stmt in &then.stmts { - if let StmtSemi(ref stmt, _) = stmt.node { - check_for_insert(cx, expr.span, map, key, stmt, sole_expr); - } + if let Some(ref then) = then.expr { + check_for_insert(cx, expr.span, map, key, then, sole_expr, kind); + } + + for stmt in &then.stmts { + if let StmtSemi(ref stmt, _) = stmt.node { + check_for_insert(cx, expr.span, map, key, stmt, sole_expr, kind); } } } @@ -73,7 +82,7 @@ impl LateLintPass for HashMapLint { } } -fn check_for_insert(cx: &LateContext, span: Span, map: &Expr, key: &Expr, expr: &Expr, sole_expr: bool) { +fn check_for_insert(cx: &LateContext, span: Span, map: &Expr, key: &Expr, expr: &Expr, sole_expr: bool, kind: &str) { if_let_chain! { [ let ExprMethodCall(ref name, _, ref params) = expr.node, @@ -82,21 +91,22 @@ fn check_for_insert(cx: &LateContext, span: Span, map: &Expr, key: &Expr, expr: get_item_name(cx, map) == get_item_name(cx, &*params[0]), is_exp_equal(cx, key, ¶ms[1]) ], { - if sole_expr { - span_help_and_lint(cx, HASHMAP_ENTRY, span, - "usage of `contains_key` followed by `insert` on `HashMap`", - &format!("Consider using `{}.entry({}).or_insert({})`", - snippet(cx, map.span, ".."), - snippet(cx, params[1].span, ".."), - snippet(cx, params[2].span, ".."))); + let help = if sole_expr { + format!("{}.entry({}).or_insert({})", + snippet(cx, map.span, ".."), + snippet(cx, params[1].span, ".."), + snippet(cx, params[2].span, "..")) } else { - span_help_and_lint(cx, HASHMAP_ENTRY, span, - "usage of `contains_key` followed by `insert` on `HashMap`", - &format!("Consider using `{}.entry({})`", - snippet(cx, map.span, ".."), - snippet(cx, params[1].span, ".."))); - } + format!("{}.entry({})", + snippet(cx, map.span, ".."), + snippet(cx, params[1].span, "..")) + }; + + span_lint_and_then(cx, MAP_ENTRY, span, + &format!("usage of `contains_key` followed by `insert` on `{}`", kind), |db| { + db.span_suggestion(span, "Consider using", help.clone()); + }); } } } diff --git a/src/lib.rs b/src/lib.rs index f8db15605ff..9bb59693795 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ pub mod temporary_assignment; pub mod transmute; pub mod cyclomatic_complexity; pub mod escape; -pub mod hashmap; +pub mod entry; pub mod misc_early; pub mod array_indexing; pub mod panic; @@ -113,7 +113,7 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box types::UnitCmp); reg.register_late_lint_pass(box loops::LoopsPass); reg.register_late_lint_pass(box lifetimes::LifetimePass); - reg.register_late_lint_pass(box hashmap::HashMapLint); + reg.register_late_lint_pass(box entry::HashMapLint); reg.register_late_lint_pass(box ranges::StepByZero); reg.register_late_lint_pass(box types::CastPass); reg.register_late_lint_pass(box types::TypeComplexityPass); @@ -167,10 +167,10 @@ pub fn plugin_registrar(reg: &mut Registry) { block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT, collapsible_if::COLLAPSIBLE_IF, cyclomatic_complexity::CYCLOMATIC_COMPLEXITY, + entry::MAP_ENTRY, eq_op::EQ_OP, escape::BOXED_LOCAL, eta_reduction::REDUNDANT_CLOSURE, - hashmap::HASHMAP_ENTRY, identity_op::IDENTITY_OP, len_zero::LEN_WITHOUT_IS_EMPTY, len_zero::LEN_ZERO, diff --git a/src/utils.rs b/src/utils.rs index 77e63fe4458..53f83d6921a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,17 +19,18 @@ use std::ops::{Deref, DerefMut}; pub type MethodArgs = HirVec>; // module DefPaths for certain structs/enums we check for +pub const BEGIN_UNWIND: [&'static str; 3] = ["std", "rt", "begin_unwind"]; +pub const BTREEMAP_PATH: [&'static str; 4] = ["collections", "btree", "map", "BTreeMap"]; +pub const CLONE_PATH: [&'static str; 2] = ["Clone", "clone"]; +pub const COW_PATH: [&'static str; 3] = ["collections", "borrow", "Cow"]; +pub const HASHMAP_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const LL_PATH: [&'static str; 3] = ["collections", "linked_list", "LinkedList"]; +pub const MUTEX_PATH: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"]; +pub const OPEN_OPTIONS_PATH: [&'static str; 3] = ["std", "fs", "OpenOptions"]; pub const OPTION_PATH: [&'static str; 3] = ["core", "option", "Option"]; pub const RESULT_PATH: [&'static str; 3] = ["core", "result", "Result"]; pub const STRING_PATH: [&'static str; 3] = ["collections", "string", "String"]; pub const VEC_PATH: [&'static str; 3] = ["collections", "vec", "Vec"]; -pub const LL_PATH: [&'static str; 3] = ["collections", "linked_list", "LinkedList"]; -pub const HASHMAP_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "HashMap"]; -pub const OPEN_OPTIONS_PATH: [&'static str; 3] = ["std", "fs", "OpenOptions"]; -pub const MUTEX_PATH: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"]; -pub const CLONE_PATH: [&'static str; 2] = ["Clone", "clone"]; -pub const BEGIN_UNWIND: [&'static str; 3] = ["std", "rt", "begin_unwind"]; -pub const COW_PATH: [&'static str; 3] = ["collections", "borrow", "Cow"]; /// Produce a nested chain of if-lets and ifs from the patterns: /// @@ -77,20 +78,21 @@ macro_rules! if_let_chain { }; } -/// Returns true if the two spans come from differing expansions (i.e. one is from a macro and one isn't) +/// Returns true if the two spans come from differing expansions (i.e. one is from a macro and one +/// isn't). pub fn differing_macro_contexts(sp1: Span, sp2: Span) -> bool { sp1.expn_id != sp2.expn_id } -/// returns true if this expn_info was expanded by any macro +/// Returns true if this `expn_info` was expanded by any macro. pub fn in_macro(cx: &T, span: Span) -> bool { cx.sess().codemap().with_expn_info(span.expn_id, |info| info.is_some()) } -/// returns true if the macro that expanded the crate was outside of -/// the current crate or was a compiler plugin +/// Returns true if the macro that expanded the crate was outside of the current crate or was a +/// compiler plugin. pub fn in_external_macro(cx: &T, span: Span) -> bool { - /// invokes in_macro with the expansion info of the given span - /// slightly heavy, try to use this after other checks have already happened + /// Invokes in_macro with the expansion info of the given span slightly heavy, try to use this + /// after other checks have already happened. fn in_macro_ext(cx: &T, opt_info: Option<&ExpnInfo>) -> bool { // no ExpnInfo = no macro opt_info.map_or(false, |info| { @@ -109,9 +111,12 @@ pub fn in_external_macro(cx: &T, span: Span) -> bool { cx.sess().codemap().with_expn_info(span.expn_id, |info| in_macro_ext(cx, info)) } -/// check if a DefId's path matches the given absolute type path -/// usage e.g. with -/// `match_def_path(cx, id, &["core", "option", "Option"])` +/// Check if a `DefId`'s path matches the given absolute type path usage. +/// +/// # Examples +/// ``` +/// match_def_path(cx, id, &["core", "option", "Option"]) +/// ``` pub fn match_def_path(cx: &LateContext, def_id: DefId, path: &[&str]) -> bool { cx.tcx.with_path(def_id, |iter| { iter.zip(path) @@ -119,7 +124,7 @@ pub fn match_def_path(cx: &LateContext, def_id: DefId, path: &[&str]) -> bool { }) } -/// check if type is struct or enum type with given def path +/// Check if type is struct or enum type with given def path. pub fn match_type(cx: &LateContext, ty: ty::Ty, path: &[&str]) -> bool { match ty.sty { ty::TyEnum(ref adt, _) | ty::TyStruct(ref adt, _) => match_def_path(cx, adt.did, path), @@ -127,7 +132,7 @@ pub fn match_type(cx: &LateContext, ty: ty::Ty, path: &[&str]) -> bool { } } -/// check if method call given in "expr" belongs to given trait +/// Check if the method call given in `expr` belongs to given trait. pub fn match_impl_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool { let method_call = ty::MethodCall::expr(expr.id); @@ -144,9 +149,10 @@ pub fn match_impl_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool { } } -/// check if method call given in "expr" belongs to given trait +/// Check if the method call given in `expr` belongs to given trait. pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool { let method_call = ty::MethodCall::expr(expr.id); + let trt_id = cx.tcx .tables .borrow() @@ -160,21 +166,31 @@ pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool } } -/// match a Path against a slice of segment string literals, e.g. -/// `match_path(path, &["std", "rt", "begin_unwind"])` +/// Match a `Path` against a slice of segment string literals. +/// +/// # Examples +/// ``` +/// match_path(path, &["std", "rt", "begin_unwind"]) +/// ``` pub fn match_path(path: &Path, segments: &[&str]) -> bool { path.segments.iter().rev().zip(segments.iter().rev()).all(|(a, b)| a.identifier.name.as_str() == *b) } -/// match a Path against a slice of segment string literals, e.g. -/// `match_path(path, &["std", "rt", "begin_unwind"])` +/// Match a `Path` against a slice of segment string literals, e.g. +/// +/// # Examples +/// ``` +/// match_path(path, &["std", "rt", "begin_unwind"]) +/// ``` pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { path.segments.iter().rev().zip(segments.iter().rev()).all(|(a, b)| a.identifier.name.as_str() == *b) } -/// match an Expr against a chain of methods, and return the matched Exprs. For example, if `expr` -/// represents the `.baz()` in `foo.bar().baz()`, `matched_method_chain(expr, &["bar", "baz"])` -/// will return a Vec containing the Exprs for `.bar()` and `.baz()` +/// Match an `Expr` against a chain of methods, and return the matched `Expr`s. +/// +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`, +/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` containing the `Expr`s for +/// `.bar()` and `.baz()` pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option> { let mut current = expr; let mut matched = Vec::with_capacity(methods.len()); @@ -196,7 +212,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option Option { let parent_id = cx.tcx.map.get_parent(expr.id); match cx.tcx.map.find(parent_id) { @@ -207,7 +223,7 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option { } } -/// checks if a `let` decl is from a for loop desugaring +/// Checks if a `let` decl is from a `for` loop desugaring. pub fn is_from_for_desugar(decl: &Decl) -> bool { if_let_chain! { [ @@ -221,31 +237,39 @@ pub fn is_from_for_desugar(decl: &Decl) -> bool { } -/// convert a span to a code snippet if available, otherwise use default, e.g. -/// `snippet(cx, expr.span, "..")` +/// Convert a span to a code snippet if available, otherwise use default. +/// +/// # Example +/// ``` +/// snippet(cx, expr.span, "..") +/// ``` pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or(Cow::Borrowed(default)) } -/// Converts a span to a code snippet. Returns None if not available. +/// Convert a span to a code snippet. Returns `None` if not available. pub fn snippet_opt(cx: &T, span: Span) -> Option { cx.sess().codemap().span_to_snippet(span).ok() } -/// convert a span (from a block) to a code snippet if available, otherwise use default, e.g. -/// `snippet(cx, expr.span, "..")` -/// This trims the code of indentation, except for the first line -/// Use it for blocks or block-like things which need to be printed as such +/// Convert a span (from a block) to a code snippet if available, otherwise use default. +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like +/// things which need to be printed as such. +/// +/// # Example +/// ``` +/// snippet(cx, expr.span, "..") +/// ``` pub fn snippet_block<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { let snip = snippet(cx, span, default); trim_multiline(snip, true) } -/// Like snippet_block, but add braces if the expr is not an ExprBlock -/// Also takes an Option which can be put inside the braces +/// Like `snippet_block`, but add braces if the expr is not an `ExprBlock`. +/// Also takes an `Option` which can be put inside the braces. pub fn expr_block<'a, T: LintContext>(cx: &T, expr: &Expr, option: Option, default: &'a str) -> Cow<'a, str> { let code = snippet_block(cx, expr.span, default); - let string = option.map_or("".to_owned(), |s| s); + let string = option.unwrap_or_default(); if let ExprBlock(_) = expr.node { Cow::Owned(format!("{}{}", code, string)) } else if string.is_empty() { @@ -255,8 +279,7 @@ pub fn expr_block<'a, T: LintContext>(cx: &T, expr: &Expr, option: Option, ignore_first: bool) -> Cow { let s_space = trim_multiline_inner(s, ignore_first, ' '); let s_tab = trim_multiline_inner(s_space, ignore_first, '\t'); @@ -296,7 +319,7 @@ fn trim_multiline_inner(s: Cow, ignore_first: bool, ch: char) -> Cow { } } -/// get a parent expr if any – this is useful to constrain a lint +/// Get a parent expressions if any – this is useful to constrain a lint. pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> { let map = &cx.tcx.map; let node_id: NodeId = e.id; @@ -349,7 +372,6 @@ impl<'a> Deref for DiagnosticWrapper<'a> { } } -#[cfg(not(feature="structured_logging"))] pub fn span_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str) -> DiagnosticWrapper<'a> { let mut db = cx.struct_span_lint(lint, sp, msg); if cx.current_level(lint) != Level::Allow { @@ -360,20 +382,6 @@ pub fn span_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, sp: Span, m DiagnosticWrapper(db) } -#[cfg(feature="structured_logging")] -pub fn span_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str) -> DiagnosticWrapper<'a> { - // lint.name / lint.desc is can give details of the lint - // cx.sess().codemap() has all these nice functions for line/column/snippet details - // http://doc.rust-lang.org/syntax/codemap/struct.CodeMap.html#method.span_to_string - let mut db = cx.struct_span_lint(lint, sp, msg); - if cx.current_level(lint) != Level::Allow { - db.fileline_help(sp, - &format!("for further information visit https://github.com/Manishearth/rust-clippy/wiki#{}", - lint.name_lower())); - } - DiagnosticWrapper(db) -} - pub fn span_help_and_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, span: Span, msg: &str, help: &str) -> DiagnosticWrapper<'a> { let mut db = cx.struct_span_lint(lint, span, msg); @@ -418,7 +426,7 @@ pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, db } -/// return the base type for references and raw pointers +/// Return the base type for references and raw pointers. pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty { match ty.sty { ty::TyRef(_, ref tm) | ty::TyRawPtr(ref tm) => walk_ptrs_ty(tm.ty), @@ -426,7 +434,7 @@ pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty { } } -/// return the base type for references and raw pointers, and count reference depth +/// Return the base type for references and raw pointers, and count reference depth. pub fn walk_ptrs_ty_depth(ty: ty::Ty) -> (ty::Ty, usize) { fn inner(ty: ty::Ty, depth: usize) -> (ty::Ty, usize) { match ty.sty { diff --git a/tests/compile-fail/entry.rs b/tests/compile-fail/entry.rs new file mode 100644 index 00000000000..a7460282007 --- /dev/null +++ b/tests/compile-fail/entry.rs @@ -0,0 +1,52 @@ +#![feature(plugin)] +#![plugin(clippy)] +#![allow(unused)] + +#![deny(map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { m.insert(k, v); } + //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap` + //~| HELP Consider + //~| SUGGESTION m.entry(k).or_insert(v) +} + +fn insert_if_absent1(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { foo(); m.insert(k, v); } + //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap` + //~| HELP Consider + //~| SUGGESTION m.entry(k) +} + +fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { m.insert(k, v) } else { None }; + //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap` + //~| HELP Consider + //~| SUGGESTION m.entry(k).or_insert(v) +} + +fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None }; + //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap` + //~| HELP Consider + //~| SUGGESTION m.entry(k) +} + +fn insert_in_btreemap(m: &mut BTreeMap, k: K, v: V) { + if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None }; + //~^ ERROR usage of `contains_key` followed by `insert` on `BTreeMap` + //~| HELP Consider + //~| SUGGESTION m.entry(k) +} + +fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { + if !m.contains_key(&k) { m.insert(o, v); } +} + +fn main() { +} diff --git a/tests/compile-fail/hashmap.rs b/tests/compile-fail/hashmap.rs deleted file mode 100644 index a53566a794e..00000000000 --- a/tests/compile-fail/hashmap.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![feature(plugin)] -#![plugin(clippy)] -#![allow(unused)] - -#![deny(hashmap_entry)] - -use std::collections::HashMap; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { m.insert(k, v); } - //~^ERROR: usage of `contains_key` followed by `insert` on `HashMap` - //~^^HELP: Consider using `m.entry(k).or_insert(v)` -} - -fn insert_if_absent1(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { foo(); m.insert(k, v); } - //~^ERROR: usage of `contains_key` followed by `insert` on `HashMap` - //~^^HELP: Consider using `m.entry(k)` -} - -fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { m.insert(k, v) } else { None }; - //~^ERROR: usage of `contains_key` followed by `insert` on `HashMap` - //~^^HELP: Consider using `m.entry(k).or_insert(v)` -} - -fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None }; - //~^ERROR: usage of `contains_key` followed by `insert` on `HashMap` - //~^^HELP: Consider using `m.entry(k)` -} - -fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { - if !m.contains_key(&k) { m.insert(o, v); } -} - -fn main() { -}