Skip to content

Commit a9103a2

Browse files
committed
fix(map_entry): provide explicit type when suggesting None
1 parent 809f95e commit a9103a2

File tree

7 files changed

+88
-18
lines changed

7 files changed

+88
-18
lines changed

clippy_lints/src/entry.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::hir_id::HirIdSet;
1212
use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
1313
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
1414
use rustc_lint::{LateContext, LateLintPass};
15+
use rustc_middle::ty::IsSuggestable;
1516
use rustc_session::declare_lint_pass;
1617
use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym};
1718
use std::fmt;
@@ -25,17 +26,6 @@ declare_clippy_lint! {
2526
/// ### Why is this bad?
2627
/// Using `entry` is more efficient.
2728
///
28-
/// ### Known problems
29-
/// The suggestion may have type inference errors in some cases. e.g.
30-
/// ```no_run
31-
/// let mut map = std::collections::HashMap::new();
32-
/// let _ = if !map.contains_key(&0) {
33-
/// map.insert(0, 0)
34-
/// } else {
35-
/// None
36-
/// };
37-
/// ```
38-
///
3929
/// ### Example
4030
/// ```no_run
4131
/// # use std::collections::HashMap;
@@ -653,15 +643,24 @@ impl<'tcx> InsertSearchResults<'tcx> {
653643
(
654644
self.snippet(cx, span, app, |res, insertion, ctxt, app| {
655645
// Insertion into a map would return `None`, but the entry returns a mutable reference.
656-
let value_str = snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0;
646+
let value = insertion.value;
647+
let value_str = snippet_with_context(cx, value.span, ctxt, "..", app).0;
648+
649+
let value_ty = cx.typeck_results().expr_ty(value);
650+
debug_assert!(
651+
value_ty.is_suggestable(cx.tcx, true),
652+
"an unsuggestable type used as the element type:{value_ty:#?}\nexpr={value:#?}"
653+
);
654+
let none_str = format_args!("None::<{value_ty}>");
655+
657656
let _: fmt::Result = if is_expr_final_block_expr(cx.tcx, insertion.call) {
658657
write!(
659658
res,
660-
"e.insert({value_str});\n{indent}None",
659+
"e.insert({value_str});\n{indent}{none_str}",
661660
indent = snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
662661
)
663662
} else {
664-
write!(res, "{{ e.insert({value_str}); None }}")
663+
write!(res, "{{ e.insert({value_str}); {none_str} }}")
665664
};
666665
}),
667666
"Vacant(e)",

tests/ui/entry.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,11 @@ mod issue14449 {
248248
}
249249
}
250250

251+
fn issue15307<K: Eq + Hash, V>(mut m: HashMap<K, V>, k: K, v: V) {
252+
if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
253+
//~^ map_entry
254+
assert!({ e.insert(v); None::<V> }.is_none());
255+
}
256+
}
257+
251258
fn main() {}

tests/ui/entry.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,11 @@ mod issue14449 {
254254
}
255255
}
256256

257+
fn issue15307<K: Eq + Hash, V>(mut m: HashMap<K, V>, k: K, v: V) {
258+
if !m.contains_key(&k) {
259+
//~^ map_entry
260+
assert!(m.insert(k, v).is_none());
261+
}
262+
}
263+
257264
fn main() {}

tests/ui/entry.stderr

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,5 +246,22 @@ LL + e.insert(42);
246246
LL + }
247247
|
248248

249-
error: aborting due to 11 previous errors
249+
error: usage of `contains_key` followed by `insert` on a `HashMap`
250+
--> tests/ui/entry.rs:258:5
251+
|
252+
LL | / if !m.contains_key(&k) {
253+
LL | |
254+
LL | | assert!(m.insert(k, v).is_none());
255+
LL | | }
256+
| |_____^
257+
|
258+
help: try
259+
|
260+
LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
261+
LL +
262+
LL + assert!({ e.insert(v); None::<V> }.is_none());
263+
LL + }
264+
|
265+
266+
error: aborting due to 12 previous errors
250267

tests/ui/entry_with_else.fixed

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
6262
}
6363
std::collections::hash_map::Entry::Vacant(e) => {
6464
e.insert(v);
65-
None
65+
None::<V>
6666
}
6767
};
6868

@@ -73,6 +73,15 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
7373
} else {
7474
None
7575
};
76+
77+
// used to result in type ambiguity before #15759
78+
let _ = if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
79+
//~^ map_entry
80+
e.insert(v);
81+
None::<V>
82+
} else {
83+
None
84+
};
7685
}
7786

7887
fn main() {}

tests/ui/entry_with_else.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
6060
} else {
6161
None
6262
};
63+
64+
// used to result in type ambiguity before #15759
65+
let _ = if !m.contains_key(&k) {
66+
//~^ map_entry
67+
m.insert(k, v)
68+
} else {
69+
None
70+
};
6371
}
6472

6573
fn main() {}

tests/ui/entry_with_else.stderr

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ LL + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
134134
LL + }
135135
LL + std::collections::hash_map::Entry::Vacant(e) => {
136136
LL + e.insert(v);
137-
LL + None
137+
LL + None::<V>
138138
LL + }
139139
LL ~ };
140140
|
@@ -162,5 +162,28 @@ LL + None
162162
LL ~ };
163163
|
164164

165-
error: aborting due to 7 previous errors
165+
error: usage of `contains_key` followed by `insert` on a `HashMap`
166+
--> tests/ui/entry_with_else.rs:65:13
167+
|
168+
LL | let _ = if !m.contains_key(&k) {
169+
| _____________^
170+
LL | |
171+
LL | | m.insert(k, v)
172+
LL | | } else {
173+
LL | | None
174+
LL | | };
175+
| |_____^
176+
|
177+
help: try
178+
|
179+
LL ~ let _ = if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
180+
LL +
181+
LL + e.insert(v);
182+
LL + None::<V>
183+
LL + } else {
184+
LL + None
185+
LL ~ };
186+
|
187+
188+
error: aborting due to 8 previous errors
166189

0 commit comments

Comments
 (0)