@@ -214,12 +214,31 @@ impl MapType {
214
214
}
215
215
}
216
216
217
+ /// Details on an expression checking whether a map contains a key.
218
+ ///
219
+ /// For instance, with the following:
220
+ /// ```ignore
221
+ /// !!!self.the_map.contains_key("the_key")
222
+ /// ```
223
+ ///
224
+ /// - `negated` will be set to `true` (the 3 `!` negate the condition)
225
+ /// - `map` will be the `self.the_map` expression
226
+ /// - `key` will be the `"the_key"` expression
217
227
struct ContainsExpr<'tcx> {
228
+ /// Whether the check for `contains_key` was negated.
218
229
negated: bool,
230
+ /// The map on which the check is performed.
219
231
map: &'tcx Expr<'tcx>,
232
+ /// The key that is checked to be contained.
220
233
key: &'tcx Expr<'tcx>,
234
+ /// The context of the whole condition expression.
221
235
call_ctxt: SyntaxContext,
222
236
}
237
+
238
+ /// Inspect the given expression and return details about the `contains_key` check.
239
+ ///
240
+ /// If the given expression is not a `contains_key` check against a `BTreeMap` or a `HashMap`,
241
+ /// return `None`.
223
242
fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
224
243
let mut negated = false;
225
244
let expr = peel_hir_expr_while(expr, |e| match e.kind {
@@ -229,6 +248,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
229
248
},
230
249
_ => None,
231
250
});
251
+
232
252
match expr.kind {
233
253
ExprKind::MethodCall(
234
254
_,
@@ -261,11 +281,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
261
281
}
262
282
}
263
283
284
+ /// Details on an expression inserting a key into a map.
285
+ ///
286
+ /// For instance, on the following:
287
+ /// ```ignore
288
+ /// self.the_map.insert("the_key", 3 + 4);
289
+ /// ```
290
+ ///
291
+ /// - `map` will be the `self.the_map` expression
292
+ /// - `key` will be the `"the_key"` expression
293
+ /// - `value` will be the `3 + 4` expression
264
294
struct InsertExpr<'tcx> {
295
+ /// The map into which the insertion is performed.
265
296
map: &'tcx Expr<'tcx>,
297
+ /// The key at which to insert.
266
298
key: &'tcx Expr<'tcx>,
299
+ /// The value to insert.
267
300
value: &'tcx Expr<'tcx>,
268
301
}
302
+
303
+ /// Inspect the given expression and return details about the `insert` call.
304
+ ///
305
+ /// If the given expression is not an `insert` call into a `BTreeMap` or a `HashMap`, return `None`.
269
306
fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
270
307
if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
271
308
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
@@ -298,7 +335,7 @@ struct Insertion<'tcx> {
298
335
value: &'tcx Expr<'tcx>,
299
336
}
300
337
301
- /// This visitor needs to do a multiple things:
338
+ /// This visitor needs to do multiple things:
302
339
/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
303
340
/// * Determine if there's an insertion using the same key. There's no need for the entry api
304
341
/// otherwise.
@@ -346,14 +383,27 @@ impl<'tcx> InsertSearcher<'_, 'tcx> {
346
383
res
347
384
}
348
385
349
- /// Visits an expression which is not itself in a tail position, but other sibling expressions
386
+ /// Visit an expression which is not itself in a tail position, but other sibling expressions
350
387
/// may be. e.g. if conditions
351
388
fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
352
389
let in_tail_pos = self.in_tail_pos;
353
390
self.in_tail_pos = false;
354
391
self.visit_expr(e);
355
392
self.in_tail_pos = in_tail_pos;
356
393
}
394
+
395
+ /// Visit the key and value expression of an insert expression.
396
+ /// There may not be uses of the map in either of those two either.
397
+ fn visit_insert_expr_arguments(&mut self, e: &InsertExpr<'tcx>) {
398
+ let in_tail_pos = self.in_tail_pos;
399
+ let allow_insert_closure = self.allow_insert_closure;
400
+ let is_single_insert = self.is_single_insert;
401
+ walk_expr(self, e.key);
402
+ walk_expr(self, e.value);
403
+ self.in_tail_pos = in_tail_pos;
404
+ self.allow_insert_closure = allow_insert_closure;
405
+ self.is_single_insert = is_single_insert;
406
+ }
357
407
}
358
408
impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
359
409
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
@@ -425,6 +475,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
425
475
426
476
match try_parse_insert(self.cx, expr) {
427
477
Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
478
+ self.visit_insert_expr_arguments(&insert_expr);
428
479
// Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
429
480
if self.is_map_used
430
481
|| !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
0 commit comments