Skip to content

Commit 4103238

Browse files
Rollup merge of rust-lang#59266 - estebank:struct-parse-recovery, r=petrochenkov
Do not complain about non-existing fields after parse recovery When failing to parse struct-like enum variants, the ADT gets recorded as having no fields. Record that we have actually recovered during parsing of this variant to avoid complaing about non-existing fields when actually using it. Fix rust-lang#57361.
2 parents 20211eb + 757eb67 commit 4103238

File tree

15 files changed

+111
-58
lines changed

15 files changed

+111
-58
lines changed

src/librustc/hir/lowering.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2669,7 +2669,7 @@ impl<'a> LoweringContext<'a> {
26692669

26702670
fn lower_variant_data(&mut self, vdata: &VariantData) -> hir::VariantData {
26712671
match *vdata {
2672-
VariantData::Struct(ref fields, id) => {
2672+
VariantData::Struct(ref fields, id, recovered) => {
26732673
let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(id);
26742674

26752675
hir::VariantData::Struct(
@@ -2679,6 +2679,7 @@ impl<'a> LoweringContext<'a> {
26792679
.map(|f| self.lower_struct_field(f))
26802680
.collect(),
26812681
hir_id,
2682+
recovered,
26822683
)
26832684
},
26842685
VariantData::Tuple(ref fields, id) => {

src/librustc/hir/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,7 @@ impl StructField {
21722172
/// Id of the whole struct lives in `Item`.
21732173
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
21742174
pub enum VariantData {
2175-
Struct(HirVec<StructField>, HirId),
2175+
Struct(HirVec<StructField>, HirId, /* recovered */ bool),
21762176
Tuple(HirVec<StructField>, HirId),
21772177
Unit(HirId),
21782178
}
@@ -2186,7 +2186,7 @@ impl VariantData {
21862186
}
21872187
pub fn hir_id(&self) -> HirId {
21882188
match *self {
2189-
VariantData::Struct(_, hir_id)
2189+
VariantData::Struct(_, hir_id, _)
21902190
| VariantData::Tuple(_, hir_id)
21912191
| VariantData::Unit(hir_id) => hir_id,
21922192
}

src/librustc/ty/mod.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,7 @@ pub struct VariantDef {
18111811
pub fields: Vec<FieldDef>,
18121812
pub ctor_kind: CtorKind,
18131813
flags: VariantFlags,
1814+
pub recovered: bool,
18141815
}
18151816

18161817
impl<'a, 'gcx, 'tcx> VariantDef {
@@ -1829,16 +1830,17 @@ impl<'a, 'gcx, 'tcx> VariantDef {
18291830
///
18301831
/// If someone speeds up attribute loading to not be a performance concern, they can
18311832
/// remove this hack and use the constructor `DefId` everywhere.
1832-
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
1833-
did: DefId,
1834-
ident: Ident,
1835-
discr: VariantDiscr,
1836-
fields: Vec<FieldDef>,
1837-
adt_kind: AdtKind,
1838-
ctor_kind: CtorKind,
1839-
attribute_def_id: DefId)
1840-
-> Self
1841-
{
1833+
pub fn new(
1834+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1835+
did: DefId,
1836+
ident: Ident,
1837+
discr: VariantDiscr,
1838+
fields: Vec<FieldDef>,
1839+
adt_kind: AdtKind,
1840+
ctor_kind: CtorKind,
1841+
attribute_def_id: DefId,
1842+
recovered: bool,
1843+
) -> Self {
18421844
debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, ident, discr,
18431845
fields, adt_kind, ctor_kind, attribute_def_id);
18441846
let mut flags = VariantFlags::NO_VARIANT_FLAGS;
@@ -1852,7 +1854,8 @@ impl<'a, 'gcx, 'tcx> VariantDef {
18521854
discr,
18531855
fields,
18541856
ctor_kind,
1855-
flags
1857+
flags,
1858+
recovered,
18561859
}
18571860
}
18581861

@@ -1868,7 +1871,8 @@ impl_stable_hash_for!(struct VariantDef {
18681871
discr,
18691872
fields,
18701873
ctor_kind,
1871-
flags
1874+
flags,
1875+
recovered
18721876
});
18731877

18741878
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)]

src/librustc_metadata/decoder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,8 @@ impl<'a, 'tcx> CrateMetadata {
576576
}).collect(),
577577
adt_kind,
578578
data.ctor_kind,
579-
attribute_def_id
579+
attribute_def_id,
580+
false,
580581
)
581582
}
582583

src/librustc_save_analysis/dump_visitor.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
488488
};
489489

490490
let (value, fields) = match item.node {
491-
ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) |
492-
ast::ItemKind::Union(ast::VariantData::Struct(ref fields, _), _) => {
491+
ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, ..), _) |
492+
ast::ItemKind::Union(ast::VariantData::Struct(ref fields, ..), _) => {
493493
let include_priv_fields = !self.save_ctxt.config.pub_only;
494494
let fields_str = fields
495495
.iter()
@@ -569,7 +569,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
569569
let name_span = variant.node.ident.span;
570570

571571
match variant.node.data {
572-
ast::VariantData::Struct(ref fields, _) => {
572+
ast::VariantData::Struct(ref fields, ..) => {
573573
let fields_str = fields
574574
.iter()
575575
.enumerate()

src/librustc_save_analysis/sig.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ impl Sig for ast::Variant_ {
703703
fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext<'_, '_>) -> Result {
704704
let mut text = self.ident.to_string();
705705
match self.data {
706-
ast::VariantData::Struct(ref fields, id) => {
706+
ast::VariantData::Struct(ref fields, id, r) => {
707707
let name_def = SigElement {
708708
id: id_from_node_id(id, scx),
709709
start: offset,
@@ -712,12 +712,16 @@ impl Sig for ast::Variant_ {
712712
text.push_str(" { ");
713713
let mut defs = vec![name_def];
714714
let mut refs = vec![];
715-
for f in fields {
716-
let field_sig = f.make(offset + text.len(), Some(id), scx)?;
717-
text.push_str(&field_sig.text);
718-
text.push_str(", ");
719-
defs.extend(field_sig.defs.into_iter());
720-
refs.extend(field_sig.refs.into_iter());
715+
if r {
716+
text.push_str("/* parse error */ ");
717+
} else {
718+
for f in fields {
719+
let field_sig = f.make(offset + text.len(), Some(id), scx)?;
720+
text.push_str(&field_sig.text);
721+
text.push_str(", ");
722+
defs.extend(field_sig.defs.into_iter());
723+
refs.extend(field_sig.refs.into_iter());
724+
}
721725
}
722726
text.push('}');
723727
Ok(Signature { text, defs, refs })

src/librustc_typeck/check/_match.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -918,14 +918,16 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
918918
pat_ty
919919
}
920920

921-
fn check_struct_pat_fields(&self,
922-
adt_ty: Ty<'tcx>,
923-
pat_id: hir::HirId,
924-
span: Span,
925-
variant: &'tcx ty::VariantDef,
926-
fields: &'gcx [Spanned<hir::FieldPat>],
927-
etc: bool,
928-
def_bm: ty::BindingMode) -> bool {
921+
fn check_struct_pat_fields(
922+
&self,
923+
adt_ty: Ty<'tcx>,
924+
pat_id: hir::HirId,
925+
span: Span,
926+
variant: &'tcx ty::VariantDef,
927+
fields: &'gcx [Spanned<hir::FieldPat>],
928+
etc: bool,
929+
def_bm: ty::BindingMode,
930+
) -> bool {
929931
let tcx = self.tcx;
930932

931933
let (substs, adt) = match adt_ty.sty {
@@ -985,7 +987,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
985987
.map(|field| field.ident.modern())
986988
.filter(|ident| !used_fields.contains_key(&ident))
987989
.collect::<Vec<_>>();
988-
if inexistent_fields.len() > 0 {
990+
if inexistent_fields.len() > 0 && !variant.recovered {
989991
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
990992
(format!("a field named `{}`", inexistent_fields[0]), "this", "")
991993
} else {

src/librustc_typeck/check/mod.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,12 +3734,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
37343734
field, expr_t)
37353735
}
37363736

3737-
fn report_unknown_field(&self,
3738-
ty: Ty<'tcx>,
3739-
variant: &'tcx ty::VariantDef,
3740-
field: &hir::Field,
3741-
skip_fields: &[hir::Field],
3742-
kind_name: &str) {
3737+
fn report_unknown_field(
3738+
&self,
3739+
ty: Ty<'tcx>,
3740+
variant: &'tcx ty::VariantDef,
3741+
field: &hir::Field,
3742+
skip_fields: &[hir::Field],
3743+
kind_name: &str,
3744+
) {
3745+
if variant.recovered {
3746+
return;
3747+
}
37433748
let mut err = self.type_error_struct_with_diag(
37443749
field.ident.span,
37453750
|actual| match ty.sty {

src/librustc_typeck/collect.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,14 +598,19 @@ fn convert_variant<'a, 'tcx>(
598598
}
599599
})
600600
.collect();
601+
let recovered = match def {
602+
hir::VariantData::Struct(_, _, r) => *r,
603+
_ => false,
604+
};
601605
ty::VariantDef::new(tcx,
602606
did,
603607
ident,
604608
discr,
605609
fields,
606610
adt_kind,
607611
CtorKind::from_hir(def),
608-
attribute_def_id
612+
attribute_def_id,
613+
recovered,
609614
)
610615
}
611616

src/libsyntax/ast.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ pub enum PatKind {
620620

621621
/// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
622622
/// The `bool` is `true` in the presence of a `..`.
623-
Struct(Path, Vec<Spanned<FieldPat>>, bool),
623+
Struct(Path, Vec<Spanned<FieldPat>>, /* recovered */ bool),
624624

625625
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
626626
/// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
@@ -2133,7 +2133,7 @@ pub enum VariantData {
21332133
/// Struct variant.
21342134
///
21352135
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
2136-
Struct(Vec<StructField>, NodeId),
2136+
Struct(Vec<StructField>, NodeId, bool),
21372137
/// Tuple variant.
21382138
///
21392139
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
@@ -2147,13 +2147,13 @@ pub enum VariantData {
21472147
impl VariantData {
21482148
pub fn fields(&self) -> &[StructField] {
21492149
match *self {
2150-
VariantData::Struct(ref fields, _) | VariantData::Tuple(ref fields, _) => fields,
2150+
VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, _) => fields,
21512151
_ => &[],
21522152
}
21532153
}
21542154
pub fn id(&self) -> NodeId {
21552155
match *self {
2156-
VariantData::Struct(_, id) | VariantData::Tuple(_, id) | VariantData::Unit(id) => id,
2156+
VariantData::Struct(_, id, _) | VariantData::Tuple(_, id) | VariantData::Unit(id) => id,
21572157
}
21582158
}
21592159
pub fn is_struct(&self) -> bool {

src/libsyntax/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ impl<'a> StripUnconfigured<'a> {
225225

226226
fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
227227
match vdata {
228-
ast::VariantData::Struct(fields, _id) |
228+
ast::VariantData::Struct(fields, _id, _) |
229229
ast::VariantData::Tuple(fields, _id) =>
230230
fields.flat_map_in_place(|field| self.configure(field)),
231231
ast::VariantData::Unit(_id) => {}

src/libsyntax/mut_visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
765765

766766
pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
767767
match vdata {
768-
VariantData::Struct(fields, id) |
768+
VariantData::Struct(fields, id, _) |
769769
VariantData::Tuple(fields, id) => {
770770
visit_vec(fields, |field| vis.visit_struct_field(field));
771771
vis.visit_id(id);

src/libsyntax/parse/parser.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6838,14 +6838,16 @@ impl<'a> Parser<'a> {
68386838
VariantData::Unit(ast::DUMMY_NODE_ID)
68396839
} else {
68406840
// If we see: `struct Foo<T> where T: Copy { ... }`
6841-
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID)
6841+
let (fields, recovered) = self.parse_record_struct_body()?;
6842+
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
68426843
}
68436844
// No `where` so: `struct Foo<T>;`
68446845
} else if self.eat(&token::Semi) {
68456846
VariantData::Unit(ast::DUMMY_NODE_ID)
68466847
// Record-style struct definition
68476848
} else if self.token == token::OpenDelim(token::Brace) {
6848-
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID)
6849+
let (fields, recovered) = self.parse_record_struct_body()?;
6850+
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
68496851
// Tuple-style struct definition with optional where-clause.
68506852
} else if self.token == token::OpenDelim(token::Paren) {
68516853
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, ast::DUMMY_NODE_ID);
@@ -6873,9 +6875,11 @@ impl<'a> Parser<'a> {
68736875

68746876
let vdata = if self.token.is_keyword(keywords::Where) {
68756877
generics.where_clause = self.parse_where_clause()?;
6876-
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID)
6878+
let (fields, recovered) = self.parse_record_struct_body()?;
6879+
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
68776880
} else if self.token == token::OpenDelim(token::Brace) {
6878-
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID)
6881+
let (fields, recovered) = self.parse_record_struct_body()?;
6882+
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
68796883
} else {
68806884
let token_str = self.this_token_descr();
68816885
let mut err = self.fatal(&format!(
@@ -6907,12 +6911,16 @@ impl<'a> Parser<'a> {
69076911
}
69086912
}
69096913

6910-
fn parse_record_struct_body(&mut self) -> PResult<'a, Vec<StructField>> {
6914+
fn parse_record_struct_body(
6915+
&mut self,
6916+
) -> PResult<'a, (Vec<StructField>, /* recovered */ bool)> {
69116917
let mut fields = Vec::new();
6918+
let mut recovered = false;
69126919
if self.eat(&token::OpenDelim(token::Brace)) {
69136920
while self.token != token::CloseDelim(token::Brace) {
69146921
let field = self.parse_struct_decl_field().map_err(|e| {
69156922
self.recover_stmt();
6923+
recovered = true;
69166924
e
69176925
});
69186926
match field {
@@ -6931,7 +6939,7 @@ impl<'a> Parser<'a> {
69316939
return Err(err);
69326940
}
69336941

6934-
Ok(fields)
6942+
Ok((fields, recovered))
69356943
}
69366944

69376945
fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> {
@@ -7694,12 +7702,14 @@ impl<'a> Parser<'a> {
76947702
if self.check(&token::OpenDelim(token::Brace)) {
76957703
// Parse a struct variant.
76967704
all_nullary = false;
7697-
struct_def = VariantData::Struct(self.parse_record_struct_body()?,
7698-
ast::DUMMY_NODE_ID);
7705+
let (fields, recovered) = self.parse_record_struct_body()?;
7706+
struct_def = VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered);
76997707
} else if self.check(&token::OpenDelim(token::Paren)) {
77007708
all_nullary = false;
7701-
struct_def = VariantData::Tuple(self.parse_tuple_struct_body()?,
7702-
ast::DUMMY_NODE_ID);
7709+
struct_def = VariantData::Tuple(
7710+
self.parse_tuple_struct_body()?,
7711+
ast::DUMMY_NODE_ID,
7712+
);
77037713
} else if self.eat(&token::Eq) {
77047714
disr_expr = Some(AnonConst {
77057715
id: ast::DUMMY_NODE_ID,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
enum Foo {
2+
A { a, b: usize }
3+
//~^ ERROR expected `:`, found `,`
4+
}
5+
6+
fn main() {
7+
// no complaints about non-existing fields
8+
let f = Foo::A { a:3, b: 4};
9+
match f {
10+
// no complaints about non-existing fields
11+
Foo::A {a, b} => {}
12+
}
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected `:`, found `,`
2+
--> $DIR/recovered-struct-variant.rs:2:10
3+
|
4+
LL | A { a, b: usize }
5+
| ^ expected `:`
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)