Skip to content

Commit 2d82420

Browse files
committed
Teach parser to understand fake anonymous enum syntax
Parse `-> Ty | OtherTy`. Parse type ascription in top level patterns.
1 parent ae4d89d commit 2d82420

File tree

13 files changed

+270
-40
lines changed

13 files changed

+270
-40
lines changed

compiler/rustc_ast/src/ast.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,9 @@ pub enum TyKind {
20962096
Err,
20972097
/// Placeholder for a `va_list`.
20982098
CVarArgs,
2099+
/// Placeholder for "anonymous enums", which don't exist, but keeping their
2100+
/// information around lets us produce better diagnostics.
2101+
AnonEnum(Vec<P<Ty>>),
20992102
}
21002103

21012104
impl TyKind {

compiler/rustc_ast/src/mut_visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
470470
vis.visit_fn_decl(decl);
471471
vis.visit_span(decl_span);
472472
}
473-
TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
473+
TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
474474
TyKind::Paren(ty) => vis.visit_ty(ty),
475475
TyKind::Path(qself, path) => {
476476
vis.visit_qself(qself);

compiler/rustc_ast/src/visit.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
400400
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
401401
visitor.visit_ty(&mutable_type.ty)
402402
}
403-
TyKind::Tup(tuple_element_types) => {
404-
walk_list!(visitor, visit_ty, tuple_element_types);
403+
TyKind::AnonEnum(tys) | TyKind::Tup(tys) => {
404+
walk_list!(visitor, visit_ty, tys);
405405
}
406406
TyKind::BareFn(function_declaration) => {
407407
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);

compiler/rustc_ast_lowering/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12351235
let kind = match &t.kind {
12361236
TyKind::Infer => hir::TyKind::Infer,
12371237
TyKind::Err => hir::TyKind::Err,
1238+
TyKind::AnonEnum(_) => hir::TyKind::Err,
12381239
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
12391240
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
12401241
TyKind::Ref(region, mt) => {

compiler/rustc_ast_pretty/src/pprust/state.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,9 @@ impl<'a> State<'a> {
10411041
}
10421042
self.pclose();
10431043
}
1044+
ast::TyKind::AnonEnum(elts) => {
1045+
self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty));
1046+
}
10441047
ast::TyKind::Paren(typ) => {
10451048
self.popen();
10461049
self.print_type(typ);

compiler/rustc_parse/src/parser/diagnostics.rs

+37-13
Original file line numberDiff line numberDiff line change
@@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> {
23722372

23732373
/// Some special error handling for the "top-level" patterns in a match arm,
23742374
/// `for` loop, `let`, &c. (in contrast to subpatterns within such).
2375-
pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
2375+
pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
23762376
&mut self,
23772377
mut first_pat: P<Pat>,
23782378
expected: Expected,
@@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> {
23832383
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
23842384
|| !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
23852385
{
2386+
let mut snapshot_type = self.create_snapshot_for_diagnostic();
2387+
snapshot_type.bump(); // `:`
2388+
match snapshot_type.parse_ty() {
2389+
Err(inner_err) => {
2390+
inner_err.cancel();
2391+
}
2392+
Ok(ty) => {
2393+
let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
2394+
return first_pat;
2395+
};
2396+
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2397+
self.restore_snapshot(snapshot_type);
2398+
let span = first_pat.span.to(ty.span);
2399+
first_pat = self.mk_pat(span, PatKind::Wild);
2400+
err.emit();
2401+
}
2402+
}
23862403
return first_pat;
23872404
}
23882405
// The pattern looks like it might be a path with a `::` -> `:` typo:
23892406
// `match foo { bar:baz => {} }`
2390-
let span = self.token.span;
2407+
let colon_span = self.token.span;
23912408
// We only emit "unexpected `:`" error here if we can successfully parse the
23922409
// whole pattern correctly in that case.
2393-
let snapshot = self.create_snapshot_for_diagnostic();
2410+
let mut snapshot_pat = self.create_snapshot_for_diagnostic();
2411+
let mut snapshot_type = self.create_snapshot_for_diagnostic();
23942412

23952413
// Create error for "unexpected `:`".
23962414
match self.expected_one_of_not_found(&[], &[]) {
23972415
Err(mut err) => {
2398-
self.bump(); // Skip the `:`.
2399-
match self.parse_pat_no_top_alt(expected) {
2416+
snapshot_pat.bump(); // Skip the `:`.
2417+
snapshot_type.bump(); // Skip the `:`.
2418+
match snapshot_pat.parse_pat_no_top_alt(expected) {
24002419
Err(inner_err) => {
2401-
// Carry on as if we had not done anything, callers will emit a
2402-
// reasonable error.
24032420
inner_err.cancel();
2404-
err.cancel();
2405-
self.restore_snapshot(snapshot);
24062421
}
24072422
Ok(mut pat) => {
24082423
// We've parsed the rest of the pattern.
@@ -2466,22 +2481,31 @@ impl<'a> Parser<'a> {
24662481
_ => {}
24672482
}
24682483
if show_sugg {
2469-
err.span_suggestion(
2470-
span,
2484+
err.span_suggestion_verbose(
2485+
colon_span.until(self.look_ahead(1, |t| t.span)),
24712486
"maybe write a path separator here",
24722487
"::",
24732488
Applicability::MaybeIncorrect,
24742489
);
24752490
} else {
24762491
first_pat = self.mk_pat(new_span, PatKind::Wild);
24772492
}
2478-
err.emit();
2493+
self.restore_snapshot(snapshot_pat);
24792494
}
24802495
}
2496+
match snapshot_type.parse_ty() {
2497+
Err(inner_err) => {
2498+
inner_err.cancel();
2499+
}
2500+
Ok(ty) => {
2501+
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2502+
self.restore_snapshot(snapshot_type);
2503+
}
2504+
}
2505+
err.emit();
24812506
}
24822507
_ => {
24832508
// Carry on as if we had not done anything. This should be unreachable.
2484-
self.restore_snapshot(snapshot);
24852509
}
24862510
};
24872511
first_pat

compiler/rustc_parse/src/parser/pat.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ impl<'a> Parser<'a> {
116116

117117
// Check if the user wrote `foo:bar` instead of `foo::bar`.
118118
if ra == RecoverColon::Yes {
119-
first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected);
119+
first_pat =
120+
self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected);
120121
}
121122

122123
if let Some(leading_vert_span) = leading_vert_span {

compiler/rustc_parse/src/parser/ty.rs

+63-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_ast::{
1111
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
1212
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
1313
};
14+
use rustc_ast_pretty::pprust;
1415
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
1516
use rustc_span::source_map::Span;
1617
use rustc_span::symbol::{kw, sym, Ident};
@@ -43,17 +44,24 @@ pub(super) enum AllowPlus {
4344
No,
4445
}
4546

46-
#[derive(PartialEq)]
47+
#[derive(PartialEq, Clone, Copy)]
4748
pub(super) enum RecoverQPath {
4849
Yes,
4950
No,
5051
}
5152

53+
#[derive(PartialEq, Clone, Copy)]
5254
pub(super) enum RecoverQuestionMark {
5355
Yes,
5456
No,
5557
}
5658

59+
#[derive(PartialEq, Clone, Copy)]
60+
pub(super) enum RecoverAnonEnum {
61+
Yes,
62+
No,
63+
}
64+
5765
/// Signals whether parsing a type should recover `->`.
5866
///
5967
/// More specifically, when parsing a function like:
@@ -86,7 +94,7 @@ impl RecoverReturnSign {
8694
}
8795

8896
// Is `...` (`CVarArgs`) legal at this level of type parsing?
89-
#[derive(PartialEq)]
97+
#[derive(PartialEq, Clone, Copy)]
9098
enum AllowCVariadic {
9199
Yes,
92100
No,
@@ -111,6 +119,7 @@ impl<'a> Parser<'a> {
111119
RecoverReturnSign::Yes,
112120
None,
113121
RecoverQuestionMark::Yes,
122+
RecoverAnonEnum::No,
114123
)
115124
}
116125

@@ -125,6 +134,7 @@ impl<'a> Parser<'a> {
125134
RecoverReturnSign::Yes,
126135
Some(ty_params),
127136
RecoverQuestionMark::Yes,
137+
RecoverAnonEnum::No,
128138
)
129139
}
130140

@@ -139,6 +149,7 @@ impl<'a> Parser<'a> {
139149
RecoverReturnSign::Yes,
140150
None,
141151
RecoverQuestionMark::Yes,
152+
RecoverAnonEnum::Yes,
142153
)
143154
}
144155

@@ -156,6 +167,7 @@ impl<'a> Parser<'a> {
156167
RecoverReturnSign::Yes,
157168
None,
158169
RecoverQuestionMark::Yes,
170+
RecoverAnonEnum::No,
159171
)
160172
}
161173

@@ -169,6 +181,7 @@ impl<'a> Parser<'a> {
169181
RecoverReturnSign::Yes,
170182
None,
171183
RecoverQuestionMark::No,
184+
RecoverAnonEnum::No,
172185
)
173186
}
174187

@@ -180,6 +193,7 @@ impl<'a> Parser<'a> {
180193
RecoverReturnSign::Yes,
181194
None,
182195
RecoverQuestionMark::No,
196+
RecoverAnonEnum::No,
183197
)
184198
}
185199

@@ -192,6 +206,7 @@ impl<'a> Parser<'a> {
192206
RecoverReturnSign::OnlyFatArrow,
193207
None,
194208
RecoverQuestionMark::Yes,
209+
RecoverAnonEnum::No,
195210
)
196211
}
197212

@@ -211,6 +226,7 @@ impl<'a> Parser<'a> {
211226
recover_return_sign,
212227
None,
213228
RecoverQuestionMark::Yes,
229+
RecoverAnonEnum::Yes,
214230
)?;
215231
FnRetTy::Ty(ty)
216232
} else if recover_return_sign.can_recover(&self.token.kind) {
@@ -232,6 +248,7 @@ impl<'a> Parser<'a> {
232248
recover_return_sign,
233249
None,
234250
RecoverQuestionMark::Yes,
251+
RecoverAnonEnum::Yes,
235252
)?;
236253
FnRetTy::Ty(ty)
237254
} else {
@@ -247,6 +264,7 @@ impl<'a> Parser<'a> {
247264
recover_return_sign: RecoverReturnSign,
248265
ty_generics: Option<&Generics>,
249266
recover_question_mark: RecoverQuestionMark,
267+
recover_anon_enum: RecoverAnonEnum,
250268
) -> PResult<'a, P<Ty>> {
251269
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
252270
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
@@ -325,14 +343,55 @@ impl<'a> Parser<'a> {
325343
let mut ty = self.mk_ty(span, kind);
326344

327345
// Try to recover from use of `+` with incorrect priority.
328-
if matches!(allow_plus, AllowPlus::Yes) {
346+
if allow_plus == AllowPlus::Yes {
329347
self.maybe_recover_from_bad_type_plus(&ty)?;
330348
} else {
331349
self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty);
332350
}
333-
if let RecoverQuestionMark::Yes = recover_question_mark {
351+
if RecoverQuestionMark::Yes == recover_question_mark {
334352
ty = self.maybe_recover_from_question_mark(ty);
335353
}
354+
if recover_anon_enum == RecoverAnonEnum::Yes
355+
&& self.check_noexpect(&token::BinOp(token::Or))
356+
&& self.look_ahead(1, |t| t.can_begin_type())
357+
{
358+
let mut pipes = vec![self.token.span];
359+
let mut types = vec![ty];
360+
loop {
361+
if !self.eat(&token::BinOp(token::Or)) {
362+
break;
363+
}
364+
pipes.push(self.prev_token.span);
365+
types.push(self.parse_ty_common(
366+
allow_plus,
367+
allow_c_variadic,
368+
recover_qpath,
369+
recover_return_sign,
370+
ty_generics,
371+
recover_question_mark,
372+
RecoverAnonEnum::No,
373+
)?);
374+
}
375+
let mut err = self.struct_span_err(pipes, "anonymous enums are not supported");
376+
for ty in &types {
377+
err.span_label(ty.span, "");
378+
}
379+
err.help(&format!(
380+
"create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}",
381+
types
382+
.iter()
383+
.enumerate()
384+
.map(|(i, t)| format!(
385+
" Variant{}({}),",
386+
i + 1, // Lets not confuse people with zero-indexing :)
387+
pprust::to_string(|s| s.print_type(&t)),
388+
))
389+
.collect::<Vec<_>>()
390+
.join("\n"),
391+
));
392+
err.emit();
393+
return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types)));
394+
}
336395
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
337396
}
338397

compiler/rustc_passes/src/hir_stats.rs

+1
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
579579
[
580580
Slice,
581581
Array,
582+
AnonEnum,
582583
Ptr,
583584
Ref,
584585
BareFn,

src/tools/rustfmt/src/types.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,9 @@ impl Rewrite for ast::Ty {
839839
})
840840
}
841841
ast::TyKind::CVarArgs => Some("...".to_owned()),
842-
ast::TyKind::Err => Some(context.snippet(self.span).to_owned()),
842+
ast::TyKind::AnonEnum(_) | ast::TyKind::Err => {
843+
Some(context.snippet(self.span).to_owned())
844+
}
843845
ast::TyKind::Typeof(ref anon_const) => rewrite_call(
844846
context,
845847
"typeof",

tests/ui/parser/anon-enums.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fn foo(x: bool | i32) -> i32 | f64 {
2+
//~^ ERROR anonymous enums are not supported
3+
//~| ERROR anonymous enums are not supported
4+
match x {
5+
x: i32 => x, //~ ERROR expected
6+
//~^ ERROR failed to resolve
7+
true => 42.,
8+
false => 0.333,
9+
}
10+
}
11+
12+
fn main() {
13+
match foo(true) {
14+
42: i32 => (), //~ ERROR expected
15+
_: f64 => (), //~ ERROR expected
16+
x: i32 => (), //~ ERROR expected
17+
//~^ ERROR failed to resolve
18+
}
19+
}

0 commit comments

Comments
 (0)