Skip to content

Commit

Permalink
Fix recovery of anonymous types
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Aug 13, 2023
1 parent 9ce026c commit 1686551
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 243 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2093,9 +2093,9 @@ pub enum TyKind {
/// A tuple (`(A, B, C, D,...)`).
Tup(ThinVec<P<Ty>>),
/// An anonymous struct type i.e. `struct { foo: Type }`
AnonymousStruct(ThinVec<FieldDef>, /* recovered */ bool),
AnonymousStruct(ThinVec<FieldDef>),
/// An anonymous union type i.e. `union { bar: Type }`
AnonymousUnion(ThinVec<FieldDef>, /* recovered */ bool),
AnonymousUnion(ThinVec<FieldDef>),
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
///
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
TyKind::AnonymousStruct(fields, _recovered)
| TyKind::AnonymousUnion(fields, _recovered) => {
TyKind::AnonymousStruct(fields) | TyKind::AnonymousUnion(fields) => {
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
}
}
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,10 +708,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}

pub(super) fn lower_field_def(
&mut self,
(index, f): (usize, &FieldDef),
) -> hir::FieldDef<'hir> {
fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
let ty = if let TyKind::Path(qself, path) = &f.ty.kind {
let t = self.lower_path_ty(
&f.ty,
Expand Down
21 changes: 13 additions & 8 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
#![feature(let_chains)]
#![feature(never_type)]
#![recursion_limit = "256"]
// #![deny(rustc::untranslatable_diagnostic)]
// #![deny(rustc::diagnostic_outside_of_impl)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

#[macro_use]
extern crate tracing;
Expand Down Expand Up @@ -1294,12 +1294,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
}
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
TyKind::AnonymousStruct(ref _fields, _recovered) => {
hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"))
}
TyKind::AnonymousUnion(ref _fields, _recovered) => {
hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"))
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonymousStruct(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
),
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonymousUnion(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Ref(region, mt) => {
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl<'a> AstValidator<'a> {
}
TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => {
// self.with_banned_assoc_ty_bound(|this| {
walk_list!(self, visit_struct_field_def, fields)
walk_list!(self, visit_struct_field_def, fields)
// });
}
_ => visit::walk_ty(self, t),
Expand Down Expand Up @@ -865,6 +865,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

fn visit_ty(&mut self, ty: &'a Ty) {
self.visit_ty_common(ty);
tracing::info!(?ty);
self.deny_anonymous_struct(ty);
self.walk_ty(ty)
}
Expand Down Expand Up @@ -1085,7 +1086,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.visit_ident(item.ident);
self.visit_generics(generics);
// self.with_banned_assoc_ty_bound(|this| {
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_struct_field_def, fields);
// });
walk_list!(self, visit_attribute, &item.attrs);
return;
Expand All @@ -1102,7 +1103,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.visit_ident(item.ident);
self.visit_generics(generics);
// self.with_banned_assoc_ty_bound(|this| {
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_struct_field_def, fields);
// });
walk_list!(self, visit_attribute, &item.attrs);
return;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,11 +1053,11 @@ impl<'a> State<'a> {
}
self.pclose();
}
ast::TyKind::AnonymousStruct(fields, _recovered) => {
ast::TyKind::AnonymousStruct(fields) => {
self.head("struct");
self.print_record_struct_body(&fields, ty.span);
}
ast::TyKind::AnonymousUnion(fields, _recovered) => {
ast::TyKind::AnonymousUnion(fields) => {
self.head("union");
self.print_record_struct_body(&fields, ty.span);
}
Expand Down
177 changes: 100 additions & 77 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::ty::{AllowPlus, RecoverAnonymousStructOrUnion, RecoverQPath, RecoverReturnSign};
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{self, MacroExpandsToAdtField};
use crate::fluent_generated as fluent;
Expand Down Expand Up @@ -602,7 +602,7 @@ impl<'a> Parser<'a> {
Some(self.mk_ty(self.prev_token.span, TyKind::Err))
} else if has_for || self.token.can_begin_type() {
snapshot_before_last_ty = self.create_snapshot_for_diagnostic();
Some(self.parse_ty_no_anon_recovery()?)
Some(self.parse_ty()?)
} else {
None
};
Expand All @@ -615,7 +615,7 @@ impl<'a> Parser<'a> {
if let Some(mut err) = err {
let mut snapshot = snapshot_before_last_ty;

if snapshot.can_start_anonymous_type() {
if snapshot.can_start_anonymous_union() {
let recover_result = {
let recover_last_ty = match snapshot.parse_ty() {
Ok(ty) => Some(ty),
Expand Down Expand Up @@ -1665,11 +1665,26 @@ impl<'a> Parser<'a> {
Ok((class_name, ItemKind::Union(vdata, generics)))
}

pub(crate) fn parse_record_struct_body(
fn parse_record_struct_body(
&mut self,
adt_ty: &str,
ident_span: Span,
parsed_where: bool,
) -> PResult<'a, (ThinVec<FieldDef>, /* recovered */ bool)> {
self.parse_record_struct_body_common(
adt_ty,
ident_span,
parsed_where,
RecoverAnonymousStructOrUnion::No,
)
}

pub(crate) fn parse_record_struct_body_common(
&mut self,
adt_ty: &str,
ident_span: Span,
parsed_where: bool,
recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion,
) -> PResult<'a, (ThinVec<FieldDef>, /* recovered */ bool)> {
let mut fields = ThinVec::new();
let mut recovered = false;
Expand All @@ -1683,6 +1698,16 @@ impl<'a> Parser<'a> {
match field {
Ok(field) => fields.push(field),
Err(mut err) => {
// When recovering the anonymous structs or unions, we should't emit the error
// immediately, because it may also be a type path `union` followed by a block,
// such as `impl union { fn foo() {} }`. Here we are actaully not parsing a
// record struct body but an `impl` body.
//
// Instead, the error should be thrown and handled by the caller
// `parse_anonymous_struct_or_union`.
if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes {
return Err(err);
}
err.span_label(ident_span, format!("while parsing this {adt_ty}"));
err.emit();
break;
Expand Down Expand Up @@ -1965,85 +1990,83 @@ impl<'a> Parser<'a> {
/// for better diagnostics and suggestions.
fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
let (ident, is_raw) = self.ident_or_err(true)?;
if !is_raw && ident.is_reserved() {
if ident.name == kw::Underscore {
self.sess.gated_spans.gate(sym::unnamed_fields, lo);
} else if !is_raw && ident.is_reserved() {
let snapshot = self.create_snapshot_for_diagnostic();
if ident.name == kw::Underscore {
self.sess.gated_spans.gate(sym::unnamed_fields, lo);
} else {
let err = if self.check_fn_front_matter(false, Case::Sensitive) {
let inherited_vis = Visibility {
span: rustc_span::DUMMY_SP,
kind: VisibilityKind::Inherited,
tokens: None,
};
// We use `parse_fn` to get a span for the function
let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
match self.parse_fn(
&mut AttrVec::new(),
fn_parse_mode,
lo,
&inherited_vis,
Case::Insensitive,
) {
Ok(_) => {
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
format!("functions are not allowed in {adt_ty} definitions"),
);
err.help(
"unlike in C++, Java, and C#, functions are declared in `impl` blocks",
);
err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
err
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
let err = if self.check_fn_front_matter(false, Case::Sensitive) {
let inherited_vis = Visibility {
span: rustc_span::DUMMY_SP,
kind: VisibilityKind::Inherited,
tokens: None,
};
// We use `parse_fn` to get a span for the function
let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
match self.parse_fn(
&mut AttrVec::new(),
fn_parse_mode,
lo,
&inherited_vis,
Case::Insensitive,
) {
Ok(_) => {
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
format!("functions are not allowed in {adt_ty} definitions"),
);
err.help(
"unlike in C++, Java, and C#, functions are declared in `impl` blocks",
);
err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
err
}
} else if self.eat_keyword(kw::Struct) {
match self.parse_item_struct() {
Ok((ident, _)) => {
let mut err = self.struct_span_err(
lo.with_hi(ident.span.hi()),
format!("structs are not allowed in {adt_ty} definitions"),
);
err.help("consider creating a new `struct` definition instead of nesting");
err
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
} else {
let mut err = self.expected_ident_found_err();
if self.eat_keyword_noexpect(kw::Let)
&& let removal_span = self.prev_token.span.until(self.token.span)
&& let Ok(ident) = self.parse_ident_common(false)
// Cancel this error, we don't need it.
.map_err(|err| err.cancel())
&& self.token.kind == TokenKind::Colon
{
err.span_suggestion(
removal_span,
"remove this `let` keyword",
String::new(),
Applicability::MachineApplicable,
}
} else if self.eat_keyword(kw::Struct) {
match self.parse_item_struct() {
Ok((ident, _)) => {
let mut err = self.struct_span_err(
lo.with_hi(ident.span.hi()),
format!("structs are not allowed in {adt_ty} definitions"),
);
err.note("the `let` keyword is not allowed in `struct` fields");
err.note("see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information");
err.emit();
return Ok(ident);
} else {
err.help("consider creating a new `struct` definition instead of nesting");
err
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
err
};
return Err(err);
}
}
} else {
let mut err = self.expected_ident_found_err();
if self.eat_keyword_noexpect(kw::Let)
&& let removal_span = self.prev_token.span.until(self.token.span)
&& let Ok(ident) = self.parse_ident_common(false)
// Cancel this error, we don't need it.
.map_err(|err| err.cancel())
&& self.token.kind == TokenKind::Colon
{
err.span_suggestion(
removal_span,
"remove this `let` keyword",
String::new(),
Applicability::MachineApplicable,
);
err.note("the `let` keyword is not allowed in `struct` fields");
err.note("see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information");
err.emit();
return Ok(ident);
} else {
self.restore_snapshot(snapshot);
}
err
};
return Err(err);
}
self.bump();
Ok(ident)
Expand Down
Loading

0 comments on commit 1686551

Please sign in to comment.