Rollup merge of #106960 - estebank:parse-anon-enums, r=cjgillot

Teach parser to understand fake anonymous enum syntax

Parse `Ty | OtherTy` in function argument and return types.
Parse type ascription in top level patterns.

Minimally address #100741.
This commit is contained in:
Matthias Krüger 2023-01-26 06:15:24 +01:00 committed by GitHub
commit ba928ba041
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 266 additions and 47 deletions

View file

@ -400,8 +400,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref); walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
visitor.visit_ty(&mutable_type.ty) visitor.visit_ty(&mutable_type.ty)
} }
TyKind::Tup(tuple_element_types) => { TyKind::Tup(tys) => {
walk_list!(visitor, visit_ty, tuple_element_types); walk_list!(visitor, visit_ty, tys);
} }
TyKind::BareFn(function_declaration) => { TyKind::BareFn(function_declaration) => {
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params); walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);

View file

@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> {
/// Some special error handling for the "top-level" patterns in a match arm, /// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such). /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
pub(crate) fn maybe_recover_colon_colon_in_pat_typo( pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
&mut self, &mut self,
mut first_pat: P<Pat>, mut first_pat: P<Pat>,
expected: Expected, expected: Expected,
@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> {
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..)) if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
|| !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident()) || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
{ {
let mut snapshot_type = self.create_snapshot_for_diagnostic();
snapshot_type.bump(); // `:`
match snapshot_type.parse_ty() {
Err(inner_err) => {
inner_err.cancel();
}
Ok(ty) => {
let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
return first_pat;
};
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(span, PatKind::Wild);
err.emit();
}
}
return first_pat; return first_pat;
} }
// The pattern looks like it might be a path with a `::` -> `:` typo: // The pattern looks like it might be a path with a `::` -> `:` typo:
// `match foo { bar:baz => {} }` // `match foo { bar:baz => {} }`
let span = self.token.span; let colon_span = self.token.span;
// We only emit "unexpected `:`" error here if we can successfully parse the // We only emit "unexpected `:`" error here if we can successfully parse the
// whole pattern correctly in that case. // whole pattern correctly in that case.
let snapshot = self.create_snapshot_for_diagnostic(); let mut snapshot_pat = self.create_snapshot_for_diagnostic();
let mut snapshot_type = self.create_snapshot_for_diagnostic();
// Create error for "unexpected `:`". // Create error for "unexpected `:`".
match self.expected_one_of_not_found(&[], &[]) { match self.expected_one_of_not_found(&[], &[]) {
Err(mut err) => { Err(mut err) => {
self.bump(); // Skip the `:`. snapshot_pat.bump(); // Skip the `:`.
match self.parse_pat_no_top_alt(expected) { snapshot_type.bump(); // Skip the `:`.
match snapshot_pat.parse_pat_no_top_alt(expected) {
Err(inner_err) => { Err(inner_err) => {
// Carry on as if we had not done anything, callers will emit a
// reasonable error.
inner_err.cancel(); inner_err.cancel();
err.cancel();
self.restore_snapshot(snapshot);
} }
Ok(mut pat) => { Ok(mut pat) => {
// We've parsed the rest of the pattern. // We've parsed the rest of the pattern.
@ -2466,8 +2481,8 @@ impl<'a> Parser<'a> {
_ => {} _ => {}
} }
if show_sugg { if show_sugg {
err.span_suggestion( err.span_suggestion_verbose(
span, colon_span.until(self.look_ahead(1, |t| t.span)),
"maybe write a path separator here", "maybe write a path separator here",
"::", "::",
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
@ -2475,13 +2490,24 @@ impl<'a> Parser<'a> {
} else { } else {
first_pat = self.mk_pat(new_span, PatKind::Wild); first_pat = self.mk_pat(new_span, PatKind::Wild);
} }
err.emit(); self.restore_snapshot(snapshot_pat);
} }
} }
match snapshot_type.parse_ty() {
Err(inner_err) => {
inner_err.cancel();
}
Ok(ty) => {
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let new_span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(new_span, PatKind::Wild);
}
}
err.emit();
} }
_ => { _ => {
// Carry on as if we had not done anything. This should be unreachable. // Carry on as if we had not done anything. This should be unreachable.
self.restore_snapshot(snapshot);
} }
}; };
first_pat first_pat

View file

@ -116,7 +116,8 @@ impl<'a> Parser<'a> {
// Check if the user wrote `foo:bar` instead of `foo::bar`. // Check if the user wrote `foo:bar` instead of `foo::bar`.
if ra == RecoverColon::Yes { if ra == RecoverColon::Yes {
first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected); first_pat =
self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected);
} }
if let Some(leading_vert_span) = leading_vert_span { if let Some(leading_vert_span) = leading_vert_span {

View file

@ -11,6 +11,7 @@ use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
}; };
use rustc_ast_pretty::pprust;
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
@ -43,17 +44,24 @@ pub(super) enum AllowPlus {
No, No,
} }
#[derive(PartialEq)] #[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQPath { pub(super) enum RecoverQPath {
Yes, Yes,
No, No,
} }
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQuestionMark { pub(super) enum RecoverQuestionMark {
Yes, Yes,
No, No,
} }
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverAnonEnum {
Yes,
No,
}
/// Signals whether parsing a type should recover `->`. /// Signals whether parsing a type should recover `->`.
/// ///
/// More specifically, when parsing a function like: /// More specifically, when parsing a function like:
@ -86,7 +94,7 @@ impl RecoverReturnSign {
} }
// Is `...` (`CVarArgs`) legal at this level of type parsing? // Is `...` (`CVarArgs`) legal at this level of type parsing?
#[derive(PartialEq)] #[derive(PartialEq, Clone, Copy)]
enum AllowCVariadic { enum AllowCVariadic {
Yes, Yes,
No, No,
@ -111,6 +119,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
) )
} }
@ -125,6 +134,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
Some(ty_params), Some(ty_params),
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
) )
} }
@ -139,6 +149,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
) )
} }
@ -156,6 +167,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
) )
} }
@ -169,6 +181,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
None, None,
RecoverQuestionMark::No, RecoverQuestionMark::No,
RecoverAnonEnum::No,
) )
} }
@ -180,6 +193,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes, RecoverReturnSign::Yes,
None, None,
RecoverQuestionMark::No, RecoverQuestionMark::No,
RecoverAnonEnum::No,
) )
} }
@ -192,6 +206,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::OnlyFatArrow, RecoverReturnSign::OnlyFatArrow,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
) )
} }
@ -211,6 +226,7 @@ impl<'a> Parser<'a> {
recover_return_sign, recover_return_sign,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?; )?;
FnRetTy::Ty(ty) FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) { } else if recover_return_sign.can_recover(&self.token.kind) {
@ -232,6 +248,7 @@ impl<'a> Parser<'a> {
recover_return_sign, recover_return_sign,
None, None,
RecoverQuestionMark::Yes, RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?; )?;
FnRetTy::Ty(ty) FnRetTy::Ty(ty)
} else { } else {
@ -247,6 +264,7 @@ impl<'a> Parser<'a> {
recover_return_sign: RecoverReturnSign, recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>, ty_generics: Option<&Generics>,
recover_question_mark: RecoverQuestionMark, recover_question_mark: RecoverQuestionMark,
recover_anon_enum: RecoverAnonEnum,
) -> PResult<'a, P<Ty>> { ) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
@ -325,14 +343,55 @@ impl<'a> Parser<'a> {
let mut ty = self.mk_ty(span, kind); let mut ty = self.mk_ty(span, kind);
// Try to recover from use of `+` with incorrect priority. // Try to recover from use of `+` with incorrect priority.
if matches!(allow_plus, AllowPlus::Yes) { if allow_plus == AllowPlus::Yes {
self.maybe_recover_from_bad_type_plus(&ty)?; self.maybe_recover_from_bad_type_plus(&ty)?;
} else { } else {
self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty); self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty);
} }
if let RecoverQuestionMark::Yes = recover_question_mark { if RecoverQuestionMark::Yes == recover_question_mark {
ty = self.maybe_recover_from_question_mark(ty); ty = self.maybe_recover_from_question_mark(ty);
} }
if recover_anon_enum == RecoverAnonEnum::Yes
&& self.check_noexpect(&token::BinOp(token::Or))
&& self.look_ahead(1, |t| t.can_begin_type())
{
let mut pipes = vec![self.token.span];
let mut types = vec![ty];
loop {
if !self.eat(&token::BinOp(token::Or)) {
break;
}
pipes.push(self.prev_token.span);
types.push(self.parse_ty_common(
allow_plus,
allow_c_variadic,
recover_qpath,
recover_return_sign,
ty_generics,
recover_question_mark,
RecoverAnonEnum::No,
)?);
}
let mut err = self.struct_span_err(pipes, "anonymous enums are not supported");
for ty in &types {
err.span_label(ty.span, "");
}
err.help(&format!(
"create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}",
types
.iter()
.enumerate()
.map(|(i, t)| format!(
" Variant{}({}),",
i + 1, // Lets not confuse people with zero-indexing :)
pprust::to_string(|s| s.print_type(&t)),
))
.collect::<Vec<_>>()
.join("\n"),
));
err.emit();
return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err));
}
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
} }

View file

@ -0,0 +1,17 @@
fn foo(x: bool | i32) -> i32 | f64 {
//~^ ERROR anonymous enums are not supported
//~| ERROR anonymous enums are not supported
match x {
x: i32 => x, //~ ERROR expected
true => 42.,
false => 0.333,
}
}
fn main() {
match foo(true) {
42: i32 => (), //~ ERROR expected
_: f64 => (), //~ ERROR expected
x: i32 => (), //~ ERROR expected
}
}

View file

@ -0,0 +1,68 @@
error: anonymous enums are not supported
--> $DIR/anon-enums.rs:1:16
|
LL | fn foo(x: bool | i32) -> i32 | f64 {
| ---- ^ ---
|
= help: create a named `enum` and use it here instead:
enum Name {
Variant1(bool),
Variant2(i32),
}
error: anonymous enums are not supported
--> $DIR/anon-enums.rs:1:30
|
LL | fn foo(x: bool | i32) -> i32 | f64 {
| --- ^ ---
|
= help: create a named `enum` and use it here instead:
enum Name {
Variant1(i32),
Variant2(f64),
}
error: expected one of `@` or `|`, found `:`
--> $DIR/anon-enums.rs:5:10
|
LL | x: i32 => x,
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|
help: maybe write a path separator here
|
LL | x::i32 => x,
| ~~
error: expected one of `...`, `..=`, `..`, or `|`, found `:`
--> $DIR/anon-enums.rs:13:11
|
LL | 42: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `...`, `..=`, `..`, or `|`
error: expected `|`, found `:`
--> $DIR/anon-enums.rs:14:10
|
LL | _: f64 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected `|`
error: expected one of `@` or `|`, found `:`
--> $DIR/anon-enums.rs:15:10
|
LL | x: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|
help: maybe write a path separator here
|
LL | x::i32 => (),
| ~~
error: aborting due to 6 previous errors

View file

@ -0,0 +1,20 @@
// build-pass
macro_rules! check_ty {
($Z:ty) => { compile_error!("triggered"); };
($X:ty | $Y:ty) => { $X };
}
macro_rules! check {
($Z:ty) => { compile_error!("triggered"); };
($X:ty | $Y:ty) => { };
}
check! { i32 | u8 }
fn foo(x: check_ty! { i32 | u8 }) -> check_ty! { i32 | u8 } {
x
}
fn main() {
let x: check_ty! { i32 | u8 } = 42;
let _: check_ty! { i32 | u8 } = foo(x);
}

View file

@ -68,7 +68,6 @@ fn main() {
Foo:Bar::Baz => {} Foo:Bar::Baz => {}
//~^ ERROR: expected one of //~^ ERROR: expected one of
//~| HELP: maybe write a path separator here //~| HELP: maybe write a path separator here
//~| ERROR: failed to resolve: `Bar` is a variant, not a module
} }
match myfoo { match myfoo {
Foo::Bar => {} Foo::Bar => {}

View file

@ -2,89 +2,118 @@ error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:17:12 --> $DIR/issue-87086-colon-path-sep.rs:17:12
| |
LL | Foo:Bar => {} LL | Foo:Bar => {}
| ^ | ^--- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | Foo::Bar => {}
| ~~
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:` error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:23:17 --> $DIR/issue-87086-colon-path-sep.rs:23:17
| |
LL | qux::Foo:Bar => {} LL | qux::Foo:Bar => {}
| ^ | ^--- specifying the type of a pattern isn't supported
| | | |
| expected one of 8 possible tokens | expected one of 8 possible tokens
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | qux::Foo::Bar => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:29:12 --> $DIR/issue-87086-colon-path-sep.rs:29:12
| |
LL | qux:Foo::Baz => {} LL | qux:Foo::Baz => {}
| ^ | ^-------- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:35:12 --> $DIR/issue-87086-colon-path-sep.rs:35:12
| |
LL | qux: Foo::Baz if true => {} LL | qux: Foo::Baz if true => {}
| ^ | ^ -------- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | qux::Foo::Baz if true => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:40:15 --> $DIR/issue-87086-colon-path-sep.rs:40:15
| |
LL | if let Foo:Bar = f() { LL | if let Foo:Bar = f() {
| ^ | ^--- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | if let Foo::Bar = f() {
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:48:16 --> $DIR/issue-87086-colon-path-sep.rs:48:16
| |
LL | ref qux: Foo::Baz => {} LL | ref qux: Foo::Baz => {}
| ^ | ^ -------- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | ref qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:57:16 --> $DIR/issue-87086-colon-path-sep.rs:57:16
| |
LL | mut qux: Foo::Baz => {} LL | mut qux: Foo::Baz => {}
| ^ | ^ -------- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | mut qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:68:12 --> $DIR/issue-87086-colon-path-sep.rs:68:12
| |
LL | Foo:Bar::Baz => {} LL | Foo:Bar::Baz => {}
| ^ | ^-------- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::` |
help: maybe write a path separator here
|
LL | Foo::Bar::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:` error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:75:12 --> $DIR/issue-87086-colon-path-sep.rs:74:12
| |
LL | Foo:Bar => {} LL | Foo:Bar => {}
| ^ | ^--- specifying the type of a pattern isn't supported
| | | |
| expected one of `@` or `|` | expected one of `@` or `|`
| help: maybe write a path separator here: `::`
error[E0433]: failed to resolve: `Bar` is a variant, not a module
--> $DIR/issue-87086-colon-path-sep.rs:68:13
| |
LL | Foo:Bar::Baz => {} help: maybe write a path separator here
| ^^^ `Bar` is a variant, not a module |
LL | Foo::Bar => {}
| ~~
error: aborting due to 10 previous errors error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0433`.