@@ -611,7 +611,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
611611 err : & ' a mut Diag < ' infcx > ,
612612 ty : Ty < ' tcx > ,
613613 suggested : bool ,
614+ infcx : & ' a rustc_infer:: infer:: InferCtxt < ' tcx > ,
614615 }
616+
615617 impl < ' a , ' infcx , ' tcx > Visitor < ' tcx > for SuggestIndexOperatorAlternativeVisitor < ' a , ' infcx , ' tcx > {
616618 fn visit_stmt ( & mut self , stmt : & ' tcx hir:: Stmt < ' tcx > ) {
617619 hir:: intravisit:: walk_stmt ( self , stmt) ;
@@ -622,81 +624,101 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
622624 return ;
623625 }
624626 } ;
627+
628+ /// Taken straight from https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.peel_hir_ty_refs.html
629+ /// Adapted to mid using https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.peel_refs
630+ /// Simplified to counting only
631+ /// Peels off all references on the type. Returns the number of references
632+ /// removed.
633+ fn count_ty_refs < ' tcx > ( ty : Ty < ' tcx > ) -> usize {
634+ let mut count = 0 ;
635+ let mut ty = ty;
636+ while let ty:: Ref ( _, inner_ty, _) = ty. kind ( ) {
637+ ty = * inner_ty;
638+ count += 1 ;
639+ }
640+ count
641+ }
625642 if let hir:: ExprKind :: Assign ( place, rv, _sp) = expr. kind
626643 && let hir:: ExprKind :: Index ( val, index, _) = place. kind
627644 && ( expr. span == self . assign_span || place. span == self . assign_span )
628645 {
629- // val[index] = rv;
630- // ---------- place
631- self . err . multipart_suggestions (
646+ let ref_depth_difference: usize ;
647+ let _index_is_copy_clone: bool ;
648+
649+ if let Some ( index_ty) =
650+ self . infcx . tcx . typeck ( val. hir_id . owner . def_id ) . expr_ty_opt ( index)
651+ {
652+ // we know ty is a map, with a key type at walk distance 2.
653+ let key_type = self . ty . walk ( ) . nth ( 1 ) . unwrap ( ) . expect_ty ( ) ;
654+ let key_ref_depth = count_ty_refs ( key_type) ;
655+
656+ let index_ref_depth = count_ty_refs ( index_ty) ;
657+ ref_depth_difference = index_ref_depth - key_ref_depth; //index should
658+ //be deeper than key
659+ } else {
660+ // no type ?
661+ return ;
662+ } ;
663+
664+ // remove the exessive referencing if necessary, but get_mut requires a ref
665+ let ( prefix, gm_prefix) = match ref_depth_difference {
666+ 0 => ( String :: new ( ) , String :: from ( "&" ) ) ,
667+ n => ( "*" . repeat ( n) , "*" . repeat ( n - 1 ) ) ,
668+ } ;
669+
670+ self . err . multipart_suggestion (
671+ format ! ( "use `.insert()` to insert a value into a `{}`" , self . ty) ,
672+ vec ! [
673+ // val.insert(index, rv);
674+ (
675+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
676+ format!( ".insert({prefix}" ) ,
677+ ) ,
678+ ( index. span. shrink_to_hi( ) . with_hi( rv. span. lo( ) ) , ", " . to_string( ) ) ,
679+ ( rv. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
680+ ] ,
681+ Applicability :: MaybeIncorrect ,
682+ ) ;
683+ self . err . multipart_suggestion (
632684 format ! (
633- "use `.insert()` to insert a value into a `{}`, `.get_mut()` \
634- to modify it, or the entry API for more flexibility",
685+ "use `.get_mut()` to modify an existing key in a `{}`" ,
635686 self . ty,
636687 ) ,
637688 vec ! [
638- vec![
639- // val.insert(index, rv);
640- (
641- val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
642- ".insert(" . to_string( ) ,
643- ) ,
644- (
645- index. span. shrink_to_hi( ) . with_hi( rv. span. lo( ) ) ,
646- ", " . to_string( ) ,
647- ) ,
648- ( rv. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
649- ] ,
650- vec![
651- // if let Some(v) = val.get_mut(index) { *v = rv; }
652- ( val. span. shrink_to_lo( ) , "if let Some(val) = " . to_string( ) ) ,
653- (
654- val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
655- ".get_mut(" . to_string( ) ,
656- ) ,
657- (
658- index. span. shrink_to_hi( ) . with_hi( place. span. hi( ) ) ,
659- ") { *val" . to_string( ) ,
660- ) ,
661- ( rv. span. shrink_to_hi( ) , "; }" . to_string( ) ) ,
662- ] ,
663- vec![
664- // let x = val.entry(index).or_insert(rv);
665- ( val. span. shrink_to_lo( ) , "let val = " . to_string( ) ) ,
666- (
667- val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
668- ".entry(" . to_string( ) ,
669- ) ,
670- (
671- index. span. shrink_to_hi( ) . with_hi( rv. span. lo( ) ) ,
672- ").or_insert(" . to_string( ) ,
673- ) ,
674- ( rv. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
675- ] ,
689+ // if let Some(v) = val.get_mut(index) { *v = rv; }
690+ ( val. span. shrink_to_lo( ) , "if let Some(val) = " . to_string( ) ) ,
691+ (
692+ val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
693+ format!( ".get_mut({gm_prefix}" ) ,
694+ ) ,
695+ (
696+ index. span. shrink_to_hi( ) . with_hi( place. span. hi( ) ) ,
697+ ") { *val" . to_string( ) ,
698+ ) ,
699+ ( rv. span. shrink_to_hi( ) , "; }" . to_string( ) ) ,
676700 ] ,
677- Applicability :: MachineApplicable ,
701+ Applicability :: MaybeIncorrect ,
678702 ) ;
679- self . suggested = true ;
680- } else if let hir:: ExprKind :: MethodCall ( _path, receiver, _, sp) = expr. kind
681- && let hir:: ExprKind :: Index ( val, index, _) = receiver. kind
682- && receiver. span == self . assign_span
683- {
684- // val[index].path(args..);
685703 self . err . multipart_suggestion (
686- format ! ( "to modify a `{}` use `.get_mut()`" , self . ty) ,
704+ format ! (
705+ "use the entry API to modify a `{}` for more flexibility" ,
706+ self . ty
707+ ) ,
687708 vec ! [
688- ( val. span. shrink_to_lo( ) , "if let Some(val) = " . to_string( ) ) ,
709+ // let x = val.entry(index).insert_entry(rv);
710+ ( val. span. shrink_to_lo( ) , "let val = " . to_string( ) ) ,
689711 (
690712 val. span. shrink_to_hi( ) . with_hi( index. span. lo( ) ) ,
691- ".get_mut (". to_string ( ) ,
713+ format! ( ".entry({prefix}" ) ,
692714 ) ,
693715 (
694- index. span. shrink_to_hi( ) . with_hi( receiver . span. hi ( ) ) ,
695- ") { val " . to_string( ) ,
716+ index. span. shrink_to_hi( ) . with_hi( rv . span. lo ( ) ) ,
717+ ").insert_entry( " . to_string( ) ,
696718 ) ,
697- ( sp . shrink_to_hi( ) , "; } " . to_string( ) ) ,
719+ ( rv . span . shrink_to_hi( ) , ") " . to_string( ) ) ,
698720 ] ,
699- Applicability :: MachineApplicable ,
721+ Applicability :: MaybeIncorrect ,
700722 ) ;
701723 self . suggested = true ;
702724 }
@@ -711,6 +733,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
711733 err,
712734 ty,
713735 suggested : false ,
736+ infcx : self . infcx ,
714737 } ;
715738 v. visit_body ( & body) ;
716739 if !v. suggested {
0 commit comments