use clippy_utils::{
diagnostics::span_lint_and_sugg,
is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
- source::{snippet_indent, snippet_with_applicability, snippet_with_context},
+ source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
SpanlessEq,
};
use rustc_errors::Applicability;
let mut app = Applicability::MachineApplicable;
let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
- let sugg = if !contains_expr.negated || else_expr.is_some() || then_search.insertions.is_empty() {
+ let sugg = if let Some(else_expr) = else_expr {
+ // if .. { .. } else { .. }
+ let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
+ Some(search) if !(then_search.insertions.is_empty() && search.insertions.is_empty()) => search,
+ _ => return,
+ };
+
+ if then_search.insertions.is_empty() || else_search.insertions.is_empty() {
+ // if .. { insert } else { .. } or if .. { .. } else { then } of
+ let (then_str, else_str, entry_kind) = if else_search.insertions.is_empty() {
+ if contains_expr.negated {
+ (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ "Vacant(e)",
+ )
+ } else {
+ (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ "Occupied(mut e)",
+ )
+ }
+ } else if contains_expr.negated {
+ (
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ "Occupied(mut e)",
+ )
+ } else {
+ (
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ "Vacant(e)",
+ )
+ };
+ format!(
+ "if let {}::{} = {}.entry({}) {} else {}",
+ map_ty.entry_path(),
+ entry_kind,
+ map_str,
+ key_str,
+ then_str,
+ else_str,
+ )
+ } else {
+ // if .. { insert } else { insert }
+ let (then_str, else_str, then_entry, else_entry) = if contains_expr.negated {
+ (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ "Vacant(e)",
+ "Occupied(mut e)",
+ )
+ } else {
+ (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ "Occupied(mut e)",
+ "Vacant(e)",
+ )
+ };
+ let indent_str = snippet_indent(cx, expr.span);
+ let indent_str = indent_str.as_deref().unwrap_or("");
+ format!(
+ "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
+ {indent} {entry}::{} => {}\n{indent}}}",
+ map_str,
+ key_str,
+ then_entry,
+ reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
+ else_entry,
+ reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
+ entry = map_ty.entry_path(),
+ indent = indent_str,
+ )
+ }
+ } else if then_search.insertions.is_empty() || !contains_expr.negated {
return;
} else {
// if .. { insert }
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v2);
+ }
+ }
+
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ None
+ }
+ };
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ foo();
+ Some(e.insert(v))
+ } else {
+ None
+ };
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ foo();
+ }
+
+ if !m.contains_key(&k) {
+ foo();
+ } else {
+ m.insert(k, v);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ if true { m.insert(k, v) } else { m.insert(k, v2) }
+ } else {
+ m.insert(k, v)
+ };
+
+ if m.contains_key(&k) {
+ foo();
+ m.insert(k, v)
+ } else {
+ None
+ };
+}
+
+fn main() {}
--- /dev/null
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:16:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:22:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:28:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | foo();
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL | e.insert(v);
+LL | } else {
+LL | foo();
+LL | }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:34:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | } else {
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL | e.insert(v);
+LL | } else {
+LL | foo();
+LL | }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:40:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+LL | }
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:46:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | if true { m.insert(k, v) } else { m.insert(k, v2) }
+LL | | } else {
+LL | | m.insert(k, v)
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL | match m.entry(k) {
+LL | std::collections::hash_map::Entry::Occupied(mut e) => {
+LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+LL | }
+LL | std::collections::hash_map::Entry::Vacant(e) => {
+LL | e.insert(v);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:52:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | foo();
+LL | | m.insert(k, v)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL | foo();
+LL | Some(e.insert(v))
+LL | } else {
+LL | None
+LL | };
+ |
+
+error: aborting due to 7 previous errors
+