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);
visitor.visit_ty(&mutable_type.ty)
}
TyKind::Tup(tuple_element_types) => {
walk_list!(visitor, visit_ty, tuple_element_types);
TyKind::Tup(tys) => {
walk_list!(visitor, visit_ty, tys);
}
TyKind::BareFn(function_declaration) => {
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,
/// `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 first_pat: P<Pat>,
expected: Expected,
@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> {
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
|| !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;
}
// The pattern looks like it might be a path with a `::` -> `:` typo:
// `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
// 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 `:`".
match self.expected_one_of_not_found(&[], &[]) {
Err(mut err) => {
self.bump(); // Skip the `:`.
match self.parse_pat_no_top_alt(expected) {
snapshot_pat.bump(); // Skip the `:`.
snapshot_type.bump(); // Skip the `:`.
match snapshot_pat.parse_pat_no_top_alt(expected) {
Err(inner_err) => {
// Carry on as if we had not done anything, callers will emit a
// reasonable error.
inner_err.cancel();
err.cancel();
self.restore_snapshot(snapshot);
}
Ok(mut pat) => {
// We've parsed the rest of the pattern.
@ -2466,8 +2481,8 @@ impl<'a> Parser<'a> {
_ => {}
}
if show_sugg {
err.span_suggestion(
span,
err.span_suggestion_verbose(
colon_span.until(self.look_ahead(1, |t| t.span)),
"maybe write a path separator here",
"::",
Applicability::MaybeIncorrect,
@ -2475,13 +2490,24 @@ impl<'a> Parser<'a> {
} else {
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.
self.restore_snapshot(snapshot);
}
};
first_pat

View file

@ -116,7 +116,8 @@ impl<'a> Parser<'a> {
// Check if the user wrote `foo:bar` instead of `foo::bar`.
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 {

View file

@ -11,6 +11,7 @@ use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident};
@ -43,17 +44,24 @@ pub(super) enum AllowPlus {
No,
}
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQPath {
Yes,
No,
}
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQuestionMark {
Yes,
No,
}
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverAnonEnum {
Yes,
No,
}
/// Signals whether parsing a type should recover `->`.
///
/// More specifically, when parsing a function like:
@ -86,7 +94,7 @@ impl RecoverReturnSign {
}
// Is `...` (`CVarArgs`) legal at this level of type parsing?
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
enum AllowCVariadic {
Yes,
No,
@ -111,6 +119,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -125,6 +134,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
Some(ty_params),
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -139,6 +149,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)
}
@ -156,6 +167,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -169,6 +181,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
RecoverAnonEnum::No,
)
}
@ -180,6 +193,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
RecoverAnonEnum::No,
)
}
@ -192,6 +206,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::OnlyFatArrow,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -211,6 +226,7 @@ impl<'a> Parser<'a> {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
@ -232,6 +248,7 @@ impl<'a> Parser<'a> {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else {
@ -247,6 +264,7 @@ impl<'a> Parser<'a> {
recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>,
recover_question_mark: RecoverQuestionMark,
recover_anon_enum: RecoverAnonEnum,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
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);
// 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)?;
} else {
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);
}
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) }
}

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 => {}
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
//~| ERROR: failed to resolve: `Bar` is a variant, not a module
}
match myfoo {
Foo::Bar => {}

View file

@ -2,89 +2,118 @@ error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:17:12
|
LL | Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:23:17
|
LL | qux::Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:29:12
|
LL | qux:Foo::Baz => {}
| ^
| ^-------- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:35:12
|
LL | qux: Foo::Baz if true => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:40:15
|
LL | if let Foo:Bar = f() {
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:48:16
|
LL | ref qux: Foo::Baz => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:57:16
|
LL | mut qux: Foo::Baz => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:68:12
|
LL | Foo:Bar::Baz => {}
| ^
| ^-------- specifying the type of a pattern isn't supported
| |
| 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 `:`
--> $DIR/issue-87086-colon-path-sep.rs:75:12
--> $DIR/issue-87086-colon-path-sep.rs:74:12
|
LL | Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| 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 => {}
| ^^^ `Bar` is a variant, not a module
help: maybe write a path separator here
|
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`.