@@ -15,7 +15,7 @@ use rustc_span::hygiene::DesugaringKind;
1515use rustc_span:: lev_distance:: find_best_match_for_name;
1616use rustc_span:: source_map:: { Span , Spanned } ;
1717use rustc_span:: symbol:: Ident ;
18- use rustc_span:: { BytePos , DUMMY_SP } ;
18+ use rustc_span:: { BytePos , MultiSpan , DUMMY_SP } ;
1919use rustc_trait_selection:: autoderef:: Autoderef ;
2020use rustc_trait_selection:: traits:: { ObligationCause , Pattern } ;
2121use ty:: VariantDef ;
@@ -990,11 +990,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
990990 ) {
991991 let subpats_ending = pluralize ! ( subpats. len( ) ) ;
992992 let fields_ending = pluralize ! ( fields. len( ) ) ;
993- let fields_span = pat_span. trim_start ( qpath. span ( ) ) . unwrap_or ( pat_span) ;
993+
994+ let subpat_spans = if subpats. is_empty ( ) {
995+ vec ! [ pat_span. trim_start( qpath. span( ) ) . unwrap_or( pat_span) ]
996+ } else {
997+ subpats. iter ( ) . map ( |p| p. span ) . collect ( )
998+ } ;
999+ let last_subpat_span = * subpat_spans. last ( ) . unwrap ( ) ;
9941000 let res_span = self . tcx . def_span ( res. def_id ( ) ) ;
1001+ let def_ident_span = self . tcx . def_ident_span ( res. def_id ( ) ) . unwrap_or ( res_span) ;
1002+ let field_def_spans = if fields. is_empty ( ) {
1003+ vec ! [ res_span. trim_start( def_ident_span) . unwrap_or( res_span) ]
1004+ } else {
1005+ fields. iter ( ) . map ( |f| f. ident . span ) . collect ( )
1006+ } ;
1007+ let last_field_def_span = * field_def_spans. last ( ) . unwrap ( ) ;
1008+
9951009 let mut err = struct_span_err ! (
9961010 self . tcx. sess,
997- fields_span ,
1011+ MultiSpan :: from_spans ( subpat_spans . clone ( ) ) ,
9981012 E0023 ,
9991013 "this pattern has {} field{}, but the corresponding {} has {} field{}" ,
10001014 subpats. len( ) ,
@@ -1004,11 +1018,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10041018 fields_ending,
10051019 ) ;
10061020 err. span_label (
1007- fields_span,
1008- format ! ( "expected {} field{}, found {}" , fields. len( ) , fields_ending, subpats. len( ) , ) ,
1009- )
1010- . span_label ( qpath. span ( ) , format ! ( "this {}" , res. descr( ) ) )
1011- . span_label ( res_span, format ! ( "{} defined here" , res. descr( ) ) ) ;
1021+ last_subpat_span,
1022+ & format ! ( "expected {} field{}, found {}" , fields. len( ) , fields_ending, subpats. len( ) ) ,
1023+ ) ;
1024+ err. span_label ( qpath. span ( ) , "" ) ;
1025+ if self . tcx . sess . source_map ( ) . is_multiline ( def_ident_span. between ( field_def_spans[ 0 ] ) ) {
1026+ err. span_label ( def_ident_span, format ! ( "{} defined here" , res. descr( ) ) ) ;
1027+ }
1028+ for span in & field_def_spans[ ..field_def_spans. len ( ) - 1 ] {
1029+ err. span_label ( * span, "" ) ;
1030+ }
1031+ err. span_label (
1032+ last_field_def_span,
1033+ & format ! ( "{} has {} field{}" , res. descr( ) , fields. len( ) , fields_ending) ,
1034+ ) ;
10121035
10131036 // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`.
10141037 // More generally, the expected type wants a tuple variant with one field of an
0 commit comments