1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
1
+ use clippy_utils:: diagnostics:: { span_lint , span_lint_and_sugg} ;
2
2
use clippy_utils:: source:: { reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context} ;
3
+ use clippy_utils:: ty:: is_copy;
3
4
use clippy_utils:: visitors:: for_each_expr;
4
5
use clippy_utils:: {
5
6
SpanlessEq , can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
@@ -84,14 +85,21 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
84
85
return ;
85
86
} ;
86
87
88
+ let lint_msg = format ! ( "usage of `contains_key` followed by `insert` on a `{}`" , map_ty. name( ) ) ;
87
89
let mut app = Applicability :: MachineApplicable ;
88
90
let map_str = snippet_with_context ( cx, contains_expr. map . span , contains_expr. call_ctxt , ".." , & mut app) . 0 ;
89
91
let key_str = snippet_with_context ( cx, contains_expr. key . span , contains_expr. call_ctxt , ".." , & mut app) . 0 ;
92
+
90
93
let sugg = if let Some ( else_expr) = else_expr {
91
94
let Some ( else_search) = find_insert_calls ( cx, & contains_expr, else_expr) else {
92
95
return ;
93
96
} ;
94
97
98
+ if then_search. is_key_used_and_no_copy || else_search. is_key_used_and_no_copy {
99
+ span_lint ( cx, MAP_ENTRY , expr. span , lint_msg) ;
100
+ return ;
101
+ }
102
+
95
103
if then_search. edits . is_empty ( ) && else_search. edits . is_empty ( ) {
96
104
// No insertions
97
105
return ;
@@ -184,15 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
184
192
}
185
193
} ;
186
194
187
- span_lint_and_sugg (
188
- cx,
189
- MAP_ENTRY ,
190
- expr. span ,
191
- format ! ( "usage of `contains_key` followed by `insert` on a `{}`" , map_ty. name( ) ) ,
192
- "try" ,
193
- sugg,
194
- app,
195
- ) ;
195
+ span_lint_and_sugg ( cx, MAP_ENTRY , expr. span , lint_msg, "try" , sugg, app) ;
196
196
}
197
197
}
198
198
@@ -354,6 +354,8 @@ struct InsertSearcher<'cx, 'tcx> {
354
354
key : & ' tcx Expr < ' tcx > ,
355
355
/// The context of the top level block. All insert calls must be in the same context.
356
356
ctxt : SyntaxContext ,
357
+ /// The spanless equality utility used to compare expressions.
358
+ spanless_eq : SpanlessEq < ' cx , ' tcx > ,
357
359
/// Whether this expression can be safely moved into a closure.
358
360
allow_insert_closure : bool ,
359
361
/// Whether this expression can use the entry api.
@@ -364,6 +366,8 @@ struct InsertSearcher<'cx, 'tcx> {
364
366
is_single_insert : bool ,
365
367
/// If the visitor has seen the map being used.
366
368
is_map_used : bool ,
369
+ /// If the visitor has seen the key being used.
370
+ is_key_used : bool ,
367
371
/// The locations where changes need to be made for the suggestion.
368
372
edits : Vec < Edit < ' tcx > > ,
369
373
/// A stack of loops the visitor is currently in.
@@ -479,11 +483,11 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
479
483
}
480
484
481
485
match try_parse_insert ( self . cx , expr) {
482
- Some ( insert_expr) if SpanlessEq :: new ( self . cx ) . eq_expr ( self . map , insert_expr. map ) => {
486
+ Some ( insert_expr) if self . spanless_eq . eq_expr ( self . map , insert_expr. map ) => {
483
487
self . visit_insert_expr_arguments ( & insert_expr) ;
484
488
// Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
485
489
if self . is_map_used
486
- || !SpanlessEq :: new ( self . cx ) . eq_expr ( self . key , insert_expr. key )
490
+ || !self . spanless_eq . eq_expr ( self . key , insert_expr. key )
487
491
|| expr. span . ctxt ( ) != self . ctxt
488
492
{
489
493
self . can_use_entry = false ;
@@ -502,9 +506,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
502
506
self . visit_non_tail_expr ( insert_expr. value ) ;
503
507
self . is_single_insert = is_single_insert;
504
508
} ,
505
- _ if is_any_expr_in_map_used ( self . cx , self . map , expr) => {
509
+ _ if is_any_expr_in_map_used ( self . cx , & mut self . spanless_eq , self . map , expr) => {
506
510
self . is_map_used = true ;
507
511
} ,
512
+ _ if self . spanless_eq . eq_expr ( self . key , expr) => {
513
+ self . is_key_used = true ;
514
+ } ,
508
515
_ => match expr. kind {
509
516
ExprKind :: If ( cond_expr, then_expr, Some ( else_expr) ) => {
510
517
self . is_single_insert = false ;
@@ -568,9 +575,14 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
568
575
/// Check if the given expression is used for each sub-expression in the given map.
569
576
/// For example, in map `a.b.c.my_map`, The expression `a.b.c.my_map`, `a.b.c`, `a.b`, and `a` are
570
577
/// all checked.
571
- fn is_any_expr_in_map_used < ' tcx > ( cx : & LateContext < ' tcx > , map : & ' tcx Expr < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
578
+ fn is_any_expr_in_map_used < ' tcx > (
579
+ cx : & LateContext < ' tcx > ,
580
+ spanless_eq : & mut SpanlessEq < ' _ , ' tcx > ,
581
+ map : & ' tcx Expr < ' tcx > ,
582
+ expr : & ' tcx Expr < ' tcx > ,
583
+ ) -> bool {
572
584
for_each_expr ( cx, map, |e| {
573
- if SpanlessEq :: new ( cx ) . eq_expr ( e, expr) {
585
+ if spanless_eq . eq_expr ( e, expr) {
574
586
return ControlFlow :: Break ( ( ) ) ;
575
587
}
576
588
ControlFlow :: Continue ( ( ) )
@@ -582,6 +594,7 @@ struct InsertSearchResults<'tcx> {
582
594
edits : Vec < Edit < ' tcx > > ,
583
595
allow_insert_closure : bool ,
584
596
is_single_insert : bool ,
597
+ is_key_used_and_no_copy : bool ,
585
598
}
586
599
impl < ' tcx > InsertSearchResults < ' tcx > {
587
600
fn as_single_insertion ( & self ) -> Option < Insertion < ' tcx > > {
@@ -694,22 +707,26 @@ fn find_insert_calls<'tcx>(
694
707
map : contains_expr. map ,
695
708
key : contains_expr. key ,
696
709
ctxt : expr. span . ctxt ( ) ,
710
+ spanless_eq : SpanlessEq :: new ( cx) ,
697
711
allow_insert_closure : true ,
698
712
can_use_entry : true ,
699
713
in_tail_pos : true ,
700
714
is_single_insert : true ,
701
715
is_map_used : false ,
716
+ is_key_used : false ,
702
717
edits : Vec :: new ( ) ,
703
718
loops : Vec :: new ( ) ,
704
719
locals : HirIdSet :: default ( ) ,
705
720
} ;
706
721
s. visit_expr ( expr) ;
707
722
let allow_insert_closure = s. allow_insert_closure ;
708
723
let is_single_insert = s. is_single_insert ;
724
+ let is_key_used_and_no_copy = s. is_key_used && !is_copy ( cx, cx. typeck_results ( ) . expr_ty ( contains_expr. key ) ) ;
709
725
let edits = s. edits ;
710
726
s. can_use_entry . then_some ( InsertSearchResults {
711
727
edits,
712
728
allow_insert_closure,
713
729
is_single_insert,
730
+ is_key_used_and_no_copy,
714
731
} )
715
732
}
0 commit comments