@@ -755,15 +755,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
755
755
}
756
756
757
757
errors. drain_filter ( |error| {
758
- let Error :: Invalid ( provided_idx, expected_idx, Compatibility :: Incompatible ( Some ( e) ) ) = error else { return false } ;
759
- let ( provided_ty, provided_span) = provided_arg_tys[ * provided_idx] ;
760
- let trace = mk_trace ( provided_span, formal_and_expected_inputs[ * expected_idx] , provided_ty) ;
761
- if !matches ! ( trace. cause. as_failure_code( * e) , FailureCode :: Error0308 ( _) ) {
762
- self . err_ctxt ( ) . report_and_explain_type_error ( trace, * e) . emit ( ) ;
763
- return true ;
764
- }
765
- false
766
- } ) ;
758
+ let Error :: Invalid (
759
+ provided_idx,
760
+ expected_idx,
761
+ Compatibility :: Incompatible ( Some ( e) ) ,
762
+ ) = error else { return false } ;
763
+ let ( provided_ty, provided_span) = provided_arg_tys[ * provided_idx] ;
764
+ let trace =
765
+ mk_trace ( provided_span, formal_and_expected_inputs[ * expected_idx] , provided_ty) ;
766
+ if !matches ! ( trace. cause. as_failure_code( * e) , FailureCode :: Error0308 ( _) ) {
767
+ self . err_ctxt ( ) . report_and_explain_type_error ( trace, * e) . emit ( ) ;
768
+ return true ;
769
+ }
770
+ false
771
+ } ) ;
767
772
768
773
// We're done if we found errors, but we already emitted them.
769
774
if errors. is_empty ( ) {
@@ -864,7 +869,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
864
869
}
865
870
let mut suggestion_text = SuggestionText :: None ;
866
871
872
+ let ty_to_snippet = |ty : Ty < ' tcx > , expected_idx : ExpectedIdx | {
873
+ if ty. is_unit ( ) {
874
+ "()" . to_string ( )
875
+ } else if ty. is_suggestable ( tcx, false ) {
876
+ format ! ( "/* {} */" , ty)
877
+ } else if let Some ( fn_def_id) = fn_def_id
878
+ && self . tcx . def_kind ( fn_def_id) . is_fn_like ( )
879
+ && let self_implicit =
880
+ matches ! ( call_expr. kind, hir:: ExprKind :: MethodCall ( ..) ) as usize
881
+ && let Some ( arg) = self . tcx . fn_arg_names ( fn_def_id)
882
+ . get ( expected_idx. as_usize ( ) + self_implicit)
883
+ && arg. name != kw:: SelfLower
884
+ {
885
+ format ! ( "/* {} */" , arg. name)
886
+ } else {
887
+ "/* value */" . to_string ( )
888
+ }
889
+ } ;
890
+
867
891
let mut errors = errors. into_iter ( ) . peekable ( ) ;
892
+ let mut suggestions = vec ! [ ] ;
868
893
while let Some ( error) = errors. next ( ) {
869
894
match error {
870
895
Error :: Invalid ( provided_idx, expected_idx, compatibility) => {
@@ -905,7 +930,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
905
930
"" . to_string ( )
906
931
} ;
907
932
labels
908
- . push ( ( provided_span, format ! ( "argument{} unexpected" , provided_ty_name) ) ) ;
933
+ . push ( ( provided_span, format ! ( "unexpected argument{}" , provided_ty_name) ) ) ;
934
+ let mut span = provided_span;
935
+ if arg_idx. index ( ) > 0
936
+ && let Some ( ( _, prev) ) = provided_arg_tys
937
+ . get ( ProvidedIdx :: from_usize ( arg_idx. index ( ) - 1 )
938
+ ) {
939
+ // Include previous comma
940
+ span = span. with_lo ( prev. hi ( ) ) ;
941
+ } else if let Some ( ( _, next) ) = provided_arg_tys. get (
942
+ ProvidedIdx :: from_usize ( arg_idx. index ( ) + 1 ) ,
943
+ ) {
944
+ // Include next comma
945
+ span = span. until ( * next) ;
946
+ }
947
+ suggestions. push ( ( span, String :: new ( ) ) ) ;
948
+
909
949
suggestion_text = match suggestion_text {
910
950
SuggestionText :: None => SuggestionText :: Remove ( false ) ,
911
951
SuggestionText :: Remove ( _) => SuggestionText :: Remove ( true ) ,
@@ -1095,6 +1135,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1095
1135
}
1096
1136
}
1097
1137
1138
+ // Incorporate the argument changes in the removal suggestion.
1139
+ // When a type is *missing*, and the rest are additional, we want to suggest these with a
1140
+ // multipart suggestion, but in order to do so we need to figure out *where* the arg that
1141
+ // was provided but had the wrong type should go, because when looking at `expected_idx`
1142
+ // that is the position in the argument list in the definition, while `provided_idx` will
1143
+ // not be present. So we have to look at what the *last* provided position was, and point
1144
+ // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's
1145
+ // probably a better more involved change we can make to make this work.
1146
+ // For example, if we have
1147
+ // ```
1148
+ // fn foo(i32, &'static str) {}
1149
+ // foo((), (), ());
1150
+ // ```
1151
+ // what should be suggested is
1152
+ // ```
1153
+ // foo(/* i32 */, /* &str */);
1154
+ // ```
1155
+ // which includes the replacement of the first two `()` for the correct type, and the
1156
+ // removal of the last `()`.
1157
+ let mut prev = -1 ;
1158
+ for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1159
+ // We want to point not at the *current* argument expression index, but rather at the
1160
+ // index position where it *should have been*, which is *after* the previous one.
1161
+ if let Some ( provided_idx) = provided_idx {
1162
+ prev = provided_idx. index ( ) as i64 ;
1163
+ }
1164
+ let idx = ProvidedIdx :: from_usize ( ( prev + 1 ) as usize ) ;
1165
+ if let None = provided_idx
1166
+ && let Some ( ( _, arg_span) ) = provided_arg_tys. get ( idx)
1167
+ {
1168
+ // There is a type that was *not* found anywhere, so it isn't a move, but a
1169
+ // replacement and we look at what type it should have been. This will allow us
1170
+ // To suggest a multipart suggestion when encountering `foo(1, "")` where the def
1171
+ // was `fn foo(())`.
1172
+ let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1173
+ suggestions. push ( ( * arg_span, ty_to_snippet ( expected_ty, expected_idx) ) ) ;
1174
+ }
1175
+ }
1176
+
1098
1177
// If we have less than 5 things to say, it would be useful to call out exactly what's wrong
1099
1178
if labels. len ( ) <= 5 {
1100
1179
for ( span, label) in labels {
@@ -1112,7 +1191,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1112
1191
Some ( format ! ( "provide the argument{}" , if plural { "s" } else { "" } ) )
1113
1192
}
1114
1193
SuggestionText :: Remove ( plural) => {
1115
- Some ( format ! ( "remove the extra argument{}" , if plural { "s" } else { "" } ) )
1194
+ err. multipart_suggestion (
1195
+ & format ! ( "remove the extra argument{}" , if plural { "s" } else { "" } ) ,
1196
+ suggestions,
1197
+ Applicability :: HasPlaceholders ,
1198
+ ) ;
1199
+ None
1116
1200
}
1117
1201
SuggestionText :: Swap => Some ( "swap these arguments" . to_string ( ) ) ,
1118
1202
SuggestionText :: Reorder => Some ( "reorder these arguments" . to_string ( ) ) ,
@@ -1151,20 +1235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1151
1235
} else {
1152
1236
// Propose a placeholder of the correct type
1153
1237
let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1154
- if expected_ty. is_unit ( ) {
1155
- "()" . to_string ( )
1156
- } else if expected_ty. is_suggestable ( tcx, false ) {
1157
- format ! ( "/* {} */" , expected_ty)
1158
- } else if let Some ( fn_def_id) = fn_def_id
1159
- && self . tcx . def_kind ( fn_def_id) . is_fn_like ( )
1160
- && let self_implicit = matches ! ( call_expr. kind, hir:: ExprKind :: MethodCall ( ..) ) as usize
1161
- && let Some ( arg) = self . tcx . fn_arg_names ( fn_def_id) . get ( expected_idx. as_usize ( ) + self_implicit)
1162
- && arg. name != kw:: SelfLower
1163
- {
1164
- format ! ( "/* {} */" , arg. name)
1165
- } else {
1166
- "/* value */" . to_string ( )
1167
- }
1238
+ ty_to_snippet ( expected_ty, expected_idx)
1168
1239
} ;
1169
1240
suggestion += & suggestion_text;
1170
1241
}
0 commit comments