@@ -1176,6 +1176,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1176
1176
self . body . source_info ( location) . span
1177
1177
}
1178
1178
} ) ;
1179
+
1180
+ let is_rhs_map_index = self . is_rhs_index_from_hashmap_or_btreemap ( local) ;
1179
1181
match opt_assignment_rhs_span. and_then ( |s| s. desugaring_kind ( ) ) {
1180
1182
// on for loops, RHS points to the iterator part
1181
1183
Some ( DesugaringKind :: ForLoop ) => {
@@ -1203,6 +1205,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1203
1205
decl_span,
1204
1206
opt_assignment_rhs_span,
1205
1207
opt_ty_info,
1208
+ is_rhs_map_index,
1206
1209
)
1207
1210
} else {
1208
1211
match local_decl. local_info ( ) {
@@ -1226,6 +1229,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1226
1229
decl_span,
1227
1230
opt_assignment_rhs_span,
1228
1231
opt_ty_info,
1232
+ is_rhs_map_index,
1229
1233
) ,
1230
1234
}
1231
1235
}
@@ -1414,6 +1418,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1414
1418
None => { }
1415
1419
}
1416
1420
}
1421
+
1422
+ /// check if the RHS is an overloaded index expression from hashmap or btreemap,
1423
+ /// if so, suggest using .get_mut() instead of &mut, see issue #143732
1424
+ /// For example
1425
+ /// ```text
1426
+ /// let mut map = HashMap::new();
1427
+ /// let value = &map["key"];
1428
+ /// ```
1429
+ fn is_rhs_index_from_hashmap_or_btreemap ( & self , local : Local ) -> bool {
1430
+ self . find_assignments ( local)
1431
+ . first ( )
1432
+ . map ( |& location| {
1433
+ if let Some ( mir:: Statement {
1434
+ source_info : _,
1435
+ kind : mir:: StatementKind :: Assign ( box ( _, mir:: Rvalue :: Ref ( _, _, place) ) ) ,
1436
+ ..
1437
+ } ) = self . body [ location. block ] . statements . get ( location. statement_index )
1438
+ && let BorrowedContentSource :: OverloadedIndex ( ty) =
1439
+ self . borrowed_content_source ( place. as_ref ( ) )
1440
+ && let Some ( def) = ty. ty_adt_def ( )
1441
+ && ( self . infcx . tcx . is_diagnostic_item ( sym:: HashMap , def. did ( ) )
1442
+ || self . infcx . tcx . is_diagnostic_item ( sym:: BTreeMap , def. did ( ) ) )
1443
+ {
1444
+ true
1445
+ } else {
1446
+ false
1447
+ }
1448
+ } )
1449
+ . unwrap_or ( false )
1450
+ }
1417
1451
}
1418
1452
1419
1453
struct BindingFinder {
@@ -1507,6 +1541,7 @@ fn suggest_ampmut<'tcx>(
1507
1541
decl_span : Span ,
1508
1542
opt_assignment_rhs_span : Option < Span > ,
1509
1543
opt_ty_info : Option < Span > ,
1544
+ is_rhs_map_index : bool ,
1510
1545
) -> Option < AmpMutSugg > {
1511
1546
// if there is a RHS and it starts with a `&` from it, then check if it is
1512
1547
// mutable, and if not, put suggest putting `mut ` to make it mutable.
@@ -1553,6 +1588,28 @@ fn suggest_ampmut<'tcx>(
1553
1588
// if the reference is already mutable then there is nothing we can do
1554
1589
// here.
1555
1590
if !is_mut {
1591
+ // If this is an overloaded index expression, suggest using .get_mut() instead of &mut
1592
+ if is_rhs_map_index {
1593
+ // Try to extract the expression from &expr[key] to suggest expr.get_mut(key).unwrap()
1594
+ let content = rhs_str. strip_prefix ( '&' ) ?;
1595
+ if content. contains ( '[' ) && content. contains ( ']' ) {
1596
+ let bracket_start = content. find ( '[' ) ?;
1597
+ let bracket_end = content. rfind ( ']' ) ?;
1598
+
1599
+ if bracket_start < bracket_end {
1600
+ let map_part = & content[ ..bracket_start] ;
1601
+ let key_part = & content[ bracket_start + 1 ..bracket_end] ;
1602
+
1603
+ return Some ( AmpMutSugg {
1604
+ has_sugg : true ,
1605
+ span : rhs_span,
1606
+ suggestion : format ! ( "{}.get_mut({}).unwrap()" , map_part, key_part) ,
1607
+ additional : None ,
1608
+ } ) ;
1609
+ }
1610
+ }
1611
+ }
1612
+
1556
1613
// shrink the span to just after the `&` in `&variable`
1557
1614
let span = rhs_span. with_lo ( rhs_span. lo ( ) + BytePos ( 1 ) ) . shrink_to_lo ( ) ;
1558
1615
0 commit comments