Skip to content

Commit

Permalink
Restrict parsing of bare union/struct to field types
Browse files Browse the repository at this point in the history
Do not unconditionally parse bare types everywhere, only parse them if
they are in a field type, and try them when recovering a misparse
everywhere else, using them only if they are successfuly fully parsed.

Fix rust-lang#88583.
  • Loading branch information
estebank committed Sep 10, 2021
1 parent 02a57fa commit 70ff7ff
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 20 deletions.
69 changes: 61 additions & 8 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,12 +765,60 @@ impl<'a> Parser<'a> {
if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() };
generics.where_clause = self.parse_where_clause()?;

let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None };
self.expect_semi()?;
let default = if self.eat(&token::Eq) {
Some(self.parse_ty_recover_anon_adt(|this| this.expect_semi())?.0)
} else {
self.expect_semi()?;
None
};

Ok((ident, ItemKind::TyAlias(Box::new(TyAliasKind(def, generics, bounds, default)))))
}

/// Parses a type. If a misparse happens, we attempt to parse the type again, allowing
/// anonymous `union`s and `struct`s in its place. If successful, we return this. The
/// anonymous ADTs will be disallowed elsewhere.
fn parse_ty_recover_anon_adt<F, R>(&mut self, after: F) -> PResult<'a, (P<Ty>, R)>
where
F: Fn(&mut Self) -> PResult<'a, R>,
{
if (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct))
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
{
let mut snapshot = self.clone();
let mut err;
match self.parse_ty() {
Ok(ty) => match after(self) {
Ok(r) => return Ok((ty, r)),
Err(orig_err) => {
err = orig_err;
}
},
Err(orig_err) => {
err = orig_err;
}
}
match snapshot.parse_ty_allow_anon_adt() {
Ok(ty) => match after(&mut snapshot) {
Ok(r) => {
err.delay_as_bug();
*self = snapshot;
return Ok((ty, r));
}
Err(mut snapshot_err) => snapshot_err.cancel(),
},
Err(mut snapshot_err) => {
snapshot_err.cancel();
}
}
Err(err)
} else {
let ty = self.parse_ty()?;
let r = after(self)?;
Ok((ty, r))
}
}

/// Parses a `UseTree`.
///
/// ```text
Expand Down Expand Up @@ -1059,14 +1107,19 @@ impl<'a> Parser<'a> {

// Parse the type of a `const` or `static mut?` item.
// That is, the `":" $ty` fragment.
let ty = if self.eat(&token::Colon) {
self.parse_ty()?
let (ty, expr) = if self.eat(&token::Colon) {
self.parse_ty_recover_anon_adt(|this| {
let expr = if this.eat(&token::Eq) { Some(this.parse_expr()?) } else { None };
this.expect_semi()?;
Ok(expr)
})?
} else {
self.recover_missing_const_type(id, m)
let ty = self.recover_missing_const_type(id, m);
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
self.expect_semi()?;
(ty, expr)
};

let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
self.expect_semi()?;
Ok((id, ty, expr))
}

Expand Down Expand Up @@ -1438,7 +1491,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, FieldDef> {
let name = self.parse_field_ident(adt_ty, lo)?;
self.expect_field_ty_separator()?;
let ty = self.parse_ty()?;
let ty = self.parse_ty_allow_anon_adt()?;
if self.token.kind == token::Eq {
self.bump();
let const_expr = self.parse_anon_const_expr()?;
Expand Down
74 changes: 62 additions & 12 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ pub(super) enum AllowPlus {
No,
}

#[derive(Copy, Clone, PartialEq)]
pub(super) enum AllowAnonymousType {
Yes,
No,
}

#[derive(PartialEq)]
pub(super) enum RecoverQPath {
Yes,
Expand Down Expand Up @@ -98,9 +104,19 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
AllowAnonymousType::No,
)
}

pub fn parse_ty_allow_anon_adt(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(
AllowPlus::Yes,
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
AllowAnonymousType::Yes,
)
}
/// Parse a type suitable for a function or function pointer parameter.
/// The difference from `parse_ty` is that this version allows `...`
/// (`CVarArgs`) at the top level of the type.
Expand All @@ -110,6 +126,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::Yes,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
AllowAnonymousType::No,
)
}

Expand All @@ -125,6 +142,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
AllowAnonymousType::No,
)
}

Expand All @@ -135,6 +153,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::Yes,
RecoverQPath::Yes,
RecoverReturnSign::OnlyFatArrow,
AllowAnonymousType::No,
)
}

Expand All @@ -152,6 +171,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
recover_qpath,
recover_return_sign,
AllowAnonymousType::No,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
Expand All @@ -171,6 +191,7 @@ impl<'a> Parser<'a> {
AllowCVariadic::No,
recover_qpath,
recover_return_sign,
AllowAnonymousType::No,
)?;
FnRetTy::Ty(ty)
} else {
Expand All @@ -184,6 +205,7 @@ impl<'a> Parser<'a> {
allow_c_variadic: AllowCVariadic,
recover_qpath: RecoverQPath,
recover_return_sign: RecoverReturnSign,
allow_anonymous: AllowAnonymousType,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
Expand Down Expand Up @@ -226,19 +248,11 @@ impl<'a> Parser<'a> {
}
} else if self.eat_keyword(kw::Impl) {
self.parse_impl_ty(&mut impl_dyn_multi)?
} else if self.token.is_keyword(kw::Union)
} else if allow_anonymous == AllowAnonymousType::Yes
&& (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct))
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
{
self.bump();
let (fields, recovered) = self.parse_record_struct_body("union")?;
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::unnamed_fields, span);
TyKind::AnonymousUnion(fields, recovered)
} else if self.eat_keyword(kw::Struct) {
let (fields, recovered) = self.parse_record_struct_body("struct")?;
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::unnamed_fields, span);
TyKind::AnonymousStruct(fields, recovered)
self.parse_anonymous_ty(lo)?
} else if self.is_explicit_dyn_type() {
self.parse_dyn_ty(&mut impl_dyn_multi)?
} else if self.eat_lt() {
Expand All @@ -263,7 +277,27 @@ impl<'a> Parser<'a> {
let mut err = self.struct_span_err(self.token.span, &msg);
err.span_label(self.token.span, "expected type");
self.maybe_annotate_with_ascription(&mut err, true);
return Err(err);

if allow_anonymous == AllowAnonymousType::No
&& (self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct))
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
{
// Recover the parser from anonymous types anywhere other than field types.
let snapshot = self.clone();
match self.parse_anonymous_ty(lo) {
Ok(ty) => {
err.delay_as_bug();
ty
}
Err(mut snapshot_err) => {
snapshot_err.cancel();
*self = snapshot;
return Err(err);
}
}
} else {
return Err(err);
}
};

let span = lo.to(self.prev_token.span);
Expand All @@ -275,6 +309,22 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)
}

fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> {
let is_union = self.token.is_keyword(kw::Union);
self.bump();
self.parse_record_struct_body(if is_union { "union" } else { "struct" }).map(
|(fields, recovered)| {
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::unnamed_fields, span);
// These will be rejected during AST validation in `deny_anonymous_struct`.
return if is_union {
TyKind::AnonymousUnion(fields, recovered)
} else {
TyKind::AnonymousStruct(fields, recovered)
};
},
)
}
/// Parses either:
/// - `(TYPE)`, a parenthesized type.
/// - `(TYPE,)`, a tuple with a single field of type TYPE.
Expand Down
15 changes: 15 additions & 0 deletions src/test/ui/parser/issue-88583-union-as-ident.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// check-pass

#![allow(non_camel_case_types)]

struct union;

impl union {
pub fn new() -> Self {
union { }
}
}

fn main() {
let _u = union::new();
}

0 comments on commit 70ff7ff

Please sign in to comment.