@@ -717,93 +717,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
717717 self . tcx . hir . span_if_local ( did)
718718 } ) . map ( |sp| self . tcx . sess . codemap ( ) . def_span ( sp) ) ; // the sp could be an fn def
719719
720- let found_ty_count =
721- match found_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
722- ty:: TyTuple ( ref tys, _) => tys. len ( ) ,
723- _ => 1 ,
724- } ;
725- let ( expected_tys, expected_ty_count) =
726- match expected_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
727- ty:: TyTuple ( ref tys, _) =>
728- ( tys. iter ( ) . map ( |t| & t. sty ) . collect ( ) , tys. len ( ) ) ,
729- ref sty => ( vec ! [ sty] , 1 ) ,
730- } ;
731- if found_ty_count == expected_ty_count {
720+ let found = match found_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
721+ ty:: TyTuple ( ref tys, _) => tys. iter ( )
722+ . map ( |_| ArgKind :: empty ( ) ) . collect :: < Vec < _ > > ( ) ,
723+ _ => vec ! [ ArgKind :: empty( ) ] ,
724+ } ;
725+ let expected = match expected_trait_ref. skip_binder ( ) . substs . type_at ( 1 ) . sty {
726+ ty:: TyTuple ( ref tys, _) => tys. iter ( )
727+ . map ( |t| match t. sty {
728+ ty:: TypeVariants :: TyTuple ( ref tys, _) => ArgKind :: Tuple (
729+ span,
730+ tys. iter ( )
731+ . map ( |ty| ( "_" . to_owned ( ) , format ! ( "{}" , ty. sty) ) )
732+ . collect :: < Vec < _ > > ( )
733+ ) ,
734+ _ => ArgKind :: Arg ( "_" . to_owned ( ) , format ! ( "{}" , t. sty) ) ,
735+ } ) . collect ( ) ,
736+ ref sty => vec ! [ ArgKind :: Arg ( "_" . to_owned( ) , format!( "{}" , sty) ) ] ,
737+ } ;
738+ if found. len ( ) == expected. len ( ) {
732739 self . report_closure_arg_mismatch ( span,
733740 found_span,
734741 found_trait_ref,
735742 expected_trait_ref)
736743 } else {
737- let expected_tuple = if expected_ty_count == 1 {
738- expected_tys. first ( ) . and_then ( |t| {
739- if let & & ty:: TyTuple ( ref tuptys, _) = t {
740- Some ( tuptys. len ( ) )
741- } else {
742- None
743- }
744- } )
745- } else {
746- None
747- } ;
748-
749- // FIXME(#44150): Expand this to "N args expected but a N-tuple found."
750- // Type of the 1st expected argument is somehow provided as type of a
751- // found one in that case.
752- //
753- // ```
754- // [1i32, 2, 3].sort_by(|(a, b)| ..)
755- // // ^^^^^^^ --------
756- // // expected_trait_ref: std::ops::FnMut<(&i32, &i32)>
757- // // found_trait_ref: std::ops::FnMut<(&i32,)>
758- // ```
759-
760- let ( closure_span, closure_args) = found_did
744+ let ( closure_span, found) = found_did
761745 . and_then ( |did| self . tcx . hir . get_if_local ( did) )
762- . and_then ( |node| {
763- if let hir:: map:: NodeExpr (
764- & hir:: Expr {
765- node : hir:: ExprClosure ( _, ref decl, id, span, _) ,
766- ..
767- } ) = node
768- {
769- let ty_snips = decl. inputs . iter ( )
770- . map ( |ty| {
771- self . tcx . sess . codemap ( ) . span_to_snippet ( ty. span ) . ok ( )
772- . and_then ( |snip| {
773- // filter out dummy spans
774- if snip == "," || snip == "|" {
775- None
776- } else {
777- Some ( snip)
778- }
779- } )
780- } )
781- . collect :: < Vec < Option < String > > > ( ) ;
782-
783- let body = self . tcx . hir . body ( id) ;
784- let pat_snips = body. arguments . iter ( )
785- . map ( |arg|
786- self . tcx . sess . codemap ( ) . span_to_snippet ( arg. pat . span ) . ok ( ) )
787- . collect :: < Option < Vec < String > > > ( ) ;
788-
789- Some ( ( span, pat_snips, ty_snips) )
790- } else {
791- None
792- }
793- } )
794- . map ( |( span, pat, ty) | ( Some ( span) , Some ( ( pat, ty) ) ) )
795- . unwrap_or ( ( None , None ) ) ;
796- let closure_args = closure_args. and_then ( |( pat, ty) | Some ( ( pat?, ty) ) ) ;
797-
798- self . report_arg_count_mismatch (
799- span,
800- closure_span. or ( found_span) ,
801- expected_ty_count,
802- expected_tuple,
803- found_ty_count,
804- closure_args,
805- found_trait_ty. is_closure ( )
806- )
746+ . map ( |node| self . get_fn_like_arguments ( node) )
747+ . unwrap_or ( ( found_span. unwrap ( ) , found) ) ;
748+
749+ self . report_arg_count_mismatch ( span,
750+ closure_span,
751+ expected,
752+ found,
753+ found_trait_ty. is_closure ( ) )
807754 }
808755 }
809756
@@ -845,94 +792,135 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
845792 }
846793 }
847794
795+ fn get_fn_like_arguments ( & self , node : hir:: map:: Node ) -> ( Span , Vec < ArgKind > ) {
796+ if let hir:: map:: NodeExpr ( & hir:: Expr {
797+ node : hir:: ExprClosure ( _, ref _decl, id, span, _) ,
798+ ..
799+ } ) = node {
800+ ( self . tcx . sess . codemap ( ) . def_span ( span) , self . tcx . hir . body ( id) . arguments . iter ( )
801+ . map ( |arg| {
802+ if let hir:: Pat {
803+ node : hir:: PatKind :: Tuple ( args, _) ,
804+ span,
805+ ..
806+ } = arg. pat . clone ( ) . into_inner ( ) {
807+ ArgKind :: Tuple (
808+ span,
809+ args. iter ( ) . map ( |pat| {
810+ let snippet = self . tcx . sess . codemap ( )
811+ . span_to_snippet ( pat. span ) . unwrap ( ) ;
812+ ( snippet, "_" . to_owned ( ) )
813+ } ) . collect :: < Vec < _ > > ( ) ,
814+ )
815+ } else {
816+ let name = self . tcx . sess . codemap ( ) . span_to_snippet ( arg. pat . span ) . unwrap ( ) ;
817+ ArgKind :: Arg ( name, "_" . to_owned ( ) )
818+ }
819+ } )
820+ . collect :: < Vec < ArgKind > > ( ) )
821+ } else if let hir:: map:: NodeItem ( & hir:: Item {
822+ span,
823+ node : hir:: ItemFn ( ref decl, ..) ,
824+ ..
825+ } ) = node {
826+ ( self . tcx . sess . codemap ( ) . def_span ( span) , decl. inputs . iter ( )
827+ . map ( |arg| match arg. clone ( ) . into_inner ( ) . node {
828+ hir:: TyTup ( ref tys) => ArgKind :: Tuple (
829+ arg. span ,
830+ tys. iter ( )
831+ . map ( |_| ( "_" . to_owned ( ) , "_" . to_owned ( ) ) )
832+ . collect :: < Vec < _ > > ( ) ,
833+ ) ,
834+ _ => ArgKind :: Arg ( "_" . to_owned ( ) , "_" . to_owned ( ) )
835+ } ) . collect :: < Vec < ArgKind > > ( ) )
836+ } else {
837+ panic ! ( "non-FnLike node found: {:?}" , node) ;
838+ }
839+ }
840+
848841 fn report_arg_count_mismatch (
849842 & self ,
850843 span : Span ,
851- found_span : Option < Span > ,
852- expected : usize ,
853- expected_tuple : Option < usize > ,
854- found : usize ,
855- closure_args : Option < ( Vec < String > , Vec < Option < String > > ) > ,
856- is_closure : bool
844+ found_span : Span ,
845+ expected_args : Vec < ArgKind > ,
846+ found_args : Vec < ArgKind > ,
847+ is_closure : bool ,
857848 ) -> DiagnosticBuilder < ' tcx > {
858- use std:: borrow:: Cow ;
859-
860849 let kind = if is_closure { "closure" } else { "function" } ;
861850
862- let args_str = |n , distinct| format ! (
863- "{} {}argument{}" ,
864- n ,
865- if distinct && n >= 2 { "distinct " } else { "" } ,
866- if n == 1 { "" } else { "s" } ,
867- ) ;
868-
869- let expected_str = if let Some ( n ) = expected_tuple {
870- assert ! ( expected == 1 ) ;
871- if closure_args . as_ref ( ) . map ( | & ( ref pats , _ ) | pats . len ( ) ) == Some ( n ) {
872- Cow :: from ( "a single tuple as argument" )
873- } else {
874- // be verbose when numbers differ
875- Cow :: from ( format ! ( "a single {}-tuple as argument" , n ) )
851+ let args_str = |arguments : & Vec < ArgKind > , other : & Vec < ArgKind > | {
852+ let arg_length = arguments . len ( ) ;
853+ let distinct = match & other [ .. ] {
854+ & [ ArgKind :: Tuple ( .. ) ] => true ,
855+ _ => false ,
856+ } ;
857+ match ( arg_length , arguments . get ( 0 ) ) {
858+ ( 1 , Some ( & ArgKind :: Tuple ( _ , ref fields ) ) ) => {
859+ format ! ( "a single {}-tuple as argument" , fields . len ( ) )
860+ }
861+ _ => format ! ( "{} {} argument{}" ,
862+ arg_length ,
863+ if distinct && arg_length > 1 { "distinct " } else { "" } ,
864+ if arg_length == 1 { "" } else { "s" } ) ,
876865 }
877- } else {
878- Cow :: from ( args_str ( expected, false ) )
879- } ;
880-
881- let found_str = if expected_tuple. is_some ( ) {
882- args_str ( found, true )
883- } else {
884- args_str ( found, false )
885866 } ;
886867
868+ let expected_str = args_str ( & expected_args, & found_args) ;
869+ let found_str = args_str ( & found_args, & expected_args) ;
887870
888- let mut err = struct_span_err ! ( self . tcx. sess, span, E0593 ,
871+ let mut err = struct_span_err ! (
872+ self . tcx. sess,
873+ span,
874+ E0593 ,
889875 "{} is expected to take {}, but it takes {}" ,
890876 kind,
891877 expected_str,
892878 found_str,
893879 ) ;
894880
895- err. span_label (
896- span,
897- format ! (
898- "expected {} that takes {}" ,
899- kind,
900- expected_str,
901- )
902- ) ;
903-
904- if let Some ( span) = found_span {
905- if let ( Some ( expected_tuple) , Some ( ( pats, tys) ) ) = ( expected_tuple, closure_args) {
906- if expected_tuple != found || pats. len ( ) != found {
907- err. span_label ( span, format ! ( "takes {}" , found_str) ) ;
908- } else {
909- let sugg = format ! (
910- "|({}){}|" ,
911- pats. join( ", " ) ,
912-
913- // add type annotations if available
914- if tys. iter( ) . any( |ty| ty. is_some( ) ) {
915- Cow :: from( format!(
916- ": ({})" ,
917- tys. into_iter( ) . map( |ty| if let Some ( ty) = ty {
918- ty
919- } else {
920- "_" . to_string( )
921- } ) . collect:: <Vec <String >>( ) . join( ", " )
922- ) )
923- } else {
924- Cow :: from( "" )
925- } ,
926- ) ;
927-
928- err. span_suggestion (
929- span,
930- "consider changing the closure to accept a tuple" ,
931- sugg
932- ) ;
933- }
934- } else {
935- err. span_label ( span, format ! ( "takes {}" , found_str) ) ;
881+ err. span_label ( span, format ! ( "expected {} that takes {}" , kind, expected_str) ) ;
882+ err. span_label ( found_span, format ! ( "takes {}" , found_str) ) ;
883+
884+ if let & [ ArgKind :: Tuple ( _, ref fields) ] = & found_args[ ..] {
885+ if fields. len ( ) == expected_args. len ( ) {
886+ let sugg = fields. iter ( )
887+ . map ( |( name, _) | name. to_owned ( ) )
888+ . collect :: < Vec < String > > ( ) . join ( ", " ) ;
889+ err. span_suggestion ( found_span,
890+ "change the closure to take multiple arguments instead of \
891+ a single tuple",
892+ format ! ( "|{}|" , sugg) ) ;
893+ }
894+ }
895+ if let & [ ArgKind :: Tuple ( _, ref fields) ] = & expected_args[ ..] {
896+ if fields. len ( ) == found_args. len ( ) && is_closure {
897+ let sugg = format ! (
898+ "|({}){}|" ,
899+ found_args. iter( )
900+ . map( |arg| match arg {
901+ ArgKind :: Arg ( name, _) => name. to_owned( ) ,
902+ _ => "_" . to_owned( ) ,
903+ } )
904+ . collect:: <Vec <String >>( )
905+ . join( ", " ) ,
906+ // add type annotations if available
907+ if found_args. iter( ) . any( |arg| match arg {
908+ ArgKind :: Arg ( _, ty) => ty != "_" ,
909+ _ => false ,
910+ } ) {
911+ format!( ": ({})" ,
912+ fields. iter( )
913+ . map( |( _, ty) | ty. to_owned( ) )
914+ . collect:: <Vec <String >>( )
915+ . join( ", " ) )
916+ } else {
917+ "" . to_owned( )
918+ } ,
919+ ) ;
920+ err. span_suggestion ( found_span,
921+ "change the closure to accept a tuple instead of individual \
922+ arguments",
923+ sugg) ;
936924 }
937925 }
938926
@@ -1325,3 +1313,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
13251313 suggested_limit) ) ;
13261314 }
13271315}
1316+
1317+ enum ArgKind {
1318+ Arg ( String , String ) ,
1319+ Tuple ( Span , Vec < ( String , String ) > ) ,
1320+ }
1321+
1322+ impl ArgKind {
1323+ fn empty ( ) -> ArgKind {
1324+ ArgKind :: Arg ( "_" . to_owned ( ) , "_" . to_owned ( ) )
1325+ }
1326+ }
0 commit comments