@@ -32,6 +32,7 @@ use rustc_hir as hir;
32
32
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
33
33
use rustc_hir:: def_id:: DefId ;
34
34
use rustc_hir:: intravisit:: Visitor ;
35
+ use rustc_hir:: lang_items:: LangItem ;
35
36
use rustc_hir:: { ExprKind , HirId , QPath } ;
36
37
use rustc_infer:: infer;
37
38
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
@@ -1556,7 +1557,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1556
1557
if inaccessible_remaining_fields {
1557
1558
self . report_inaccessible_fields ( adt_ty, span) ;
1558
1559
} else {
1559
- self . report_missing_fields ( adt_ty, span, remaining_fields) ;
1560
+ self . report_missing_fields (
1561
+ adt_ty,
1562
+ span,
1563
+ remaining_fields,
1564
+ variant,
1565
+ ast_fields,
1566
+ substs,
1567
+ ) ;
1560
1568
}
1561
1569
}
1562
1570
}
@@ -1590,6 +1598,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1590
1598
adt_ty : Ty < ' tcx > ,
1591
1599
span : Span ,
1592
1600
remaining_fields : FxHashMap < Ident , ( usize , & ty:: FieldDef ) > ,
1601
+ variant : & ' tcx ty:: VariantDef ,
1602
+ ast_fields : & ' tcx [ hir:: ExprField < ' tcx > ] ,
1603
+ substs : SubstsRef < ' tcx > ,
1593
1604
) {
1594
1605
let len = remaining_fields. len ( ) ;
1595
1606
@@ -1615,7 +1626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1615
1626
}
1616
1627
} ;
1617
1628
1618
- struct_span_err ! (
1629
+ let mut err = struct_span_err ! (
1619
1630
self . tcx. sess,
1620
1631
span,
1621
1632
E0063 ,
@@ -1624,9 +1635,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1624
1635
remaining_fields_names,
1625
1636
truncated_fields_error,
1626
1637
adt_ty
1627
- )
1628
- . span_label ( span, format ! ( "missing {}{}" , remaining_fields_names, truncated_fields_error) )
1629
- . emit ( ) ;
1638
+ ) ;
1639
+ err. span_label (
1640
+ span,
1641
+ format ! ( "missing {}{}" , remaining_fields_names, truncated_fields_error) ,
1642
+ ) ;
1643
+
1644
+ // If the last field is a range literal, but it isn't supposed to be, then they probably
1645
+ // meant to use functional update syntax.
1646
+ //
1647
+ // I don't use 'is_range_literal' because only double-sided, half-open ranges count.
1648
+ if let Some ( (
1649
+ last,
1650
+ ExprKind :: Struct (
1651
+ QPath :: LangItem ( LangItem :: Range , ..) ,
1652
+ & [ ref range_start, ref range_end] ,
1653
+ _,
1654
+ ) ,
1655
+ ) ) = ast_fields. last ( ) . map ( |last| ( last, & last. expr . kind ) ) &&
1656
+ let variant_field =
1657
+ variant. fields . iter ( ) . find ( |field| field. ident ( self . tcx ) == last. ident ) &&
1658
+ let range_def_id = self . tcx . lang_items ( ) . range_struct ( ) &&
1659
+ variant_field
1660
+ . and_then ( |field| field. ty ( self . tcx , substs) . ty_adt_def ( ) )
1661
+ . map ( |adt| adt. did ( ) )
1662
+ != range_def_id
1663
+ {
1664
+ let instead = self
1665
+ . tcx
1666
+ . sess
1667
+ . source_map ( )
1668
+ . span_to_snippet ( range_end. expr . span )
1669
+ . map ( |s| format ! ( " from `{s}`" ) )
1670
+ . unwrap_or ( String :: new ( ) ) ;
1671
+ err. span_suggestion (
1672
+ range_start. span . shrink_to_hi ( ) ,
1673
+ & format ! ( "to set the remaining fields{instead}, separate the last named field with a comma" ) ,
1674
+ "," . to_string ( ) ,
1675
+ Applicability :: MaybeIncorrect ,
1676
+ ) ;
1677
+ }
1678
+
1679
+ err. emit ( ) ;
1630
1680
}
1631
1681
1632
1682
/// Report an error for a struct field expression when there are invisible fields.
0 commit comments