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.
This commit is contained in:
Esteban Küber 2019-03-17 20:09:53 -07:00
parent 7cf074a1e6
commit 6007e6f649
15 changed files with 108 additions and 57 deletions

View File

@ -2672,7 +2672,7 @@ impl<'a> LoweringContext<'a> {
fn lower_variant_data(&mut self, vdata: &VariantData) -> hir::VariantData { fn lower_variant_data(&mut self, vdata: &VariantData) -> hir::VariantData {
match *vdata { match *vdata {
VariantData::Struct(ref fields, id) => { VariantData::Struct(ref fields, id, recovered) => {
let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(id); let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(id);
hir::VariantData::Struct( hir::VariantData::Struct(
@ -2682,6 +2682,7 @@ impl<'a> LoweringContext<'a> {
.map(|f| self.lower_struct_field(f)) .map(|f| self.lower_struct_field(f))
.collect(), .collect(),
hir_id, hir_id,
recovered,
) )
}, },
VariantData::Tuple(ref fields, id) => { VariantData::Tuple(ref fields, id) => {

View File

@ -2173,7 +2173,7 @@ impl StructField {
/// Id of the whole struct lives in `Item`. /// Id of the whole struct lives in `Item`.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
pub enum VariantData { pub enum VariantData {
Struct(HirVec<StructField>, HirId), Struct(HirVec<StructField>, HirId, bool),
Tuple(HirVec<StructField>, HirId), Tuple(HirVec<StructField>, HirId),
Unit(HirId), Unit(HirId),
} }
@ -2187,7 +2187,7 @@ impl VariantData {
} }
pub fn hir_id(&self) -> HirId { pub fn hir_id(&self) -> HirId {
match *self { match *self {
VariantData::Struct(_, hir_id) VariantData::Struct(_, hir_id, _)
| VariantData::Tuple(_, hir_id) | VariantData::Tuple(_, hir_id)
| VariantData::Unit(hir_id) => hir_id, | VariantData::Unit(hir_id) => hir_id,
} }

View File

@ -1811,6 +1811,7 @@ pub struct VariantDef {
pub fields: Vec<FieldDef>, pub fields: Vec<FieldDef>,
pub ctor_kind: CtorKind, pub ctor_kind: CtorKind,
flags: VariantFlags, flags: VariantFlags,
pub recovered: bool,
} }
impl<'a, 'gcx, 'tcx> VariantDef { impl<'a, 'gcx, 'tcx> VariantDef {
@ -1829,16 +1830,17 @@ impl<'a, 'gcx, 'tcx> VariantDef {
/// ///
/// If someone speeds up attribute loading to not be a performance concern, they can /// If someone speeds up attribute loading to not be a performance concern, they can
/// remove this hack and use the constructor `DefId` everywhere. /// remove this hack and use the constructor `DefId` everywhere.
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(
did: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>,
ident: Ident, did: DefId,
discr: VariantDiscr, ident: Ident,
fields: Vec<FieldDef>, discr: VariantDiscr,
adt_kind: AdtKind, fields: Vec<FieldDef>,
ctor_kind: CtorKind, adt_kind: AdtKind,
attribute_def_id: DefId) ctor_kind: CtorKind,
-> Self attribute_def_id: DefId,
{ recovered: bool,
) -> Self {
debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, ident, discr, debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, ident, discr,
fields, adt_kind, ctor_kind, attribute_def_id); fields, adt_kind, ctor_kind, attribute_def_id);
let mut flags = VariantFlags::NO_VARIANT_FLAGS; let mut flags = VariantFlags::NO_VARIANT_FLAGS;
@ -1852,7 +1854,8 @@ impl<'a, 'gcx, 'tcx> VariantDef {
discr, discr,
fields, fields,
ctor_kind, ctor_kind,
flags flags,
recovered,
} }
} }
@ -1868,7 +1871,8 @@ impl_stable_hash_for!(struct VariantDef {
discr, discr,
fields, fields,
ctor_kind, ctor_kind,
flags flags,
recovered
}); });
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)]

View File

@ -576,7 +576,8 @@ impl<'a, 'tcx> CrateMetadata {
}).collect(), }).collect(),
adt_kind, adt_kind,
data.ctor_kind, data.ctor_kind,
attribute_def_id attribute_def_id,
false,
) )
} }

View File

@ -481,8 +481,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
}; };
let (value, fields) = match item.node { let (value, fields) = match item.node {
ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) | ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, ..), _) |
ast::ItemKind::Union(ast::VariantData::Struct(ref fields, _), _) => { ast::ItemKind::Union(ast::VariantData::Struct(ref fields, ..), _) => {
let include_priv_fields = !self.save_ctxt.config.pub_only; let include_priv_fields = !self.save_ctxt.config.pub_only;
let fields_str = fields let fields_str = fields
.iter() .iter()
@ -560,7 +560,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
let name_span = variant.node.ident.span; let name_span = variant.node.ident.span;
match variant.node.data { match variant.node.data {
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, ..) => {
let fields_str = fields let fields_str = fields
.iter() .iter()
.enumerate() .enumerate()

View File

@ -703,7 +703,7 @@ impl Sig for ast::Variant_ {
fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext<'_, '_>) -> Result { fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext<'_, '_>) -> Result {
let mut text = self.ident.to_string(); let mut text = self.ident.to_string();
match self.data { match self.data {
ast::VariantData::Struct(ref fields, id) => { ast::VariantData::Struct(ref fields, id, r) => {
let name_def = SigElement { let name_def = SigElement {
id: id_from_node_id(id, scx), id: id_from_node_id(id, scx),
start: offset, start: offset,
@ -712,12 +712,16 @@ impl Sig for ast::Variant_ {
text.push_str(" { "); text.push_str(" { ");
let mut defs = vec![name_def]; let mut defs = vec![name_def];
let mut refs = vec![]; let mut refs = vec![];
for f in fields { if r {
let field_sig = f.make(offset + text.len(), Some(id), scx)?; text.push_str("/* parse error */ ");
text.push_str(&field_sig.text); } else {
text.push_str(", "); for f in fields {
defs.extend(field_sig.defs.into_iter()); let field_sig = f.make(offset + text.len(), Some(id), scx)?;
refs.extend(field_sig.refs.into_iter()); text.push_str(&field_sig.text);
text.push_str(", ");
defs.extend(field_sig.defs.into_iter());
refs.extend(field_sig.refs.into_iter());
}
} }
text.push('}'); text.push('}');
Ok(Signature { text, defs, refs }) Ok(Signature { text, defs, refs })

View File

@ -918,14 +918,16 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
pat_ty pat_ty
} }
fn check_struct_pat_fields(&self, fn check_struct_pat_fields(
adt_ty: Ty<'tcx>, &self,
pat_id: hir::HirId, adt_ty: Ty<'tcx>,
span: Span, pat_id: hir::HirId,
variant: &'tcx ty::VariantDef, span: Span,
fields: &'gcx [Spanned<hir::FieldPat>], variant: &'tcx ty::VariantDef,
etc: bool, fields: &'gcx [Spanned<hir::FieldPat>],
def_bm: ty::BindingMode) -> bool { etc: bool,
def_bm: ty::BindingMode,
) -> bool {
let tcx = self.tcx; let tcx = self.tcx;
let (substs, adt) = match adt_ty.sty { let (substs, adt) = match adt_ty.sty {
@ -985,7 +987,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
.map(|field| field.ident.modern()) .map(|field| field.ident.modern())
.filter(|ident| !used_fields.contains_key(&ident)) .filter(|ident| !used_fields.contains_key(&ident))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if inexistent_fields.len() > 0 { if inexistent_fields.len() > 0 && !variant.recovered {
let (field_names, t, plural) = if inexistent_fields.len() == 1 { let (field_names, t, plural) = if inexistent_fields.len() == 1 {
(format!("a field named `{}`", inexistent_fields[0].1), "this", "") (format!("a field named `{}`", inexistent_fields[0].1), "this", "")
} else { } else {

View File

@ -3689,12 +3689,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
field, expr_t) field, expr_t)
} }
fn report_unknown_field(&self, fn report_unknown_field(
ty: Ty<'tcx>, &self,
variant: &'tcx ty::VariantDef, ty: Ty<'tcx>,
field: &hir::Field, variant: &'tcx ty::VariantDef,
skip_fields: &[hir::Field], field: &hir::Field,
kind_name: &str) { skip_fields: &[hir::Field],
kind_name: &str,
) {
if variant.recovered {
return;
}
let mut err = self.type_error_struct_with_diag( let mut err = self.type_error_struct_with_diag(
field.ident.span, field.ident.span,
|actual| match ty.sty { |actual| match ty.sty {

View File

@ -598,6 +598,10 @@ fn convert_variant<'a, 'tcx>(
} }
}) })
.collect(); .collect();
let recovered = match def {
hir::VariantData::Struct(_, _, r) => *r,
_ => false,
};
ty::VariantDef::new(tcx, ty::VariantDef::new(tcx,
did, did,
ident, ident,
@ -605,7 +609,8 @@ fn convert_variant<'a, 'tcx>(
fields, fields,
adt_kind, adt_kind,
CtorKind::from_hir(def), CtorKind::from_hir(def),
attribute_def_id attribute_def_id,
recovered,
) )
} }

View File

@ -2133,7 +2133,7 @@ pub enum VariantData {
/// Struct variant. /// Struct variant.
/// ///
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`. /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
Struct(Vec<StructField>, NodeId), Struct(Vec<StructField>, NodeId, bool),
/// Tuple variant. /// Tuple variant.
/// ///
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
@ -2147,13 +2147,13 @@ pub enum VariantData {
impl VariantData { impl VariantData {
pub fn fields(&self) -> &[StructField] { pub fn fields(&self) -> &[StructField] {
match *self { match *self {
VariantData::Struct(ref fields, _) | VariantData::Tuple(ref fields, _) => fields, VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, _) => fields,
_ => &[], _ => &[],
} }
} }
pub fn id(&self) -> NodeId { pub fn id(&self) -> NodeId {
match *self { match *self {
VariantData::Struct(_, id) | VariantData::Tuple(_, id) | VariantData::Unit(id) => id, VariantData::Struct(_, id, _) | VariantData::Tuple(_, id) | VariantData::Unit(id) => id,
} }
} }
pub fn is_struct(&self) -> bool { pub fn is_struct(&self) -> bool {

View File

@ -225,7 +225,7 @@ impl<'a> StripUnconfigured<'a> {
fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
match vdata { match vdata {
ast::VariantData::Struct(fields, _id) | ast::VariantData::Struct(fields, _id, _) |
ast::VariantData::Tuple(fields, _id) => ast::VariantData::Tuple(fields, _id) =>
fields.flat_map_in_place(|field| self.configure(field)), fields.flat_map_in_place(|field| self.configure(field)),
ast::VariantData::Unit(_id) => {} ast::VariantData::Unit(_id) => {}

View File

@ -765,7 +765,7 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) { pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
match vdata { match vdata {
VariantData::Struct(fields, id) | VariantData::Struct(fields, id, _) |
VariantData::Tuple(fields, id) => { VariantData::Tuple(fields, id) => {
visit_vec(fields, |field| vis.visit_struct_field(field)); visit_vec(fields, |field| vis.visit_struct_field(field));
vis.visit_id(id); vis.visit_id(id);

View File

@ -6829,14 +6829,16 @@ impl<'a> Parser<'a> {
VariantData::Unit(ast::DUMMY_NODE_ID) VariantData::Unit(ast::DUMMY_NODE_ID)
} else { } else {
// If we see: `struct Foo<T> where T: Copy { ... }` // If we see: `struct Foo<T> where T: Copy { ... }`
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) let (fields, recovered) = self.parse_record_struct_body()?;
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
} }
// No `where` so: `struct Foo<T>;` // No `where` so: `struct Foo<T>;`
} else if self.eat(&token::Semi) { } else if self.eat(&token::Semi) {
VariantData::Unit(ast::DUMMY_NODE_ID) VariantData::Unit(ast::DUMMY_NODE_ID)
// Record-style struct definition // Record-style struct definition
} else if self.token == token::OpenDelim(token::Brace) { } else if self.token == token::OpenDelim(token::Brace) {
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) let (fields, recovered) = self.parse_record_struct_body()?;
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
// Tuple-style struct definition with optional where-clause. // Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(token::Paren) { } else if self.token == token::OpenDelim(token::Paren) {
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, ast::DUMMY_NODE_ID); let body = VariantData::Tuple(self.parse_tuple_struct_body()?, ast::DUMMY_NODE_ID);
@ -6864,9 +6866,11 @@ impl<'a> Parser<'a> {
let vdata = if self.token.is_keyword(keywords::Where) { let vdata = if self.token.is_keyword(keywords::Where) {
generics.where_clause = self.parse_where_clause()?; generics.where_clause = self.parse_where_clause()?;
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) let (fields, recovered) = self.parse_record_struct_body()?;
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
} else if self.token == token::OpenDelim(token::Brace) { } else if self.token == token::OpenDelim(token::Brace) {
VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) let (fields, recovered) = self.parse_record_struct_body()?;
VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered)
} else { } else {
let token_str = self.this_token_descr(); let token_str = self.this_token_descr();
let mut err = self.fatal(&format!( let mut err = self.fatal(&format!(
@ -6898,12 +6902,14 @@ impl<'a> Parser<'a> {
} }
} }
fn parse_record_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { fn parse_record_struct_body(&mut self) -> PResult<'a, (Vec<StructField>, bool)> {
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut recovered = false;
if self.eat(&token::OpenDelim(token::Brace)) { if self.eat(&token::OpenDelim(token::Brace)) {
while self.token != token::CloseDelim(token::Brace) { while self.token != token::CloseDelim(token::Brace) {
let field = self.parse_struct_decl_field().map_err(|e| { let field = self.parse_struct_decl_field().map_err(|e| {
self.recover_stmt(); self.recover_stmt();
recovered = true;
e e
}); });
match field { match field {
@ -6922,7 +6928,7 @@ impl<'a> Parser<'a> {
return Err(err); return Err(err);
} }
Ok(fields) Ok((fields, recovered))
} }
fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> {
@ -7684,12 +7690,14 @@ impl<'a> Parser<'a> {
if self.check(&token::OpenDelim(token::Brace)) { if self.check(&token::OpenDelim(token::Brace)) {
// Parse a struct variant. // Parse a struct variant.
all_nullary = false; all_nullary = false;
struct_def = VariantData::Struct(self.parse_record_struct_body()?, let (fields, recovered) = self.parse_record_struct_body()?;
ast::DUMMY_NODE_ID); struct_def = VariantData::Struct(fields, ast::DUMMY_NODE_ID, recovered);
} else if self.check(&token::OpenDelim(token::Paren)) { } else if self.check(&token::OpenDelim(token::Paren)) {
all_nullary = false; all_nullary = false;
struct_def = VariantData::Tuple(self.parse_tuple_struct_body()?, struct_def = VariantData::Tuple(
ast::DUMMY_NODE_ID); self.parse_tuple_struct_body()?,
ast::DUMMY_NODE_ID,
);
} else if self.eat(&token::Eq) { } else if self.eat(&token::Eq) {
disr_expr = Some(AnonConst { disr_expr = Some(AnonConst {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,

View File

@ -0,0 +1,13 @@
enum Foo {
A { a, b: usize }
//~^ ERROR expected `:`, found `,`
}
fn main() {
// no complaints about non-existing fields
let f = Foo::A { a:3, b: 4};
match f {
// no complaints about non-existing fields
Foo::A {a, b} => {}
}
}

View File

@ -0,0 +1,8 @@
error: expected `:`, found `,`
--> $DIR/recovered-struct-variant.rs:2:10
|
LL | A { a, b: usize }
| ^ expected `:`
error: aborting due to previous error