@@ -711,41 +711,105 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
711711 }
712712 }
713713
714- OutputTypeParameterMismatch ( ref expected_trait_ref, ref actual_trait_ref, _) => {
714+ OutputTypeParameterMismatch ( ref found_trait_ref, ref expected_trait_ref, _) => {
715+ let found_trait_ref = self . resolve_type_vars_if_possible ( & * found_trait_ref) ;
715716 let expected_trait_ref = self . resolve_type_vars_if_possible ( & * expected_trait_ref) ;
716- let actual_trait_ref = self . resolve_type_vars_if_possible ( & * actual_trait_ref) ;
717- if actual_trait_ref. self_ty ( ) . references_error ( ) {
717+ if expected_trait_ref. self_ty ( ) . references_error ( ) {
718718 return ;
719719 }
720- let expected_trait_ty = expected_trait_ref. self_ty ( ) ;
721- let found_span = expected_trait_ty. ty_to_def_id ( ) . and_then ( |did| {
720+ let found_trait_ty = found_trait_ref. self_ty ( ) ;
721+
722+ let found_did = found_trait_ty. ty_to_def_id ( ) ;
723+ let found_span = found_did. and_then ( |did| {
722724 self . tcx . hir . span_if_local ( did)
723725 } ) ;
724726
725- let self_ty_count =
726- match expected_trait_ref . skip_binder ( ) . substs . type_at ( 1 ) . sty {
727+ let found_ty_count =
728+ match found_trait_ref . skip_binder ( ) . substs . type_at ( 1 ) . sty {
727729 ty:: TyTuple ( ref tys, _) => tys. len ( ) ,
728730 _ => 1 ,
729731 } ;
730- let arg_ty_count =
731- match actual_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
732- ty:: TyTuple ( ref tys, _) => tys. len ( ) ,
733- _ => 1 ,
732+ let ( expected_tys, expected_ty_count) =
733+ match expected_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
734+ ty:: TyTuple ( ref tys, _) =>
735+ ( tys. iter ( ) . map ( |t| & t. sty ) . collect ( ) , tys. len ( ) ) ,
736+ ref sty => ( vec ! [ sty] , 1 ) ,
734737 } ;
735- if self_ty_count == arg_ty_count {
738+ if found_ty_count == expected_ty_count {
736739 self . report_closure_arg_mismatch ( span,
737740 found_span,
738- expected_trait_ref ,
739- actual_trait_ref )
741+ found_trait_ref ,
742+ expected_trait_ref )
740743 } else {
741- // Expected `|| { }`, found `|x, y| { }`
742- // Expected `fn(x) -> ()`, found `|| { }`
744+ let expected_tuple = if expected_ty_count == 1 {
745+ expected_tys. first ( ) . and_then ( |t| {
746+ if let & & ty:: TyTuple ( ref tuptys, _) = t {
747+ Some ( tuptys. len ( ) )
748+ } else {
749+ None
750+ }
751+ } )
752+ } else {
753+ None
754+ } ;
755+
756+ // FIXME(#44150): Expand this to "N args expected but a N-tuple found."
757+ // Type of the 1st expected argument is somehow provided as type of a
758+ // found one in that case.
759+ //
760+ // ```
761+ // [1i32, 2, 3].sort_by(|(a, b)| ..)
762+ // // ^^^^^^^^
763+ // // expected_trait_ref: std::ops::FnMut<(&i32, &i32)>
764+ // // found_trait_ref: std::ops::FnMut<(&i32,)>
765+ // ```
766+
767+ let ( closure_span, closure_args) = found_did
768+ . and_then ( |did| self . tcx . hir . get_if_local ( did) )
769+ . and_then ( |node| {
770+ if let hir:: map:: NodeExpr (
771+ & hir:: Expr {
772+ node : hir:: ExprClosure ( _, ref decl, id, span, _) ,
773+ ..
774+ } ) = node
775+ {
776+ let ty_snips = decl. inputs . iter ( )
777+ . map ( |ty| {
778+ self . tcx . sess . codemap ( ) . span_to_snippet ( ty. span ) . ok ( )
779+ . and_then ( |snip| {
780+ // filter out dummy spans
781+ if snip == "," || snip == "|" {
782+ None
783+ } else {
784+ Some ( snip)
785+ }
786+ } )
787+ } )
788+ . collect :: < Vec < Option < String > > > ( ) ;
789+
790+ let body = self . tcx . hir . body ( id) ;
791+ let pat_snips = body. arguments . iter ( )
792+ . map ( |arg|
793+ self . tcx . sess . codemap ( ) . span_to_snippet ( arg. pat . span ) . ok ( ) )
794+ . collect :: < Option < Vec < String > > > ( ) ;
795+
796+ Some ( ( span, pat_snips, ty_snips) )
797+ } else {
798+ None
799+ }
800+ } )
801+ . map ( |( span, pat, ty) | ( Some ( span) , Some ( ( pat, ty) ) ) )
802+ . unwrap_or ( ( None , None ) ) ;
803+ let closure_args = closure_args. and_then ( |( pat, ty) | Some ( ( pat?, ty) ) ) ;
804+
743805 self . report_arg_count_mismatch (
744806 span,
745- found_span,
746- arg_ty_count,
747- self_ty_count,
748- expected_trait_ty. is_closure ( )
807+ closure_span. or ( found_span) ,
808+ expected_ty_count,
809+ expected_tuple,
810+ found_ty_count,
811+ closure_args,
812+ found_trait_ty. is_closure ( )
749813 )
750814 }
751815 }
@@ -767,32 +831,97 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
767831 err. emit ( ) ;
768832 }
769833
770- fn report_arg_count_mismatch ( & self ,
771- span : Span ,
772- found_span : Option < Span > ,
773- expected : usize ,
774- found : usize ,
775- is_closure : bool )
776- -> DiagnosticBuilder < ' tcx >
777- {
834+ fn report_arg_count_mismatch (
835+ & self ,
836+ span : Span ,
837+ found_span : Option < Span > ,
838+ expected : usize ,
839+ expected_tuple : Option < usize > ,
840+ found : usize ,
841+ closure_args : Option < ( Vec < String > , Vec < Option < String > > ) > ,
842+ is_closure : bool
843+ ) -> DiagnosticBuilder < ' tcx > {
844+ use std:: borrow:: Cow ;
845+
846+ let kind = if is_closure { "closure" } else { "function" } ;
847+
848+ let args_str = |n, distinct| format ! (
849+ "{} {}argument{}" ,
850+ n,
851+ if distinct && n >= 2 { "distinct " } else { "" } ,
852+ if n == 1 { "" } else { "s" } ,
853+ ) ;
854+
855+ let expected_str = if let Some ( n) = expected_tuple {
856+ assert ! ( expected == 1 ) ;
857+ if closure_args. as_ref ( ) . map ( |& ( ref pats, _) | pats. len ( ) ) == Some ( n) {
858+ Cow :: from ( "a single tuple as argument" )
859+ } else {
860+ // be verbose when numbers differ
861+ Cow :: from ( format ! ( "a single {}-tuple as argument" , n) )
862+ }
863+ } else {
864+ Cow :: from ( args_str ( expected, false ) )
865+ } ;
866+
867+ let found_str = if expected_tuple. is_some ( ) {
868+ args_str ( found, true )
869+ } else {
870+ args_str ( found, false )
871+ } ;
872+
873+
778874 let mut err = struct_span_err ! ( self . tcx. sess, span, E0593 ,
779- "{} takes {} argument{} but {} argument{} {} required" ,
780- if is_closure { "closure" } else { "function" } ,
781- found,
782- if found == 1 { "" } else { "s" } ,
783- expected,
784- if expected == 1 { "" } else { "s" } ,
785- if expected == 1 { "is" } else { "are" } ) ;
786-
787- err. span_label ( span, format ! ( "expected {} that takes {} argument{}" ,
788- if is_closure { "closure" } else { "function" } ,
789- expected,
790- if expected == 1 { "" } else { "s" } ) ) ;
875+ "{} is expected to take {}, but it takes {}" ,
876+ kind,
877+ expected_str,
878+ found_str,
879+ ) ;
880+
881+ err. span_label (
882+ span,
883+ format ! (
884+ "expected {} that takes {}" ,
885+ kind,
886+ expected_str,
887+ )
888+ ) ;
889+
791890 if let Some ( span) = found_span {
792- err. span_label ( span, format ! ( "takes {} argument{}" ,
793- found,
794- if found == 1 { "" } else { "s" } ) ) ;
891+ if let ( Some ( expected_tuple) , Some ( ( pats, tys) ) ) = ( expected_tuple, closure_args) {
892+ if expected_tuple != found || pats. len ( ) != found {
893+ err. span_label ( span, format ! ( "takes {}" , found_str) ) ;
894+ } else {
895+ let sugg = format ! (
896+ "|({}){}|" ,
897+ pats. join( ", " ) ,
898+
899+ // add type annotations if available
900+ if tys. iter( ) . any( |ty| ty. is_some( ) ) {
901+ Cow :: from( format!(
902+ ": ({})" ,
903+ tys. into_iter( ) . map( |ty| if let Some ( ty) = ty {
904+ ty
905+ } else {
906+ "_" . to_string( )
907+ } ) . collect:: <Vec <String >>( ) . join( ", " )
908+ ) )
909+ } else {
910+ Cow :: from( "" )
911+ } ,
912+ ) ;
913+
914+ err. span_suggestion (
915+ span,
916+ "consider changing the closure to accept a tuple" ,
917+ sugg
918+ ) ;
919+ }
920+ } else {
921+ err. span_label ( span, format ! ( "takes {}" , found_str) ) ;
922+ }
795923 }
924+
796925 err
797926 }
798927
0 commit comments