1
- use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed } ;
1
+ use rustc_errors:: {
2
+ Applicability , Diagnostic , DiagnosticBuilder , EmissionGuarantee , ErrorGuaranteed ,
3
+ } ;
2
4
use rustc_hir as hir;
5
+ use rustc_hir:: intravisit:: Visitor ;
3
6
use rustc_hir:: Node ;
4
7
use rustc_middle:: hir:: map:: Map ;
5
8
use rustc_middle:: mir:: { Mutability , Place , PlaceRef , ProjectionElem } ;
@@ -614,7 +617,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
614
617
"trait `IndexMut` is required to modify indexed content, \
615
618
but it is not implemented for `{ty}`",
616
619
) ) ;
617
- self . suggest_map_index_mut_alternatives ( ty, & mut err) ;
620
+ self . suggest_map_index_mut_alternatives ( ty, & mut err, span ) ;
618
621
}
619
622
_ => ( ) ,
620
623
}
@@ -632,13 +635,120 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
632
635
& self ,
633
636
ty : Ty < ' _ > ,
634
637
err : & mut DiagnosticBuilder < ' _ , ErrorGuaranteed > ,
638
+ span : Span ,
635
639
) {
636
640
let Some ( adt) = ty. ty_adt_def ( ) else { return } ;
637
641
let did = adt. did ( ) ;
638
642
if self . infcx . tcx . is_diagnostic_item ( sym:: HashMap , did)
639
643
|| self . infcx . tcx . is_diagnostic_item ( sym:: BTreeMap , did)
640
644
{
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
+ }
642
752
}
643
753
}
644
754
0 commit comments