From 5404deeb64f4449079f5165ace6bfa1e52ca4b33 Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Wed, 14 Oct 2020 22:27:48 +0200 Subject: Gracefully handle mistyping -> as => in function return type --- compiler/rustc_parse/src/parser/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93be478fc8c..790a0c867af 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,5 +1,5 @@ use super::pat::{GateOr, PARAM_EXPECTED}; -use super::ty::{AllowPlus, RecoverQPath}; +use super::ty::{AllowPlus, RecoverFatArrow, RecoverQPath}; use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; @@ -1647,7 +1647,7 @@ impl<'a> Parser<'a> { self.expect_or()?; args }; - let output = self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes)?; + let output = self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverFatArrow::Yes)?; Ok(P(FnDecl { inputs, output })) } -- cgit 1.4.1-3-g733a5 From 3548be94c03fd6d1c11afd6af9c884f398a6489e Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 15 Oct 2020 21:21:45 +0200 Subject: Gracefully handle confusing -> with : in function return type --- compiler/rustc_parse/src/parser/expr.rs | 5 +- compiler/rustc_parse/src/parser/generics.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 7 ++- compiler/rustc_parse/src/parser/path.rs | 4 +- compiler/rustc_parse/src/parser/ty.rs | 78 +++++++++++++++++++++----- src/test/ui/fn/fn-fat-arrow-return.fixed | 18 ------ src/test/ui/fn/fn-fat-arrow-return.rs | 18 ------ src/test/ui/fn/fn-fat-arrow-return.stderr | 8 --- src/test/ui/fn/fn-fat-arrow-return2.rs | 10 ---- src/test/ui/fn/fn-fat-arrow-return2.stderr | 14 ----- src/test/ui/fn/fn-recover-return-sign.fixed | 28 +++++++++ src/test/ui/fn/fn-recover-return-sign.rs | 28 +++++++++ src/test/ui/fn/fn-recover-return-sign.stderr | 26 +++++++++ src/test/ui/fn/fn-recover-return-sign2.rs | 8 +++ src/test/ui/fn/fn-recover-return-sign2.stderr | 14 +++++ src/test/ui/parser/fn-colon-return-type.rs | 3 +- src/test/ui/parser/fn-colon-return-type.stderr | 4 +- src/test/ui/parser/not-a-pred.rs | 13 ++++- src/test/ui/parser/not-a-pred.stderr | 32 ++++++++++- 19 files changed, 223 insertions(+), 97 deletions(-) delete mode 100644 src/test/ui/fn/fn-fat-arrow-return.fixed delete mode 100644 src/test/ui/fn/fn-fat-arrow-return.rs delete mode 100644 src/test/ui/fn/fn-fat-arrow-return.stderr delete mode 100644 src/test/ui/fn/fn-fat-arrow-return2.rs delete mode 100644 src/test/ui/fn/fn-fat-arrow-return2.stderr create mode 100644 src/test/ui/fn/fn-recover-return-sign.fixed create mode 100644 src/test/ui/fn/fn-recover-return-sign.rs create mode 100644 src/test/ui/fn/fn-recover-return-sign.stderr create mode 100644 src/test/ui/fn/fn-recover-return-sign2.rs create mode 100644 src/test/ui/fn/fn-recover-return-sign2.stderr (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 790a0c867af..4d2167442be 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,5 +1,5 @@ use super::pat::{GateOr, PARAM_EXPECTED}; -use super::ty::{AllowPlus, RecoverFatArrow, RecoverQPath}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; @@ -1647,7 +1647,8 @@ impl<'a> Parser<'a> { self.expect_or()?; args }; - let output = self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverFatArrow::Yes)?; + let output = + self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?; Ok(P(FnDecl { inputs, output })) } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index dd99a7587dd..ed8d4f78426 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -240,7 +240,7 @@ impl<'a> Parser<'a> { // Parse type with mandatory colon and (possibly empty) bounds, // or with mandatory equality sign and the second type. - let ty = self.parse_ty()?; + let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9e342a0d681..ff0323a107a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error}; -use super::ty::{AllowPlus, RecoverFatArrow, RecoverQPath}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{FollowedByType, Parser, PathStyle}; use crate::maybe_whole; @@ -1514,7 +1514,7 @@ impl<'a> Parser<'a> { let header = self.parse_fn_front_matter()?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` - let decl = self.parse_fn_decl(req_name, AllowPlus::Yes)?; // `(p: u8, ...)` + let decl = self.parse_fn_decl(req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)` generics.where_clause = self.parse_where_clause()?; // `where T: Ord` let mut sig_hi = self.prev_token.span; @@ -1645,10 +1645,11 @@ impl<'a> Parser<'a> { &mut self, req_name: ReqName, ret_allow_plus: AllowPlus, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, P> { Ok(P(FnDecl { inputs: self.parse_fn_params(req_name)?, - output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, RecoverFatArrow::Yes)?, + output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?, })) } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 311a4829fcf..4510e86e034 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,4 +1,4 @@ -use super::ty::{AllowPlus, RecoverFatArrow, RecoverQPath}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; @@ -232,7 +232,7 @@ impl<'a> Parser<'a> { let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let span = ident.span.to(self.prev_token.span); let output = - self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverFatArrow::No)?; + self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; ParenthesizedArgs { inputs, output, span }.into() }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index ff19c5cfa85..ee0dc8f6304 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -43,12 +43,23 @@ pub(super) enum RecoverQPath { No, } -#[derive(PartialEq)] -pub(super) enum RecoverFatArrow { +#[derive(Copy, Clone, PartialEq)] +pub(super) enum RecoverReturnSign { Yes, + OnlyFatArrow, No, } +impl RecoverReturnSign { + fn can_recover(self, token: &TokenKind) -> bool { + match self { + Self::Yes => matches!(token, token::FatArrow | token::Colon), + Self::OnlyFatArrow => matches!(token, token::FatArrow), + Self::No => false, + } + } +} + // Is `...` (`CVarArgs`) legal at this level of type parsing? #[derive(PartialEq)] enum AllowCVariadic { @@ -68,14 +79,24 @@ fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::No) + self.parse_ty_common( + AllowPlus::Yes, + RecoverQPath::Yes, + AllowCVariadic::No, + RecoverReturnSign::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. pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::Yes) + self.parse_ty_common( + AllowPlus::Yes, + RecoverQPath::Yes, + AllowCVariadic::Yes, + RecoverReturnSign::Yes, + ) } /// Parses a type in restricted contexts where `+` is not permitted. @@ -85,7 +106,22 @@ impl<'a> Parser<'a> { /// Example 2: `value1 as TYPE + value2` /// `+` is prohibited to avoid interactions with expression grammar. pub(super) fn parse_ty_no_plus(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::No, RecoverQPath::Yes, AllowCVariadic::No) + self.parse_ty_common( + AllowPlus::No, + RecoverQPath::Yes, + AllowCVariadic::No, + RecoverReturnSign::Yes, + ) + } + + /// Parse a type without recovering `:` as `->` to avoid breaking code such as `where fn() : for<'a>` + pub(super) fn parse_ty_for_where_clause(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + RecoverQPath::Yes, + AllowCVariadic::Yes, + RecoverReturnSign::OnlyFatArrow, + ) } /// Parses an optional return type `[ -> TY ]` in a function declaration. @@ -93,13 +129,18 @@ impl<'a> Parser<'a> { &mut self, allow_plus: AllowPlus, recover_qpath: RecoverQPath, - recover_fat_arrow: RecoverFatArrow, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, FnRetTy> { Ok(if self.eat(&token::RArrow) { // FIXME(Centril): Can we unconditionally `allow_plus`? - let ty = self.parse_ty_common(allow_plus, recover_qpath, AllowCVariadic::No)?; + let ty = self.parse_ty_common( + allow_plus, + recover_qpath, + AllowCVariadic::No, + recover_return_sign, + )?; FnRetTy::Ty(ty) - } else if recover_fat_arrow == RecoverFatArrow::Yes && self.token == token::FatArrow { + } else if recover_return_sign.can_recover(&self.token.kind) { // Don't `eat` to prevent `=>` from being added as an expected token which isn't // actually expected and could only confuse users self.bump(); @@ -111,7 +152,12 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ) .emit(); - let ty = self.parse_ty_common(allow_plus, recover_qpath, AllowCVariadic::No)?; + let ty = self.parse_ty_common( + allow_plus, + recover_qpath, + AllowCVariadic::No, + recover_return_sign, + )?; FnRetTy::Ty(ty) } else { FnRetTy::Default(self.token.span.shrink_to_lo()) @@ -123,6 +169,7 @@ impl<'a> Parser<'a> { allow_plus: AllowPlus, recover_qpath: RecoverQPath, allow_c_variadic: AllowCVariadic, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -150,14 +197,14 @@ impl<'a> Parser<'a> { TyKind::Infer } else if self.check_fn_front_matter() { // Function pointer type - self.parse_ty_bare_fn(lo, Vec::new())? + self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; if self.check_fn_front_matter() { - self.parse_ty_bare_fn(lo, lifetime_defs)? + self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -359,9 +406,14 @@ impl<'a> Parser<'a> { /// Function Style ABI Parameter types /// ``` /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. - fn parse_ty_bare_fn(&mut self, lo: Span, params: Vec) -> PResult<'a, TyKind> { + fn parse_ty_bare_fn( + &mut self, + lo: Span, + params: Vec, + recover_return_sign: RecoverReturnSign, + ) -> PResult<'a, TyKind> { let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?; - let decl = self.parse_fn_decl(|_| false, AllowPlus::No)?; + let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { self.error_fn_ptr_bad_qualifier(whole_span, span, "const"); diff --git a/src/test/ui/fn/fn-fat-arrow-return.fixed b/src/test/ui/fn/fn-fat-arrow-return.fixed deleted file mode 100644 index 0acca85aa67..00000000000 --- a/src/test/ui/fn/fn-fat-arrow-return.fixed +++ /dev/null @@ -1,18 +0,0 @@ -// run-rustfix -#![allow(unused)] -fn a() -> usize { 0 } -//~^ ERROR return types are denoted using `->` - -fn bar(_: u32) {} - -fn baz() -> *const dyn Fn(u32) { unimplemented!() } - -fn foo() { - match () { - _ if baz() == &bar as &dyn Fn(u32) => (), - () => (), - } -} - -fn main() { -} diff --git a/src/test/ui/fn/fn-fat-arrow-return.rs b/src/test/ui/fn/fn-fat-arrow-return.rs deleted file mode 100644 index 4bdcd70d7fc..00000000000 --- a/src/test/ui/fn/fn-fat-arrow-return.rs +++ /dev/null @@ -1,18 +0,0 @@ -// run-rustfix -#![allow(unused)] -fn a() => usize { 0 } -//~^ ERROR return types are denoted using `->` - -fn bar(_: u32) {} - -fn baz() -> *const dyn Fn(u32) { unimplemented!() } - -fn foo() { - match () { - _ if baz() == &bar as &dyn Fn(u32) => (), - () => (), - } -} - -fn main() { -} diff --git a/src/test/ui/fn/fn-fat-arrow-return.stderr b/src/test/ui/fn/fn-fat-arrow-return.stderr deleted file mode 100644 index 1fcdb18a514..00000000000 --- a/src/test/ui/fn/fn-fat-arrow-return.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: return types are denoted using `->` - --> $DIR/fn-fat-arrow-return.rs:3:8 - | -LL | fn a() => usize { 0 } - | ^^ help: use `->` instead - -error: aborting due to previous error - diff --git a/src/test/ui/fn/fn-fat-arrow-return2.rs b/src/test/ui/fn/fn-fat-arrow-return2.rs deleted file mode 100644 index 316fa2cbf02..00000000000 --- a/src/test/ui/fn/fn-fat-arrow-return2.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn a() => impl Fn() => bool { - //~^ ERROR return types are denoted using `->` - //~| ERROR expected `;` or `{`, found `=>` - unimplemented!() -} - -fn main() { - let foo = |a: bool| => bool { a }; - dbg!(foo(false)); -} diff --git a/src/test/ui/fn/fn-fat-arrow-return2.stderr b/src/test/ui/fn/fn-fat-arrow-return2.stderr deleted file mode 100644 index a6f9fe3b573..00000000000 --- a/src/test/ui/fn/fn-fat-arrow-return2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: return types are denoted using `->` - --> $DIR/fn-fat-arrow-return2.rs:1:8 - | -LL | fn a() => impl Fn() => bool { - | ^^ help: use `->` instead - -error: expected `;` or `{`, found `=>` - --> $DIR/fn-fat-arrow-return2.rs:1:21 - | -LL | fn a() => impl Fn() => bool { - | ^^ expected `;` or `{` - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/fn/fn-recover-return-sign.fixed b/src/test/ui/fn/fn-recover-return-sign.fixed new file mode 100644 index 00000000000..076be6a35a4 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![allow(unused)] +fn a() -> usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn b()-> usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn bar(_: u32) {} + +fn baz() -> *const dyn Fn(u32) { unimplemented!() } + +fn foo() { + match () { + _ if baz() == &bar as &dyn Fn(u32) => (), + () => (), + } +} + +fn main() { + let foo = |a: bool| -> bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(foo(false)); + + let bar = |a: bool|-> bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(bar(false)); +} diff --git a/src/test/ui/fn/fn-recover-return-sign.rs b/src/test/ui/fn/fn-recover-return-sign.rs new file mode 100644 index 00000000000..0656023c0f8 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.rs @@ -0,0 +1,28 @@ +// run-rustfix +#![allow(unused)] +fn a() => usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn b(): usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn bar(_: u32) {} + +fn baz() -> *const dyn Fn(u32) { unimplemented!() } + +fn foo() { + match () { + _ if baz() == &bar as &dyn Fn(u32) => (), + () => (), + } +} + +fn main() { + let foo = |a: bool| => bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(foo(false)); + + let bar = |a: bool|: bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(bar(false)); +} diff --git a/src/test/ui/fn/fn-recover-return-sign.stderr b/src/test/ui/fn/fn-recover-return-sign.stderr new file mode 100644 index 00000000000..983109730ff --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.stderr @@ -0,0 +1,26 @@ +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:3:8 + | +LL | fn a() => usize { 0 } + | ^^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:6:7 + | +LL | fn b(): usize { 0 } + | ^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:21:25 + | +LL | let foo = |a: bool| => bool { a }; + | ^^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:25:24 + | +LL | let bar = |a: bool|: bool { a }; + | ^ help: use `->` instead + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/fn/fn-recover-return-sign2.rs b/src/test/ui/fn/fn-recover-return-sign2.rs new file mode 100644 index 00000000000..b6a6a1ec2a6 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign2.rs @@ -0,0 +1,8 @@ +// Separate test file because `Fn() => bool` isn't getting fixed and rustfix complained that +// even though a fix was applied the code was still incorrect + +fn foo() => impl Fn() => bool { + //~^ ERROR return types are denoted using `->` + //~| ERROR expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>` + unimplemented!() +} diff --git a/src/test/ui/fn/fn-recover-return-sign2.stderr b/src/test/ui/fn/fn-recover-return-sign2.stderr new file mode 100644 index 00000000000..d62cacd4bf5 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign2.stderr @@ -0,0 +1,14 @@ +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign2.rs:4:10 + | +LL | fn foo() => impl Fn() => bool { + | ^^ help: use `->` instead + +error: expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>` + --> $DIR/fn-recover-return-sign2.rs:4:23 + | +LL | fn foo() => impl Fn() => bool { + | ^^ expected one of `+`, `->`, `::`, `;`, `where`, or `{` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/fn-colon-return-type.rs b/src/test/ui/parser/fn-colon-return-type.rs index c791fb3ae67..0001ef57c99 100644 --- a/src/test/ui/parser/fn-colon-return-type.rs +++ b/src/test/ui/parser/fn-colon-return-type.rs @@ -1,4 +1,5 @@ -fn foo(x: i32): i32 { //~ ERROR expected one of `->`, `;`, `where`, or `{`, found `:` +fn foo(x: i32): i32 { +//~^ ERROR return types are denoted using `->` x } diff --git a/src/test/ui/parser/fn-colon-return-type.stderr b/src/test/ui/parser/fn-colon-return-type.stderr index 92df9bc60bd..1de91878205 100644 --- a/src/test/ui/parser/fn-colon-return-type.stderr +++ b/src/test/ui/parser/fn-colon-return-type.stderr @@ -1,8 +1,8 @@ -error: expected one of `->`, `;`, `where`, or `{`, found `:` +error: return types are denoted using `->` --> $DIR/fn-colon-return-type.rs:1:15 | LL | fn foo(x: i32): i32 { - | ^ expected one of `->`, `;`, `where`, or `{` + | ^ help: use `->` instead error: aborting due to previous error diff --git a/src/test/ui/parser/not-a-pred.rs b/src/test/ui/parser/not-a-pred.rs index 1b3d9bf66bb..5518b554d8e 100644 --- a/src/test/ui/parser/not-a-pred.rs +++ b/src/test/ui/parser/not-a-pred.rs @@ -1,6 +1,15 @@ fn f(a: isize, b: isize) : lt(a, b) { } -//~^ ERROR expected one of `->`, `;`, `where`, or `{`, found `:` +//~^ ERROR return types are denoted using `->` +//~| ERROR expected type, found function `lt` [E0573] +//~| ERROR expected type, found local variable `a` [E0573] +//~| ERROR expected type, found local variable `b` [E0573] fn lt(a: isize, b: isize) { } -fn main() { let a: isize = 10; let b: isize = 23; check (lt(a, b)); f(a, b); } +fn main() { + let a: isize = 10; + let b: isize = 23; + check (lt(a, b)); + //~^ ERROR cannot find function `check` in this scope [E0425] + f(a, b); +} diff --git a/src/test/ui/parser/not-a-pred.stderr b/src/test/ui/parser/not-a-pred.stderr index ec413c5594c..bcc64a687fd 100644 --- a/src/test/ui/parser/not-a-pred.stderr +++ b/src/test/ui/parser/not-a-pred.stderr @@ -1,8 +1,34 @@ -error: expected one of `->`, `;`, `where`, or `{`, found `:` +error: return types are denoted using `->` --> $DIR/not-a-pred.rs:1:26 | LL | fn f(a: isize, b: isize) : lt(a, b) { } - | ^ expected one of `->`, `;`, `where`, or `{` + | ^ help: use `->` instead -error: aborting due to previous error +error[E0573]: expected type, found function `lt` + --> $DIR/not-a-pred.rs:1:28 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^^^^^^^^ not a type + +error[E0573]: expected type, found local variable `a` + --> $DIR/not-a-pred.rs:1:31 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^ not a type + +error[E0573]: expected type, found local variable `b` + --> $DIR/not-a-pred.rs:1:34 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^ not a type + +error[E0425]: cannot find function `check` in this scope + --> $DIR/not-a-pred.rs:12:5 + | +LL | check (lt(a, b)); + | ^^^^^ not found in this scope + +error: aborting due to 5 previous errors +Some errors have detailed explanations: E0425, E0573. +For more information about an error, try `rustc --explain E0425`. -- cgit 1.4.1-3-g733a5 From 1a7d00a529503ac38a6b1ae28e8e779e434e02e0 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 10 Nov 2020 18:00:53 -0600 Subject: implement edition-specific :pat behavior for 2015/18 --- compiler/rustc_expand/src/mbe/macro_parser.rs | 33 +++++++++++---- compiler/rustc_parse/src/parser/expr.rs | 8 ++-- compiler/rustc_parse/src/parser/mod.rs | 1 + compiler/rustc_parse/src/parser/nonterminal.rs | 23 +++++++++-- compiler/rustc_parse/src/parser/pat.rs | 17 ++++++-- compiler/rustc_parse/src/parser/stmt.rs | 4 +- src/test/ui/macros/macro-pat-follow-2018.rs | 15 +++++++ src/test/ui/macros/macro-pat-follow.rs | 16 ++------ .../or-patterns/or-patterns-syntactic-fail-2018.rs | 15 +++++++ .../or-patterns-syntactic-fail-2018.stderr | 20 +++++++++ .../ui/or-patterns/or-patterns-syntactic-fail.rs | 10 ----- .../or-patterns/or-patterns-syntactic-fail.stderr | 48 +++++++--------------- .../or-patterns/or-patterns-syntactic-pass-2021.rs | 14 +++++++ src/test/ui/pattern/or-pattern-macro-pat.rs | 44 ++++++++++++++++++++ 14 files changed, 193 insertions(+), 75 deletions(-) create mode 100644 src/test/ui/macros/macro-pat-follow-2018.rs create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs create mode 100644 src/test/ui/pattern/or-pattern-macro-pat.rs (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index c37f9125675..3cf2d8f8ac1 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{OrPatNonterminalMode, Parser}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::MacroRulesNormalizedIdent; +use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent}; use smallvec::{smallvec, SmallVec}; @@ -414,6 +414,18 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { } } +/// In edition 2015/18, `:pat` can only match `pat` because otherwise, we have +/// breakage. As of edition 2021, `:pat` matches `top_pat`. +/// +/// See for more info. +fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode { + match edition { + Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt, + // FIXME(mark-i-m): uncomment this when edition 2021 machinery is added. + // Edition::Edition2021 => OrPatNonterminalMode::TopPat, + } +} + /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// @@ -553,10 +565,14 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(_, _, kind) => { - // Built-in nonterminals never start with these tokens, - // so we can eliminate them from consideration. - if Parser::nonterminal_may_begin_with(kind, token) { + TokenTree::MetaVarDecl(span, _, kind) => { + // Built-in nonterminals never start with these tokens, so we can eliminate + // them from consideration. + // + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. + if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition())) + { bb_items.push(item); } } @@ -717,7 +733,10 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let mut item = bb_items.pop().unwrap(); if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; - let nt = match parser.to_mut().parse_nonterminal(kind) { + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. + let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition())) + { Err(mut err) => { err.span_label( span, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4d2167442be..eed3e9947b2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,4 @@ -use super::pat::{GateOr, PARAM_EXPECTED}; +use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; @@ -1729,7 +1729,7 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; self.expect(&token::Eq)?; let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) @@ -1792,7 +1792,7 @@ impl<'a> Parser<'a> { _ => None, }; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; if !self.eat_keyword(kw::In) { self.error_missing_in_for_loop(); } @@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; let guard = if self.eat_keyword(kw::If) { let if_span = self.prev_token.span; let cond = self.parse_expr()?; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index d51a0fcbf09..e19ebb8fd2f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -12,6 +12,7 @@ mod ty; use crate::lexer::UnmatchedBrace; pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; +pub use pat::OrPatNonterminalMode; pub use path::PathStyle; use rustc_ast::ptr::P; diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 76ad5acd530..a6b9ac1014e 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,6 +4,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; +use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma}; use crate::parser::{FollowedByType, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -11,7 +12,11 @@ impl<'a> Parser<'a> { /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that /// token. Be conservative (return true) if not sure. - pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { + pub fn nonterminal_may_begin_with( + kind: NonterminalKind, + token: &Token, + or_pat_mode: OrPatNonterminalMode, + ) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { match *nt { @@ -70,6 +75,8 @@ impl<'a> Parser<'a> { token::ModSep | // path token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) + // leading vert `|` or-pattern + token::BinOp(token::Or) => matches!(or_pat_mode, OrPatNonterminalMode::TopPat), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -86,7 +93,12 @@ impl<'a> Parser<'a> { } } - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { + /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). + pub fn parse_nonterminal( + &mut self, + kind: NonterminalKind, + or_pat_mode: OrPatNonterminalMode, + ) -> PResult<'a, Nonterminal> { // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) // needs to have them force-captured here. // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, @@ -130,7 +142,12 @@ impl<'a> Parser<'a> { } } NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; + let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode { + OrPatNonterminalMode::TopPat => { + this.parse_top_pat(GateOr::Yes, RecoverComma::No) + } + OrPatNonterminalMode::NoTopAlt => this.parse_pat(None), + })?; // We have have eaten an NtPat, which could already have tokens if pat.tokens.is_none() { pat.tokens = tokens; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b62c7373800..1da371e0b72 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -26,11 +26,18 @@ pub(super) enum GateOr { /// Whether or not to recover a `,` when parsing or-patterns. #[derive(PartialEq, Copy, Clone)] -enum RecoverComma { +pub(super) enum RecoverComma { Yes, No, } +/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match +/// `top_pat` or `pat`. See issue . +pub enum OrPatNonterminalMode { + TopPat, + NoTopAlt, +} + impl<'a> Parser<'a> { /// Parses a pattern. /// @@ -43,13 +50,17 @@ impl<'a> Parser<'a> { /// Entry point to the main pattern parser. /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P> { + pub(super) fn parse_top_pat( + &mut self, + gate_or: GateOr, + rc: RecoverComma, + ) -> PResult<'a, P> { // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes; let leading_vert_span = self.prev_token.span; // Parse the possibly-or-pattern. - let pat = self.parse_pat_with_or(None, gate_or, RecoverComma::Yes)?; + let pat = self.parse_pat_with_or(None, gate_or, rc)?; // If we parsed a leading `|` which should be gated, // and no other gated or-pattern has been parsed thus far, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index e974556f43a..2942747991a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,7 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; -use super::pat::GateOr; +use super::pat::{GateOr, RecoverComma}; use super::path::PathStyle; use super::{BlockMode, Parser, Restrictions, SemiColonMode}; use crate::maybe_whole; @@ -185,7 +185,7 @@ impl<'a> Parser<'a> { /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; let (err, ty) = if self.eat(&token::Colon) { // Save the state of the parser before parsing type normally, in case there is a `:` diff --git a/src/test/ui/macros/macro-pat-follow-2018.rs b/src/test/ui/macros/macro-pat-follow-2018.rs new file mode 100644 index 00000000000..ce2911de986 --- /dev/null +++ b/src/test/ui/macros/macro-pat-follow-2018.rs @@ -0,0 +1,15 @@ +// run-pass +// edition:2018 + +macro_rules! pat_bar { + ($p:pat | $p2:pat) => {{ + match Some(1u8) { + $p | $p2 => {} + _ => {} + } + }}; +} + +fn main() { + pat_bar!(Some(1u8) | None); +} diff --git a/src/test/ui/macros/macro-pat-follow.rs b/src/test/ui/macros/macro-pat-follow.rs index 8673cf79467..8e02789fdd8 100644 --- a/src/test/ui/macros/macro-pat-follow.rs +++ b/src/test/ui/macros/macro-pat-follow.rs @@ -3,29 +3,19 @@ macro_rules! pat_in { ($p:pat in $e:expr) => {{ let mut iter = $e.into_iter(); while let $p = iter.next() {} - }} + }}; } macro_rules! pat_if { ($p:pat if $e:expr) => {{ match Some(1u8) { - $p if $e => {}, + $p if $e => {} _ => {} } - }} -} - -macro_rules! pat_bar { - ($p:pat | $p2:pat) => {{ - match Some(1u8) { - $p | $p2 => {}, - _ => {} - } - }} + }}; } fn main() { pat_in!(Some(_) in 0..10); pat_if!(Some(x) if x > 0); - pat_bar!(Some(1u8) | None); } diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs new file mode 100644 index 00000000000..9c3c5dd360e --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs @@ -0,0 +1,15 @@ +// Test that :pat doesn't accept top-level or-patterns in edition 2018. + +// edition:2018 + +#![feature(or_patterns)] + +fn main() {} + +// Test the `pat` macro fragment parser: +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); //~ ERROR no rules expected the token `|` +accept_pat!(|p| q); //~ ERROR no rules expected the token `|` diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr new file mode 100644 index 00000000000..7dbc3087663 --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr @@ -0,0 +1,20 @@ +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:14:15 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(p | q); + | ^ no rules expected this token in macro call + +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:15:13 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(|p| q); + | ^ no rules expected this token in macro call + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs index d2322005652..efe90b3e3c6 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -5,16 +5,6 @@ fn main() {} -// Test the `pat` macro fragment parser: -macro_rules! accept_pat { - ($p:pat) => {} -} - -accept_pat!(p | q); //~ ERROR no rules expected the token `|` -accept_pat!(| p | q); //~ ERROR no rules expected the token `|` - -// Non-macro tests: - enum E { A, B } use E::*; diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index 861d274ab5c..989aeb52006 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -1,53 +1,53 @@ error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:27:13 + --> $DIR/or-patterns-syntactic-fail.rs:17:13 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is not allowed in a parameter pattern - --> $DIR/or-patterns-syntactic-fail.rs:29:13 + --> $DIR/or-patterns-syntactic-fail.rs:19:13 | LL | fn fun2(| A | B: E) {} | ^ help: remove the `|` error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:29:15 + --> $DIR/or-patterns-syntactic-fail.rs:19:15 | LL | fn fun2(| A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:40:11 + --> $DIR/or-patterns-syntactic-fail.rs:30:11 | LL | let ( | A | B) = E::A; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:41:11 + --> $DIR/or-patterns-syntactic-fail.rs:31:11 | LL | let ( | A | B,) = (E::B,); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:42:11 + --> $DIR/or-patterns-syntactic-fail.rs:32:11 | LL | let [ | A | B ] = [E::A]; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:43:13 + --> $DIR/or-patterns-syntactic-fail.rs:33:13 | LL | let TS( | A | B ); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:44:17 + --> $DIR/or-patterns-syntactic-fail.rs:34:17 | LL | let NS { f: | A | B }; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:46:11 + --> $DIR/or-patterns-syntactic-fail.rs:36:11 | LL | let ( || A | B) = E::A; | ^^ help: remove the `||` @@ -55,7 +55,7 @@ LL | let ( || A | B) = E::A; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:47:11 + --> $DIR/or-patterns-syntactic-fail.rs:37:11 | LL | let [ || A | B ] = [E::A]; | ^^ help: remove the `||` @@ -63,7 +63,7 @@ LL | let [ || A | B ] = [E::A]; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:48:13 + --> $DIR/or-patterns-syntactic-fail.rs:38:13 | LL | let TS( || A | B ); | ^^ help: remove the `||` @@ -71,33 +71,15 @@ LL | let TS( || A | B ); = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:49:17 + --> $DIR/or-patterns-syntactic-fail.rs:39:17 | LL | let NS { f: || A | B }; | ^^ help: remove the `||` | = note: alternatives in or-patterns are separated with `|`, not `||` -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:13:15 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(p | q); - | ^ no rules expected this token in macro call - -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:14:13 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(| p | q); - | ^ no rules expected this token in macro call - error[E0369]: no implementation for `E | ()` - --> $DIR/or-patterns-syntactic-fail.rs:23:22 + --> $DIR/or-patterns-syntactic-fail.rs:13:22 | LL | let _ = |A | B: E| (); | ----^ -- () @@ -107,7 +89,7 @@ LL | let _ = |A | B: E| (); = note: an implementation of `std::ops::BitOr` might be missing for `E` error[E0308]: mismatched types - --> $DIR/or-patterns-syntactic-fail.rs:51:36 + --> $DIR/or-patterns-syntactic-fail.rs:41:36 | LL | let recovery_witness: String = 0; | ------ ^ @@ -116,7 +98,7 @@ LL | let recovery_witness: String = 0; | | help: try using a conversion method: `0.to_string()` | expected due to this -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs new file mode 100644 index 00000000000..f0ce7597aee --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs @@ -0,0 +1,14 @@ +// Tests that :pat in macros in edition 2021 allows top-level or-patterns. + +// run-pass +// ignore-test +// edition:2021 +// FIXME(mark-i-m): unignore when 2021 machinery is in place. + +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); + +fn main() {} diff --git a/src/test/ui/pattern/or-pattern-macro-pat.rs b/src/test/ui/pattern/or-pattern-macro-pat.rs new file mode 100644 index 00000000000..8749407675b --- /dev/null +++ b/src/test/ui/pattern/or-pattern-macro-pat.rs @@ -0,0 +1,44 @@ +// run-pass +// edition:2021 +// ignore-test +// FIXME(mark-i-m): enable this test again when 2021 machinery is available + +#![feature(or_patterns)] + +use Foo::*; + +#[derive(Eq, PartialEq, Debug)] +enum Foo { + A(u64), + B(u64), + C, + D, +} + +macro_rules! foo { + ($orpat:pat, $val:expr) => { + match $val { + x @ ($orpat) => x, // leading vert would not be allowed in $orpat + _ => B(0xDEADBEEFu64), + } + }; +} + +macro_rules! bar { + ($orpat:pat, $val:expr) => { + match $val { + $orpat => 42, // leading vert allowed here + _ => 0xDEADBEEFu64, + } + }; +} + +fn main() { + // Test or-pattern. + let y = foo!(A(_)|B(_), A(32)); + assert_eq!(y, A(32)); + + // Leading vert in or-pattern. + let y = bar!(|C| D, C); + assert_eq!(y, 42u64); +} -- cgit 1.4.1-3-g733a5 From a272d621bc7a2ca61d704fbe531dc532d49ab402 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 18 Dec 2020 17:32:26 +0100 Subject: Implemented a compiler diagnostic for move async mistake Ran the tidy check Following the diagnostic guide better Diagnostic generation is now relegated to its own function in the diagnostics module. Added tests Fixed the ui test --- compiler/rustc_parse/src/parser/diagnostics.rs | 18 ++++++++++++++++++ compiler/rustc_parse/src/parser/expr.rs | 18 ++++++++++++++---- .../incorrect-move-async-order-issue-79694.fixed | 8 ++++++++ .../parser/incorrect-move-async-order-issue-79694.rs | 8 ++++++++ .../incorrect-move-async-order-issue-79694.stderr | 13 +++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed create mode 100644 src/test/ui/parser/incorrect-move-async-order-issue-79694.rs create mode 100644 src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 350a372a684..98c7b9a63a5 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1912,4 +1912,22 @@ impl<'a> Parser<'a> { *self = snapshot; Err(err) } + + /// Get the diagnostics for the cases where `move async` is found. + /// + /// `move_async_span` starts at the 'm' of the move keyword and ends with the 'c' of the async keyword + pub(super) fn incorrect_move_async_order_found( + &self, + move_async_span: Span, + ) -> DiagnosticBuilder<'a> { + let mut err = + self.struct_span_err(move_async_span, "the order of `move` and `async` is incorrect"); + err.span_suggestion_verbose( + move_async_span, + "try switching the order", + "async move".to_owned(), + Applicability::MaybeIncorrect, + ); + err + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93be478fc8c..7d0d4f30137 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1603,7 +1603,7 @@ impl<'a> Parser<'a> { self.sess.gated_spans.gate(sym::async_closure, span); } - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let decl = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; let body = match decl.output { @@ -1626,8 +1626,18 @@ impl<'a> Parser<'a> { } /// Parses an optional `move` prefix to a closure-like construct. - fn parse_capture_clause(&mut self) -> CaptureBy { - if self.eat_keyword(kw::Move) { CaptureBy::Value } else { CaptureBy::Ref } + fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { + if self.eat_keyword(kw::Move) { + // Check for `move async` and recover + if self.check_keyword(kw::Async) { + let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); + Err(self.incorrect_move_async_order_found(move_async_span)) + } else { + Ok(CaptureBy::Value) + } + } else { + Ok(CaptureBy::Ref) + } } /// Parses the `|arg, arg|` header of a closure. @@ -2018,7 +2028,7 @@ impl<'a> Parser<'a> { fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; self.expect_keyword(kw::Async)?; - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body); diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed b/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed new file mode 100644 index 00000000000..055800d23b6 --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed @@ -0,0 +1,8 @@ +// run-rustfix +// edition:2018 + +// Regression test for issue 79694 + +fn main() { + let _ = async move { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect +} diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs b/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs new file mode 100644 index 00000000000..e8be16516d6 --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs @@ -0,0 +1,8 @@ +// run-rustfix +// edition:2018 + +// Regression test for issue 79694 + +fn main() { + let _ = move async { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect +} diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr b/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr new file mode 100644 index 00000000000..2add9fb33c7 --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr @@ -0,0 +1,13 @@ +error: the order of `move` and `async` is incorrect + --> $DIR/incorrect-move-async-order-issue-79694.rs:7:13 + | +LL | let _ = move async { }; + | ^^^^^^^^^^ + | +help: try switching the order + | +LL | let _ = async move { }; + | ^^^^^^^^^^ + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5