@@ -755,15 +755,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
755755 }
756756
757757 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+ } ) ;
767772
768773 // We're done if we found errors, but we already emitted them.
769774 if errors. is_empty ( ) {
@@ -864,7 +869,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
864869 }
865870 let mut suggestion_text = SuggestionText :: None ;
866871
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+
867891 let mut errors = errors. into_iter ( ) . peekable ( ) ;
892+ let mut suggestions = vec ! [ ] ;
868893 while let Some ( error) = errors. next ( ) {
869894 match error {
870895 Error :: Invalid ( provided_idx, expected_idx, compatibility) => {
@@ -905,7 +930,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
905930 "" . to_string ( )
906931 } ;
907932 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+
909949 suggestion_text = match suggestion_text {
910950 SuggestionText :: None => SuggestionText :: Remove ( false ) ,
911951 SuggestionText :: Remove ( _) => SuggestionText :: Remove ( true ) ,
@@ -1095,6 +1135,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10951135 }
10961136 }
10971137
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+
10981177 // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
10991178 if labels. len ( ) <= 5 {
11001179 for ( span, label) in labels {
@@ -1112,7 +1191,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11121191 Some ( format ! ( "provide the argument{}" , if plural { "s" } else { "" } ) )
11131192 }
11141193 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
11161200 }
11171201 SuggestionText :: Swap => Some ( "swap these arguments" . to_string ( ) ) ,
11181202 SuggestionText :: Reorder => Some ( "reorder these arguments" . to_string ( ) ) ,
@@ -1151,20 +1235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11511235 } else {
11521236 // Propose a placeholder of the correct type
11531237 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)
11681239 } ;
11691240 suggestion += & suggestion_text;
11701241 }
0 commit comments