@@ -87,8 +87,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
87
87
88
88
let lint_msg = format ! ( "usage of `contains_key` followed by `insert` on a `{}`" , map_ty. name( ) ) ;
89
89
let mut app = Applicability :: MachineApplicable ;
90
- let map_str = snippet_with_context ( cx, contains_expr. map . span , contains_expr. call_ctxt , ".. " , & mut app) . 0 ;
91
- let key_str = snippet_with_context ( cx, contains_expr. key . span , contains_expr. call_ctxt , ".. " , & mut app) . 0 ;
90
+ let map_str = snippet_with_context ( cx, contains_expr. map . span , contains_expr. call_ctxt , "(_) " , & mut app) . 0 ;
91
+ let key_str = snippet_with_context ( cx, contains_expr. key . span , contains_expr. call_ctxt , "_ " , & mut app) . 0 ;
92
92
93
93
let sugg = if let Some ( else_expr) = else_expr {
94
94
let Some ( else_search) = find_insert_calls ( cx, & contains_expr, else_expr) else {
@@ -142,8 +142,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
142
142
let indent_str = snippet_indent ( cx, expr. span ) ;
143
143
let indent_str = indent_str. as_deref ( ) . unwrap_or ( "" ) ;
144
144
Some ( format ! (
145
- "match {map_str}.entry({key_str}) {{\n {indent_str} {entry}::{then_entry} => {}\n \
146
- {indent_str} {entry}::{else_entry} => {}\n {indent_str}}}",
145
+ "\
146
+ match {map_str}.entry({key_str}) {{
147
+ {indent_str} {entry}::{then_entry} => {}
148
+ {indent_str} {entry}::{else_entry} => {}
149
+ {indent_str}}}" ,
147
150
reindent_multiline( & then_str, true , Some ( 4 + indent_str. len( ) ) ) ,
148
151
reindent_multiline( & else_str, true , Some ( 4 + indent_str. len( ) ) ) ,
149
152
entry = map_ty. entry_path( ) ,
@@ -167,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
167
170
map_ty. entry_path( ) ,
168
171
) )
169
172
} else if let Some ( insertion) = then_search. as_single_insertion ( ) {
170
- let value_str = snippet_with_context ( cx, insertion. value . span , then_expr. span . ctxt ( ) , ".. " , & mut app) . 0 ;
173
+ let value_str = snippet_with_context ( cx, insertion. value . span , then_expr. span . ctxt ( ) , "_ " , & mut app) . 0 ;
171
174
if contains_expr. negated {
172
175
if insertion. value . can_have_side_effects ( ) {
173
176
Some ( format ! ( "{map_str}.entry({key_str}).or_insert_with(|| {value_str});" ) )
@@ -312,15 +315,12 @@ struct InsertExpr<'tcx> {
312
315
///
313
316
/// If the given expression is not an `insert` call into a `BTreeMap` or a `HashMap`, return `None`.
314
317
fn try_parse_insert < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < InsertExpr < ' tcx > > {
315
- if let ExprKind :: MethodCall ( _, map, [ key, value] , _) = expr. kind {
316
- let id = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) ?;
317
- if let Some ( insert) = cx. tcx . get_diagnostic_name ( id)
318
- && matches ! ( insert, sym:: btreemap_insert | sym:: hashmap_insert)
319
- {
320
- Some ( InsertExpr { map, key, value } )
321
- } else {
322
- None
323
- }
318
+ if let ExprKind :: MethodCall ( _, map, [ key, value] , _) = expr. kind
319
+ && let Some ( id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
320
+ && let Some ( insert) = cx. tcx . get_diagnostic_name ( id)
321
+ && matches ! ( insert, sym:: btreemap_insert | sym:: hashmap_insert)
322
+ {
323
+ Some ( InsertExpr { map, key, value } )
324
324
} else {
325
325
None
326
326
}
@@ -524,15 +524,15 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
524
524
ExprKind :: If ( cond_expr, then_expr, Some ( else_expr) ) => {
525
525
self . is_single_insert = false ;
526
526
self . visit_non_tail_expr ( cond_expr) ;
527
- // Each branch may contain it's own insert expression.
527
+ // Each branch may contain its own insert expression.
528
528
let mut is_map_used = self . visit_cond_arm ( then_expr) ;
529
529
is_map_used |= self . visit_cond_arm ( else_expr) ;
530
530
self . is_map_used = is_map_used;
531
531
} ,
532
532
ExprKind :: Match ( scrutinee_expr, arms, _) => {
533
533
self . is_single_insert = false ;
534
534
self . visit_non_tail_expr ( scrutinee_expr) ;
535
- // Each branch may contain it's own insert expression.
535
+ // Each branch may contain its own insert expression.
536
536
let mut is_map_used = self . is_map_used ;
537
537
for arm in arms {
538
538
self . visit_pat ( arm. pat ) ;
@@ -609,6 +609,25 @@ impl<'tcx> InsertSearchResults<'tcx> {
609
609
self . is_single_insert . then ( || self . edits [ 0 ] . as_insertion ( ) . unwrap ( ) )
610
610
}
611
611
612
+ /// Create a snippet with all the `insert`s on the map replaced with `insert`s on the entry.
613
+ ///
614
+ /// `write_wrapped` will be called whenever the `insert` is an expression that is used, or has
615
+ /// its type unified with another branch -- for example, in cases like:
616
+ /// ```ignore
617
+ /// let _ = if !m.contains(&k) {
618
+ /// m.insert(k, v)
619
+ /// } else {
620
+ /// None
621
+ /// };
622
+ ///
623
+ /// let _ = if m.contains(&k) {
624
+ /// if true { m.insert(k, v) } else { m.insert(k, v2) }
625
+ /// } else {
626
+ /// m.insert(k, v3)
627
+ /// }
628
+ /// ```
629
+ /// The idea is that it will wrap the `insert` call so that it still type-checks with the code
630
+ /// surrounding it.
612
631
fn snippet (
613
632
& self ,
614
633
cx : & LateContext < ' _ > ,
@@ -619,6 +638,7 @@ impl<'tcx> InsertSearchResults<'tcx> {
619
638
let ctxt = span. ctxt ( ) ;
620
639
let mut res = String :: new ( ) ;
621
640
for insertion in self . edits . iter ( ) . filter_map ( |e| e. as_insertion ( ) ) {
641
+ // Take everything until the call verbatim
622
642
res. push_str ( & snippet_with_applicability (
623
643
cx,
624
644
span. until ( insertion. call . span ) ,
@@ -628,11 +648,8 @@ impl<'tcx> InsertSearchResults<'tcx> {
628
648
if is_expr_used_or_unified ( cx. tcx , insertion. call ) {
629
649
write_wrapped ( & mut res, insertion, ctxt, app) ;
630
650
} else {
631
- let _: fmt:: Result = write ! (
632
- res,
633
- "e.insert({})" ,
634
- snippet_with_context( cx, insertion. value. span, ctxt, ".." , app) . 0
635
- ) ;
651
+ let value_str = snippet_with_context ( cx, insertion. value . span , ctxt, "_" , app) . 0 ;
652
+ let _: fmt:: Result = write ! ( res, "e.insert({value_str})" ) ;
636
653
}
637
654
span = span. trim_start ( insertion. call . span ) . unwrap_or ( DUMMY_SP ) ;
638
655
}
@@ -644,11 +661,8 @@ impl<'tcx> InsertSearchResults<'tcx> {
644
661
(
645
662
self . snippet ( cx, span, app, |res, insertion, ctxt, app| {
646
663
// Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
647
- let _: fmt:: Result = write ! (
648
- res,
649
- "Some(e.insert({}))" ,
650
- snippet_with_context( cx, insertion. value. span, ctxt, ".." , app) . 0
651
- ) ;
664
+ let value_str = snippet_with_context ( cx, insertion. value . span , ctxt, "_" , app) . 0 ;
665
+ let _: fmt:: Result = write ! ( res, "Some(e.insert({value_str}))" ) ;
652
666
} ) ,
653
667
"Occupied(mut e)" ,
654
668
)
@@ -658,19 +672,15 @@ impl<'tcx> InsertSearchResults<'tcx> {
658
672
(
659
673
self . snippet ( cx, span, app, |res, insertion, ctxt, app| {
660
674
// Insertion into a map would return `None`, but the entry returns a mutable reference.
675
+ let value_str = snippet_with_context ( cx, insertion. value . span , ctxt, "_" , app) . 0 ;
661
676
let _: fmt:: Result = if is_expr_final_block_expr ( cx. tcx , insertion. call ) {
662
677
write ! (
663
678
res,
664
- "e.insert({});\n {}None" ,
665
- snippet_with_context( cx, insertion. value. span, ctxt, ".." , app) . 0 ,
666
- snippet_indent( cx, insertion. call. span) . as_deref( ) . unwrap_or( "" ) ,
679
+ "e.insert({value_str});\n {indent}None" ,
680
+ indent = snippet_indent( cx, insertion. call. span) . as_deref( ) . unwrap_or( "" ) ,
667
681
)
668
682
} else {
669
- write ! (
670
- res,
671
- "{{ e.insert({}); None }}" ,
672
- snippet_with_context( cx, insertion. value. span, ctxt, ".." , app) . 0 ,
673
- )
683
+ write ! ( res, "{{ e.insert({value_str}); None }}" )
674
684
} ;
675
685
} ) ,
676
686
"Vacant(e)" ,
@@ -683,14 +693,15 @@ impl<'tcx> InsertSearchResults<'tcx> {
683
693
for edit in & self . edits {
684
694
match * edit {
685
695
Edit :: Insertion ( insertion) => {
686
- // Cut out the value from `map.insert(key, value)`
696
+ // Take everything until the call verbatim
687
697
res. push_str ( & snippet_with_applicability (
688
698
cx,
689
699
span. until ( insertion. call . span ) ,
690
700
".." ,
691
701
app,
692
702
) ) ;
693
- res. push_str ( & snippet_with_context ( cx, insertion. value . span , ctxt, ".." , app) . 0 ) ;
703
+ let value_str = snippet_with_context ( cx, insertion. value . span , ctxt, "_" , app) . 0 ;
704
+ res. push_str ( & value_str) ;
694
705
span = span. trim_start ( insertion. call . span ) . unwrap_or ( DUMMY_SP ) ;
695
706
} ,
696
707
Edit :: RemoveSemi ( semi_span) => {
0 commit comments