Skip to content

Commit 70ff7ff

Browse files
committed
Restrict parsing of bare union/struct to field types
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.
1 parent 02a57fa commit 70ff7ff

File tree

3 files changed

+138
-20
lines changed

3 files changed

+138
-20
lines changed

compiler/rustc_parse/src/parser/item.rs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -765,12 +765,60 @@ impl<'a> Parser<'a> {
765765
if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() };
766766
generics.where_clause = self.parse_where_clause()?;
767767

768-
let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None };
769-
self.expect_semi()?;
768+
let default = if self.eat(&token::Eq) {
769+
Some(self.parse_ty_recover_anon_adt(|this| this.expect_semi())?.0)
770+
} else {
771+
self.expect_semi()?;
772+
None
773+
};
770774

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

778+
/// Parses a type. If a misparse happens, we attempt to parse the type again, allowing
779+
/// anonymous `union`s and `struct`s in its place. If successful, we return this. The
780+
/// anonymous ADTs will be disallowed elsewhere.
781+
fn parse_ty_recover_anon_adt<F, R>(&mut self, after: F) -> PResult<'a, (P<Ty>, R)>
782+
where
783+
F: Fn(&mut Self) -> PResult<'a, R>,
784+
{
785+
if (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct))
786+
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
787+
{
788+
let mut snapshot = self.clone();
789+
let mut err;
790+
match self.parse_ty() {
791+
Ok(ty) => match after(self) {
792+
Ok(r) => return Ok((ty, r)),
793+
Err(orig_err) => {
794+
err = orig_err;
795+
}
796+
},
797+
Err(orig_err) => {
798+
err = orig_err;
799+
}
800+
}
801+
match snapshot.parse_ty_allow_anon_adt() {
802+
Ok(ty) => match after(&mut snapshot) {
803+
Ok(r) => {
804+
err.delay_as_bug();
805+
*self = snapshot;
806+
return Ok((ty, r));
807+
}
808+
Err(mut snapshot_err) => snapshot_err.cancel(),
809+
},
810+
Err(mut snapshot_err) => {
811+
snapshot_err.cancel();
812+
}
813+
}
814+
Err(err)
815+
} else {
816+
let ty = self.parse_ty()?;
817+
let r = after(self)?;
818+
Ok((ty, r))
819+
}
820+
}
821+
774822
/// Parses a `UseTree`.
775823
///
776824
/// ```text
@@ -1059,14 +1107,19 @@ impl<'a> Parser<'a> {
10591107

10601108
// Parse the type of a `const` or `static mut?` item.
10611109
// That is, the `":" $ty` fragment.
1062-
let ty = if self.eat(&token::Colon) {
1063-
self.parse_ty()?
1110+
let (ty, expr) = if self.eat(&token::Colon) {
1111+
self.parse_ty_recover_anon_adt(|this| {
1112+
let expr = if this.eat(&token::Eq) { Some(this.parse_expr()?) } else { None };
1113+
this.expect_semi()?;
1114+
Ok(expr)
1115+
})?
10641116
} else {
1065-
self.recover_missing_const_type(id, m)
1117+
let ty = self.recover_missing_const_type(id, m);
1118+
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
1119+
self.expect_semi()?;
1120+
(ty, expr)
10661121
};
10671122

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

@@ -1438,7 +1491,7 @@ impl<'a> Parser<'a> {
14381491
) -> PResult<'a, FieldDef> {
14391492
let name = self.parse_field_ident(adt_ty, lo)?;
14401493
self.expect_field_ty_separator()?;
1441-
let ty = self.parse_ty()?;
1494+
let ty = self.parse_ty_allow_anon_adt()?;
14421495
if self.token.kind == token::Eq {
14431496
self.bump();
14441497
let const_expr = self.parse_anon_const_expr()?;

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ pub(super) enum AllowPlus {
3737
No,
3838
}
3939

40+
#[derive(Copy, Clone, PartialEq)]
41+
pub(super) enum AllowAnonymousType {
42+
Yes,
43+
No,
44+
}
45+
4046
#[derive(PartialEq)]
4147
pub(super) enum RecoverQPath {
4248
Yes,
@@ -98,9 +104,19 @@ impl<'a> Parser<'a> {
98104
AllowCVariadic::No,
99105
RecoverQPath::Yes,
100106
RecoverReturnSign::Yes,
107+
AllowAnonymousType::No,
101108
)
102109
}
103110

111+
pub fn parse_ty_allow_anon_adt(&mut self) -> PResult<'a, P<Ty>> {
112+
self.parse_ty_common(
113+
AllowPlus::Yes,
114+
AllowCVariadic::No,
115+
RecoverQPath::Yes,
116+
RecoverReturnSign::Yes,
117+
AllowAnonymousType::Yes,
118+
)
119+
}
104120
/// Parse a type suitable for a function or function pointer parameter.
105121
/// The difference from `parse_ty` is that this version allows `...`
106122
/// (`CVarArgs`) at the top level of the type.
@@ -110,6 +126,7 @@ impl<'a> Parser<'a> {
110126
AllowCVariadic::Yes,
111127
RecoverQPath::Yes,
112128
RecoverReturnSign::Yes,
129+
AllowAnonymousType::No,
113130
)
114131
}
115132

@@ -125,6 +142,7 @@ impl<'a> Parser<'a> {
125142
AllowCVariadic::No,
126143
RecoverQPath::Yes,
127144
RecoverReturnSign::Yes,
145+
AllowAnonymousType::No,
128146
)
129147
}
130148

@@ -135,6 +153,7 @@ impl<'a> Parser<'a> {
135153
AllowCVariadic::Yes,
136154
RecoverQPath::Yes,
137155
RecoverReturnSign::OnlyFatArrow,
156+
AllowAnonymousType::No,
138157
)
139158
}
140159

@@ -152,6 +171,7 @@ impl<'a> Parser<'a> {
152171
AllowCVariadic::No,
153172
recover_qpath,
154173
recover_return_sign,
174+
AllowAnonymousType::No,
155175
)?;
156176
FnRetTy::Ty(ty)
157177
} else if recover_return_sign.can_recover(&self.token.kind) {
@@ -171,6 +191,7 @@ impl<'a> Parser<'a> {
171191
AllowCVariadic::No,
172192
recover_qpath,
173193
recover_return_sign,
194+
AllowAnonymousType::No,
174195
)?;
175196
FnRetTy::Ty(ty)
176197
} else {
@@ -184,6 +205,7 @@ impl<'a> Parser<'a> {
184205
allow_c_variadic: AllowCVariadic,
185206
recover_qpath: RecoverQPath,
186207
recover_return_sign: RecoverReturnSign,
208+
allow_anonymous: AllowAnonymousType,
187209
) -> PResult<'a, P<Ty>> {
188210
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
189211
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
@@ -226,19 +248,11 @@ impl<'a> Parser<'a> {
226248
}
227249
} else if self.eat_keyword(kw::Impl) {
228250
self.parse_impl_ty(&mut impl_dyn_multi)?
229-
} else if self.token.is_keyword(kw::Union)
251+
} else if allow_anonymous == AllowAnonymousType::Yes
252+
&& (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct))
230253
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
231254
{
232-
self.bump();
233-
let (fields, recovered) = self.parse_record_struct_body("union")?;
234-
let span = lo.to(self.prev_token.span);
235-
self.sess.gated_spans.gate(sym::unnamed_fields, span);
236-
TyKind::AnonymousUnion(fields, recovered)
237-
} else if self.eat_keyword(kw::Struct) {
238-
let (fields, recovered) = self.parse_record_struct_body("struct")?;
239-
let span = lo.to(self.prev_token.span);
240-
self.sess.gated_spans.gate(sym::unnamed_fields, span);
241-
TyKind::AnonymousStruct(fields, recovered)
255+
self.parse_anonymous_ty(lo)?
242256
} else if self.is_explicit_dyn_type() {
243257
self.parse_dyn_ty(&mut impl_dyn_multi)?
244258
} else if self.eat_lt() {
@@ -263,7 +277,27 @@ impl<'a> Parser<'a> {
263277
let mut err = self.struct_span_err(self.token.span, &msg);
264278
err.span_label(self.token.span, "expected type");
265279
self.maybe_annotate_with_ascription(&mut err, true);
266-
return Err(err);
280+
281+
if allow_anonymous == AllowAnonymousType::No
282+
&& (self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct))
283+
&& self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
284+
{
285+
// Recover the parser from anonymous types anywhere other than field types.
286+
let snapshot = self.clone();
287+
match self.parse_anonymous_ty(lo) {
288+
Ok(ty) => {
289+
err.delay_as_bug();
290+
ty
291+
}
292+
Err(mut snapshot_err) => {
293+
snapshot_err.cancel();
294+
*self = snapshot;
295+
return Err(err);
296+
}
297+
}
298+
} else {
299+
return Err(err);
300+
}
267301
};
268302

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

312+
fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> {
313+
let is_union = self.token.is_keyword(kw::Union);
314+
self.bump();
315+
self.parse_record_struct_body(if is_union { "union" } else { "struct" }).map(
316+
|(fields, recovered)| {
317+
let span = lo.to(self.prev_token.span);
318+
self.sess.gated_spans.gate(sym::unnamed_fields, span);
319+
// These will be rejected during AST validation in `deny_anonymous_struct`.
320+
return if is_union {
321+
TyKind::AnonymousUnion(fields, recovered)
322+
} else {
323+
TyKind::AnonymousStruct(fields, recovered)
324+
};
325+
},
326+
)
327+
}
278328
/// Parses either:
279329
/// - `(TYPE)`, a parenthesized type.
280330
/// - `(TYPE,)`, a tuple with a single field of type TYPE.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// check-pass
2+
3+
#![allow(non_camel_case_types)]
4+
5+
struct union;
6+
7+
impl union {
8+
pub fn new() -> Self {
9+
union { }
10+
}
11+
}
12+
13+
fn main() {
14+
let _u = union::new();
15+
}

0 commit comments

Comments
 (0)