@@ -214,12 +214,31 @@ impl MapType {
214214 }
215215}
216216
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
217227struct ContainsExpr < ' tcx > {
228+ /// Whether the check for `contains_key` was negated.
218229 negated : bool ,
230+ /// The map on which the check is performed.
219231 map : & ' tcx Expr < ' tcx > ,
232+ /// The key that is checked to be contained.
220233 key : & ' tcx Expr < ' tcx > ,
234+ /// The context of the whole condition expression.
221235 call_ctxt : SyntaxContext ,
222236}
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`.
223242fn try_parse_contains < ' tcx > ( cx : & LateContext < ' _ > , expr : & ' tcx Expr < ' _ > ) -> Option < ( MapType , ContainsExpr < ' tcx > ) > {
224243 let mut negated = false ;
225244 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
229248 } ,
230249 _ => None ,
231250 } ) ;
251+
232252 match expr. kind {
233253 ExprKind :: MethodCall (
234254 _,
@@ -261,11 +281,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
261281 }
262282}
263283
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
264294struct InsertExpr < ' tcx > {
295+ /// The map into which the insertion is performed.
265296 map : & ' tcx Expr < ' tcx > ,
297+ /// The key at which to insert.
266298 key : & ' tcx Expr < ' tcx > ,
299+ /// The value to insert.
267300 value : & ' tcx Expr < ' tcx > ,
268301}
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`.
269306fn try_parse_insert < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < InsertExpr < ' tcx > > {
270307 if let ExprKind :: MethodCall ( _, map, [ key, value] , _) = expr. kind {
271308 let id = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) ?;
@@ -298,7 +335,7 @@ struct Insertion<'tcx> {
298335 value : & ' tcx Expr < ' tcx > ,
299336}
300337
301- /// This visitor needs to do a multiple things:
338+ /// This visitor needs to do multiple things:
302339/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
303340/// * Determine if there's an insertion using the same key. There's no need for the entry api
304341/// otherwise.
@@ -346,14 +383,27 @@ impl<'tcx> InsertSearcher<'_, 'tcx> {
346383 res
347384 }
348385
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
350387 /// may be. e.g. if conditions
351388 fn visit_non_tail_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
352389 let in_tail_pos = self . in_tail_pos ;
353390 self . in_tail_pos = false ;
354391 self . visit_expr ( e) ;
355392 self . in_tail_pos = in_tail_pos;
356393 }
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+ }
357407}
358408impl < ' tcx > Visitor < ' tcx > for InsertSearcher < ' _ , ' tcx > {
359409 fn visit_stmt ( & mut self , stmt : & ' tcx Stmt < ' _ > ) {
@@ -425,6 +475,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
425475
426476 match try_parse_insert ( self . cx , expr) {
427477 Some ( insert_expr) if SpanlessEq :: new ( self . cx ) . eq_expr ( self . map , insert_expr. map ) => {
478+ self . visit_insert_expr_arguments ( & insert_expr) ;
428479 // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
429480 if self . is_map_used
430481 || !SpanlessEq :: new ( self . cx ) . eq_expr ( self . key , insert_expr. key )
0 commit comments