|
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 |
|
@@ -364,6 +364,8 @@ struct InsertSearcher<'cx, 'tcx> { |
364 | 364 | is_single_insert: bool, |
365 | 365 | /// If the visitor has seen the map being used. |
366 | 366 | is_map_used: bool, |
| 367 | + /// If the visitor has seen the key being used. |
| 368 | + is_key_used: bool, |
367 | 369 | /// The locations where changes need to be made for the suggestion. |
368 | 370 | edits: Vec<Edit<'tcx>>, |
369 | 371 | /// A stack of loops the visitor is currently in. |
@@ -505,6 +507,9 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { |
505 | 507 | _ if is_any_expr_in_map_used(self.cx, self.map, expr) => { |
506 | 508 | self.is_map_used = true; |
507 | 509 | }, |
| 510 | + _ if SpanlessEq::new(self.cx).eq_expr(self.key, expr) => { |
| 511 | + self.is_key_used = true; |
| 512 | + }, |
508 | 513 | _ => match expr.kind { |
509 | 514 | ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { |
510 | 515 | self.is_single_insert = false; |
@@ -582,6 +587,7 @@ struct InsertSearchResults<'tcx> { |
582 | 587 | edits: Vec<Edit<'tcx>>, |
583 | 588 | allow_insert_closure: bool, |
584 | 589 | is_single_insert: bool, |
| 590 | + is_key_used_and_no_copy: bool, |
585 | 591 | } |
586 | 592 | impl<'tcx> InsertSearchResults<'tcx> { |
587 | 593 | fn as_single_insertion(&self) -> Option<Insertion<'tcx>> { |
@@ -699,17 +705,20 @@ fn find_insert_calls<'tcx>( |
699 | 705 | in_tail_pos: true, |
700 | 706 | is_single_insert: true, |
701 | 707 | is_map_used: false, |
| 708 | + is_key_used: false, |
702 | 709 | edits: Vec::new(), |
703 | 710 | loops: Vec::new(), |
704 | 711 | locals: HirIdSet::default(), |
705 | 712 | }; |
706 | 713 | s.visit_expr(expr); |
707 | 714 | let allow_insert_closure = s.allow_insert_closure; |
708 | 715 | let is_single_insert = s.is_single_insert; |
| 716 | + let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key)); |
709 | 717 | let edits = s.edits; |
710 | 718 | s.can_use_entry.then_some(InsertSearchResults { |
711 | 719 | edits, |
712 | 720 | allow_insert_closure, |
713 | 721 | is_single_insert, |
| 722 | + is_key_used_and_no_copy, |
714 | 723 | }) |
715 | 724 | } |
0 commit comments