@@ -717,93 +717,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
717
717
self . tcx . hir . span_if_local ( did)
718
718
} ) . map ( |sp| self . tcx . sess . codemap ( ) . def_span ( sp) ) ; // the sp could be an fn def
719
719
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 ( ) {
732
739
self . report_closure_arg_mismatch ( span,
733
740
found_span,
734
741
found_trait_ref,
735
742
expected_trait_ref)
736
743
} 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
761
745
. 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 ( ) )
807
754
}
808
755
}
809
756
@@ -845,94 +792,135 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
845
792
}
846
793
}
847
794
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
+
848
841
fn report_arg_count_mismatch (
849
842
& self ,
850
843
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 ,
857
848
) -> DiagnosticBuilder < ' tcx > {
858
- use std:: borrow:: Cow ;
859
-
860
849
let kind = if is_closure { "closure" } else { "function" } ;
861
850
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" } ) ,
876
865
}
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 )
885
866
} ;
886
867
868
+ let expected_str = args_str ( & expected_args, & found_args) ;
869
+ let found_str = args_str ( & found_args, & expected_args) ;
887
870
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 ,
889
875
"{} is expected to take {}, but it takes {}" ,
890
876
kind,
891
877
expected_str,
892
878
found_str,
893
879
) ;
894
880
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) ;
936
924
}
937
925
}
938
926
@@ -1325,3 +1313,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
1325
1313
suggested_limit) ) ;
1326
1314
}
1327
1315
}
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