@@ -519,7 +519,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
519519 but it is not implemented for `{ty}`",
520520 ) ) ;
521521 }
522- Some ( BorrowedContentSource :: OverloadedIndex ( ty) ) => {
522+ Some ( BorrowedContentSource :: OverloadedIndex ( ty, _ ) ) => {
523523 err. help ( format ! (
524524 "trait `IndexMut` is required to modify indexed content, \
525525 but it is not implemented for `{ty}`",
@@ -1196,7 +1196,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11961196 None
11971197 }
11981198 None => {
1199- if name != kw:: SelfLower {
1199+ let ( should_suggest_ref_mut, suggest_get_mut) =
1200+ self . suggest_get_mut_or_ref_mut ( local, opt_assignment_rhs_span) ;
1201+ if !should_suggest_ref_mut {
1202+ suggest_get_mut
1203+ } else if name != kw:: SelfLower {
12001204 suggest_ampmut (
12011205 self . infcx . tcx ,
12021206 local_decl. ty ,
@@ -1414,6 +1418,79 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
14141418 None => { }
14151419 }
14161420 }
1421+
1422+ /// check if the RHS is an overloaded index expression whose source type
1423+ /// doesn't implement `IndexMut`. This category contains two sub-categories:
1424+ /// 1. HashMap or BTreeMap
1425+ /// 2. Other types
1426+ /// For the first sub-category, we suggest using `.get_mut()` instead of `&mut`,
1427+ /// For the second sub-category, we still suggest use `&mut`.
1428+ /// See issue #143732.
1429+ ///
1430+ /// the first element of return value is a boolean indicating
1431+ /// if we should suggest use `&mut`
1432+ fn suggest_get_mut_or_ref_mut (
1433+ & self ,
1434+ local : Local ,
1435+ opt_assignment_rhs_span : Option < Span > ,
1436+ ) -> ( bool , Option < AmpMutSugg > ) {
1437+ self . find_assignments ( local)
1438+ . first ( )
1439+ . map ( |& location| {
1440+ if let Some ( mir:: Statement {
1441+ source_info : _,
1442+ kind : mir:: StatementKind :: Assign ( box ( _, mir:: Rvalue :: Ref ( _, _, place) ) ) ,
1443+ ..
1444+ } ) = self . body [ location. block ] . statements . get ( location. statement_index )
1445+ && let BorrowedContentSource :: OverloadedIndex ( ty, index_ty) =
1446+ self . borrowed_content_source ( place. as_ref ( ) )
1447+ && let Some ( index_mut_trait) = self . infcx . tcx . lang_items ( ) . index_mut_trait ( )
1448+ && !self
1449+ . infcx
1450+ . type_implements_trait (
1451+ index_mut_trait,
1452+ [ ty, index_ty] ,
1453+ self . infcx . param_env ,
1454+ )
1455+ . must_apply_modulo_regions ( )
1456+ {
1457+ if let Some ( adt) = ty. ty_adt_def ( )
1458+ && ( self . infcx . tcx . is_diagnostic_item ( sym:: HashMap , adt. did ( ) )
1459+ || self . infcx . tcx . is_diagnostic_item ( sym:: BTreeMap , adt. did ( ) ) )
1460+ && let Some ( rhs_span) = opt_assignment_rhs_span
1461+ && let Ok ( rhs_str) =
1462+ self . infcx . tcx . sess . source_map ( ) . span_to_snippet ( rhs_span)
1463+ && let Some ( content) = rhs_str. strip_prefix ( '&' )
1464+ && content. contains ( '[' )
1465+ && content. contains ( ']' )
1466+ && let Some ( bracket_start) = content. find ( '[' )
1467+ && let Some ( bracket_end) = content. rfind ( ']' )
1468+ && bracket_start < bracket_end
1469+ {
1470+ let map_part = & content[ ..bracket_start] ;
1471+ let key_part = & content[ bracket_start + 1 ..bracket_end] ;
1472+
1473+ (
1474+ false ,
1475+ Some ( AmpMutSugg {
1476+ has_sugg : true ,
1477+ span : rhs_span,
1478+ suggestion : format ! ( "{}.get_mut({}).unwrap()" , map_part, key_part) ,
1479+ additional : None ,
1480+ } ) ,
1481+ )
1482+ } else {
1483+ // Type not implemented `IndexMut`,
1484+ // and we did not suggest because of HashMap or BTreeMap
1485+ ( false , None )
1486+ }
1487+ } else {
1488+ // Type Impl IndexMut, we should suggest use `&mut`
1489+ ( true , None )
1490+ }
1491+ } )
1492+ . unwrap_or ( ( true , None ) )
1493+ }
14171494}
14181495
14191496struct BindingFinder {
0 commit comments