Skip to content

Commit c6feb9b

Browse files
committed
Handle returning a value in map_entry
Allow the usage of the map after any insertions in `map_entry`
1 parent 288e3c4 commit c6feb9b

File tree

10 files changed

+400
-174
lines changed

10 files changed

+400
-174
lines changed

clippy_lints/src/entry.rs

+209-112
Large diffs are not rendered by default.

clippy_utils/src/lib.rs

+34
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,40 @@ pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
13631363
)
13641364
}
13651365

1366+
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
1367+
let map = tcx.hir();
1368+
let mut child_id = expr.hir_id;
1369+
let mut iter = map.parent_iter(child_id);
1370+
loop {
1371+
match iter.next() {
1372+
None => break None,
1373+
Some((id, Node::Block(_))) => child_id = id,
1374+
Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1375+
Some((_, Node::Expr(expr))) => match expr.kind {
1376+
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1377+
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1378+
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1379+
_ => break Some(Node::Expr(expr)),
1380+
},
1381+
Some((_, node)) => break Some(node),
1382+
}
1383+
}
1384+
}
1385+
1386+
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1387+
!matches!(
1388+
get_expr_use_or_unification_node(tcx, expr),
1389+
None | Some(Node::Stmt(Stmt {
1390+
kind: StmtKind::Expr(_) | StmtKind::Semi(_),
1391+
..
1392+
}))
1393+
)
1394+
}
1395+
1396+
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1397+
matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
1398+
}
1399+
13661400
pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
13671401
cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
13681402
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {

clippy_utils/src/source.rs

+9
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
6666
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
6767
}
6868

69+
/// Gets a snippet of the indentation of the line of a span
70+
pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
71+
snippet_opt(cx, line_span(cx, span)).map(|mut s| {
72+
let len = s.len() - s.trim_start().len();
73+
s.truncate(len);
74+
s
75+
})
76+
}
77+
6978
// If the snippet is empty, it's an attribute that was inserted during macro
7079
// expansion and we want to ignore those, because they could come from external
7180
// sources that the user has no control over.

tests/ui/entry.fixed

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ macro_rules! m {
1212

1313
fn foo() {}
1414

15-
fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
15+
fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
1616
m.entry(k).or_insert(v);
1717

1818
m.entry(k).or_insert_with(|| {
@@ -97,4 +97,11 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
9797
m.entry(m!(k)).or_insert_with(|| m!(v));
9898
}
9999

100+
fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
101+
if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
102+
e.insert(v);
103+
foo();
104+
}
105+
}
106+
100107
fn main() {}

tests/ui/entry.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ macro_rules! m {
1212

1313
fn foo() {}
1414

15-
fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
15+
fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
1616
if !m.contains_key(&k) {
1717
m.insert(k, v);
1818
}
@@ -101,4 +101,11 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
101101
}
102102
}
103103

104+
fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
105+
if !m.contains_key(&k) {
106+
m.insert(k, v);
107+
foo();
108+
}
109+
}
110+
104111
fn main() {}

tests/ui/entry.stderr

+18-1
Original file line numberDiff line numberDiff line change
@@ -165,5 +165,22 @@ LL | | m.insert(m!(k), m!(v));
165165
LL | | }
166166
| |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
167167

168-
error: aborting due to 9 previous errors
168+
error: usage of `contains_key` followed by `insert` on a `BTreeMap`
169+
--> $DIR/entry.rs:105:5
170+
|
171+
LL | / if !m.contains_key(&k) {
172+
LL | | m.insert(k, v);
173+
LL | | foo();
174+
LL | | }
175+
| |_____^
176+
|
177+
help: try this
178+
|
179+
LL | if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
180+
LL | e.insert(v);
181+
LL | foo();
182+
LL | }
183+
|
184+
185+
error: aborting due to 10 previous errors
169186

tests/ui/entry_unfixable.rs

-43
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,6 @@ macro_rules! m {
1313

1414
fn foo() {}
1515

16-
fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
17-
if !m.contains_key(&k) {
18-
m.insert(k, v)
19-
} else {
20-
None
21-
};
22-
}
23-
24-
fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
25-
if m.contains_key(&k) {
26-
None
27-
} else {
28-
m.insert(k, v)
29-
};
30-
}
31-
32-
fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
33-
if !m.contains_key(&k) {
34-
foo();
35-
m.insert(k, v)
36-
} else {
37-
None
38-
};
39-
}
40-
41-
fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
42-
if m.contains_key(&k) {
43-
None
44-
} else {
45-
foo();
46-
m.insert(k, v)
47-
};
48-
}
49-
50-
fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
51-
if !m.contains_key(&k) {
52-
foo();
53-
m.insert(k, v)
54-
} else {
55-
None
56-
};
57-
}
58-
5916
// should not trigger
6017
fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
6118
if !m.contains_key(&k) {

tests/ui/entry_with_else.fixed

+26-6
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,43 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
3131
}
3232
}
3333

34+
if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
35+
e.insert(v);
36+
} else {
37+
foo();
38+
}
39+
40+
if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
41+
e.insert(v);
42+
} else {
43+
foo();
44+
}
45+
3446
match m.entry(k) {
3547
std::collections::hash_map::Entry::Vacant(e) => {
3648
e.insert(v);
3749
}
38-
std::collections::hash_map::Entry::Occupied(_) => {
39-
foo();
50+
std::collections::hash_map::Entry::Occupied(mut e) => {
51+
e.insert(v2);
4052
}
4153
}
4254

4355
match m.entry(k) {
44-
std::collections::hash_map::Entry::Vacant(_) => {
45-
foo();
46-
}
4756
std::collections::hash_map::Entry::Occupied(mut e) => {
57+
if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
58+
}
59+
std::collections::hash_map::Entry::Vacant(e) => {
4860
e.insert(v);
61+
None
4962
}
50-
}
63+
};
64+
65+
if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
66+
foo();
67+
Some(e.insert(v))
68+
} else {
69+
None
70+
};
5171
}
5272

5373
fn main() {}

tests/ui/entry_with_else.rs

+19
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,25 @@ fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K,
3636
} else {
3737
m.insert(k, v);
3838
}
39+
40+
if !m.contains_key(&k) {
41+
m.insert(k, v);
42+
} else {
43+
m.insert(k, v2);
44+
}
45+
46+
if m.contains_key(&k) {
47+
if true { m.insert(k, v) } else { m.insert(k, v2) }
48+
} else {
49+
m.insert(k, v)
50+
};
51+
52+
if m.contains_key(&k) {
53+
foo();
54+
m.insert(k, v)
55+
} else {
56+
None
57+
};
3958
}
4059

4160
fn main() {}

tests/ui/entry_with_else.stderr

+69-10
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,12 @@ LL | | }
5151
|
5252
help: try this
5353
|
54-
LL | match m.entry(k) {
55-
LL | std::collections::hash_map::Entry::Vacant(e) => {
56-
LL | e.insert(v);
57-
LL | }
58-
LL | std::collections::hash_map::Entry::Occupied(_) => {
59-
LL | foo();
60-
...
54+
LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
55+
LL | e.insert(v);
56+
LL | } else {
57+
LL | foo();
58+
LL | }
59+
|
6160

6261
error: usage of `contains_key` followed by `insert` on a `HashMap`
6362
--> $DIR/entry_with_else.rs:34:5
@@ -66,18 +65,78 @@ LL | / if !m.contains_key(&k) {
6665
LL | | foo();
6766
LL | | } else {
6867
LL | | m.insert(k, v);
68+
LL | | }
69+
| |_____^
70+
|
71+
help: try this
72+
|
73+
LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
74+
LL | e.insert(v);
75+
LL | } else {
76+
LL | foo();
77+
LL | }
78+
|
79+
80+
error: usage of `contains_key` followed by `insert` on a `HashMap`
81+
--> $DIR/entry_with_else.rs:40:5
82+
|
83+
LL | / if !m.contains_key(&k) {
84+
LL | | m.insert(k, v);
85+
LL | | } else {
86+
LL | | m.insert(k, v2);
6987
LL | | }
7088
| |_____^
7189
|
7290
help: try this
7391
|
7492
LL | match m.entry(k) {
75-
LL | std::collections::hash_map::Entry::Vacant(_) => {
76-
LL | foo();
93+
LL | std::collections::hash_map::Entry::Vacant(e) => {
94+
LL | e.insert(v);
7795
LL | }
7896
LL | std::collections::hash_map::Entry::Occupied(mut e) => {
97+
LL | e.insert(v2);
98+
...
99+
100+
error: usage of `contains_key` followed by `insert` on a `HashMap`
101+
--> $DIR/entry_with_else.rs:46:5
102+
|
103+
LL | / if m.contains_key(&k) {
104+
LL | | if true { m.insert(k, v) } else { m.insert(k, v2) }
105+
LL | | } else {
106+
LL | | m.insert(k, v)
107+
LL | | };
108+
| |_____^
109+
|
110+
help: try this
111+
|
112+
LL | match m.entry(k) {
113+
LL | std::collections::hash_map::Entry::Occupied(mut e) => {
114+
LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
115+
LL | }
116+
LL | std::collections::hash_map::Entry::Vacant(e) => {
79117
LL | e.insert(v);
80118
...
81119

82-
error: aborting due to 4 previous errors
120+
error: usage of `contains_key` followed by `insert` on a `HashMap`
121+
--> $DIR/entry_with_else.rs:52:5
122+
|
123+
LL | / if m.contains_key(&k) {
124+
LL | | foo();
125+
LL | | m.insert(k, v)
126+
LL | | } else {
127+
LL | | None
128+
LL | | };
129+
| |_____^
130+
|
131+
help: try this
132+
|
133+
LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
134+
LL | foo();
135+
LL | Some(e.insert(v))
136+
LL | } else {
137+
LL | None
138+
LL | };
139+
|
140+
141+
error: aborting due to 7 previous errors
83142

0 commit comments

Comments
 (0)