1- use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed } ;
1+ use rustc_errors:: {
2+ Applicability , Diagnostic , DiagnosticBuilder , EmissionGuarantee , ErrorGuaranteed ,
3+ } ;
24use rustc_hir as hir;
5+ use rustc_hir:: intravisit:: Visitor ;
36use rustc_hir:: Node ;
47use rustc_middle:: hir:: map:: Map ;
58use rustc_middle:: mir:: { Mutability , Place , PlaceRef , ProjectionElem } ;
@@ -614,7 +617,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
614617 "trait `IndexMut` is required to modify indexed content, \
615618 but it is not implemented for `{ty}`",
616619 ) ) ;
617- self . suggest_map_index_mut_alternatives ( ty, & mut err) ;
620+ self . suggest_map_index_mut_alternatives ( ty, & mut err, span ) ;
618621 }
619622 _ => ( ) ,
620623 }
@@ -632,13 +635,120 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
632635 & self ,
633636 ty : Ty < ' _ > ,
634637 err : & mut DiagnosticBuilder < ' _ , ErrorGuaranteed > ,
638+ span : Span ,
635639 ) {
636640 let Some ( adt) = ty. ty_adt_def ( ) else { return } ;
637641 let did = adt. did ( ) ;
638642 if self . infcx . tcx . is_diagnostic_item ( sym:: HashMap , did)
639643 || self . infcx . tcx . is_diagnostic_item ( sym:: BTreeMap , did)
640644 {
641- err. help ( format ! ( "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API" ) ) ;
645+ struct V < ' a , ' b , ' tcx , G : EmissionGuarantee > {
646+ assign_span : Span ,
647+ err : & ' a mut DiagnosticBuilder < ' b , G > ,
648+ ty : Ty < ' tcx > ,
649+ suggested : bool ,
650+ }
651+ impl < ' a , ' b : ' a , ' hir , ' tcx , G : EmissionGuarantee > Visitor < ' hir > for V < ' a , ' b , ' tcx , G > {
652+ fn visit_stmt ( & mut self , stmt : & ' hir hir:: Stmt < ' hir > ) {
653+ hir:: intravisit:: walk_stmt ( self , stmt) ;
654+ let expr = match stmt. kind {
655+ hir:: StmtKind :: Semi ( expr) | hir:: StmtKind :: Expr ( expr) => expr,
656+ hir:: StmtKind :: Local ( hir:: Local { init : Some ( expr) , .. } ) => expr,
657+ _ => {
658+ return ;
659+ }
660+ } ;
661+ if let hir:: ExprKind :: Assign ( place, rv, _sp) = expr. kind
662+ && let hir:: ExprKind :: Index ( val, index) = place. kind
663+ && ( expr. span == self . assign_span || place. span == self . assign_span )
664+ {
665+ // val[index] = rv;
666+ // ---------- place
667+ self . err . multipart_suggestions (
668+ & format ! (
669+ "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API" ,
670+ self . ty,
671+ ) ,
672+ vec ! [
673+ vec![ // val.insert(index, rv);
674+ (
675+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
676+ ".insert(" . to_string( ) ,
677+ ) ,
678+ (
679+ index. span. shrink_to_hi( ) . with_hi( rv. span. lo( ) ) ,
680+ ", " . to_string( ) ,
681+ ) ,
682+ ( rv. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
683+ ] ,
684+ vec![ // val.get_mut(index).map(|v| { *v = rv; });
685+ (
686+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
687+ ".get_mut(" . to_string( ) ,
688+ ) ,
689+ (
690+ index. span. shrink_to_hi( ) . with_hi( place. span. hi( ) ) ,
691+ ").map(|val| { *val" . to_string( ) ,
692+ ) ,
693+ (
694+ rv. span. shrink_to_hi( ) ,
695+ "; })" . to_string( ) ,
696+ ) ,
697+ ] ,
698+ vec![ // let x = val.entry(index).or_insert(rv);
699+ ( val. span. shrink_to_lo( ) , "let val = " . to_string( ) ) ,
700+ (
701+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
702+ ".entry(" . to_string( ) ,
703+ ) ,
704+ (
705+ index. span. shrink_to_hi( ) . with_hi( rv. span. lo( ) ) ,
706+ ").or_insert(" . to_string( ) ,
707+ ) ,
708+ ( rv. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
709+ ] ,
710+ ] . into_iter ( ) ,
711+ Applicability :: MachineApplicable ,
712+ ) ;
713+ self . suggested = true ;
714+ } else if let hir:: ExprKind :: MethodCall ( _path, args @ [ _, ..] , sp) = expr. kind
715+ && let hir:: ExprKind :: Index ( val, index) = args[ 0 ] . kind
716+ && expr. span == self . assign_span
717+ {
718+ // val[index].path(args..);
719+ self . err . multipart_suggestion (
720+ & format ! ( "to modify a `{}` use `.get_mut()`" , self . ty) ,
721+ vec ! [
722+ (
723+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
724+ ".get_mut(" . to_string( ) ,
725+ ) ,
726+ (
727+ index. span. shrink_to_hi( ) . with_hi( args[ 0 ] . span. hi( ) ) ,
728+ ").map(|val| val" . to_string( ) ,
729+ ) ,
730+ ( sp. shrink_to_hi( ) , ")" . to_string( ) ) ,
731+ ] ,
732+ Applicability :: MachineApplicable ,
733+ ) ;
734+ self . suggested = true ;
735+ }
736+ }
737+ }
738+ let hir_map = self . infcx . tcx . hir ( ) ;
739+ let def_id = self . body . source . def_id ( ) ;
740+ let hir_id = hir_map. local_def_id_to_hir_id ( def_id. as_local ( ) . unwrap ( ) ) ;
741+ let node = hir_map. find ( hir_id) ;
742+ let Some ( hir:: Node :: Item ( item) ) = node else { return ; } ;
743+ let hir:: ItemKind :: Fn ( .., body_id) = item. kind else { return ; } ;
744+ let body = self . infcx . tcx . hir ( ) . body ( body_id) ;
745+ let mut v = V { assign_span : span, err, ty, suggested : false } ;
746+ v. visit_body ( body) ;
747+ if !v. suggested {
748+ err. help ( & format ! (
749+ "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API" ,
750+ ) ) ;
751+ }
642752 }
643753 }
644754
0 commit comments