diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index aa43c15e286..d2bcf31cb05 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -798,12 +798,12 @@ impl<'a> Parser<'a> { let defaultness = self.parse_defaultness(); let (name, kind, generics) = if self.eat_keyword(kw::Type) { self.parse_assoc_ty()? - } else if self.is_const_item() { - self.parse_assoc_const()? + } else if self.is_fn_front_matter() { + self.parse_assoc_fn(at_end, &mut attrs, is_name_required)? } else if let Some(mac) = self.parse_assoc_macro_invoc("associated", Some(&vis), at_end)? { (Ident::invalid(), AssocItemKind::Macro(mac), Generics::default()) } else { - self.parse_assoc_fn(at_end, &mut attrs, is_name_required)? + self.parse_assoc_const()? }; Ok(AssocItem { @@ -819,12 +819,6 @@ impl<'a> Parser<'a> { }) } - /// Returns `true` if we are looking at `const ID` - /// (returns `false` for things like `const fn`, etc.). - fn is_const_item(&self) -> bool { - self.token.is_keyword(kw::Const) && !self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe]) - } - /// This parses the grammar: /// /// AssocConst = "const" Ident ":" Ty "=" Expr ";" @@ -1034,21 +1028,20 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - let visibility = self.parse_visibility(FollowedByType::No)?; + let vis = self.parse_visibility(FollowedByType::No)?; - // FOREIGN TYPE ITEM if self.check_keyword(kw::Type) { - return self.parse_item_foreign_type(visibility, lo, attrs); - } - - // FOREIGN STATIC ITEM - if self.is_static_global() { + // FOREIGN TYPE ITEM + self.parse_item_foreign_type(vis, lo, attrs) + } else if self.is_fn_front_matter() { + // FOREIGN FUNCTION ITEM + self.parse_item_foreign_fn(vis, lo, attrs) + } else if self.is_static_global() { + // FOREIGN STATIC ITEM self.bump(); // `static` - return self.parse_item_foreign_static(visibility, lo, attrs); - } - - // Treat `const` as `static` for error recovery, but don't add it to expected tokens. - if self.is_kw_followed_by_ident(kw::Const) { + self.parse_item_foreign_static(vis, lo, attrs) + } else if self.token.is_keyword(kw::Const) { + // Treat `const` as `static` for error recovery, but don't add it to expected tokens. self.bump(); // `const` self.struct_span_err(self.prev_span, "extern items cannot be `const`") .span_suggestion( @@ -1058,32 +1051,17 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ) .emit(); - return self.parse_item_foreign_static(visibility, lo, attrs); - } - - // FOREIGN FUNCTION ITEM - const MAY_INTRODUCE_FN: &[Symbol] = &[kw::Const, kw::Async, kw::Unsafe, kw::Extern, kw::Fn]; - if MAY_INTRODUCE_FN.iter().any(|&kw| self.check_keyword(kw)) { - return self.parse_item_foreign_fn(visibility, lo, attrs); - } - - match self.parse_assoc_macro_invoc("extern", Some(&visibility), &mut false)? { - Some(mac) => Ok(P(ForeignItem { - ident: Ident::invalid(), - span: lo.to(self.prev_span), - id: DUMMY_NODE_ID, - attrs, - vis: visibility, - kind: ForeignItemKind::Macro(mac), - tokens: None, - })), - None => { - if !attrs.is_empty() { - self.expected_item_err(&attrs)?; - } - - self.unexpected() + self.parse_item_foreign_static(vis, lo, attrs) + } else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), &mut false)? { + let kind = ForeignItemKind::Macro(mac); + let span = lo.to(self.prev_span); + let ident = Ident::invalid(); + Ok(P(ForeignItem { ident, span, id: DUMMY_NODE_ID, attrs, vis, kind, tokens: None })) + } else { + if !attrs.is_empty() { + self.expected_item_err(&attrs)?; } + self.unexpected() } } @@ -1752,6 +1730,29 @@ impl<'a> Parser<'a> { Ok(body) } + /// Is the current token unambiguously the start of an `FnHeader`? + fn is_fn_front_matter(&mut self) -> bool { + // We use an over-approximation here. + // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. + // This works for `async fn` and similar as `async async` is an invalid + // parse and `async fn` is never a valid parse on previous editions. + const QUALIFIER: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern]; + + let check_qual_follow = |this: &mut Self, dist| { + this.look_ahead(dist, |t| { + // ...qualified and then `fn`, e.g. `const fn`. + t.is_keyword(kw::Fn) + // Two qualifiers. This is enough. + || QUALIFIER.iter().any(|&kw| t.is_keyword(kw)) + }) + }; + self.check_keyword(kw::Fn) // Definitely an `fn`. + // `$qual fn` or `$qual $qual`: + || QUALIFIER.iter().any(|&kw| self.check_keyword(kw)) && check_qual_follow(self, 1) + // `extern ABI fn` or `extern ABI $qual`; skip 1 for the ABI. + || self.check_keyword(kw::Extern) && check_qual_follow(self, 2) + } + /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, /// up to and including the `fn` keyword. The formal grammar is: /// @@ -1763,16 +1764,13 @@ impl<'a> Parser<'a> { fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let constness = self.parse_constness(); let asyncness = self.parse_asyncness(); + let unsafety = self.parse_unsafety(); + let ext = self.parse_extern()?; + if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - let unsafety = self.parse_unsafety(); - let (constness, unsafety, ext) = if let Const::Yes(_) = constness { - (constness, unsafety, Extern::None) - } else { - let ext = self.parse_extern()?; - (Const::No, unsafety, ext) - }; + if !self.eat_keyword(kw::Fn) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't @@ -1781,6 +1779,7 @@ impl<'a> Parser<'a> { unreachable!() } } + Ok(FnHeader { constness, unsafety, asyncness, ext }) } diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index e11cdd5dadb..e97af0dc00c 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -199,7 +199,7 @@ impl<'a> Parser<'a> { } } - pub(super) fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { + fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } diff --git a/src/test/ui/issues/issue-60075.rs b/src/test/ui/issues/issue-60075.rs index 7d3fc83786e..1323f646be8 100644 --- a/src/test/ui/issues/issue-60075.rs +++ b/src/test/ui/issues/issue-60075.rs @@ -6,6 +6,5 @@ trait T { }); //~^ ERROR expected one of `async` //~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `}` -//~| ERROR expected identifier, found `;` Some(4) } diff --git a/src/test/ui/issues/issue-60075.stderr b/src/test/ui/issues/issue-60075.stderr index e8ef981f515..60eb99b46b7 100644 --- a/src/test/ui/issues/issue-60075.stderr +++ b/src/test/ui/issues/issue-60075.stderr @@ -13,11 +13,5 @@ LL | let _ = if true { LL | }); | ^ help: `}` may belong here -error: expected identifier, found `;` - --> $DIR/issue-60075.rs:6:11 - | -LL | }); - | ^ expected identifier - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/fn-header-semantic-fail.rs b/src/test/ui/parser/fn-header-semantic-fail.rs index c2b7e69c80d..98ff2b6d2e8 100644 --- a/src/test/ui/parser/fn-header-semantic-fail.rs +++ b/src/test/ui/parser/fn-header-semantic-fail.rs @@ -18,9 +18,9 @@ fn main() { unsafe fn ft2(); // OK. const fn ft3(); //~ ERROR trait fns cannot be declared const extern "C" fn ft4(); // OK. - /* const */ async unsafe extern "C" fn ft5(); + const async unsafe extern "C" fn ft5(); //~^ ERROR trait fns cannot be declared `async` - //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + //~| ERROR trait fns cannot be declared const } struct Y; @@ -30,10 +30,10 @@ fn main() { unsafe fn ft2() {} // OK. const fn ft3() {} //~ ERROR trait fns cannot be declared const extern "C" fn ft4() {} - /* const */ async unsafe extern "C" fn ft5() {} + const async unsafe extern "C" fn ft5() {} //~^ ERROR trait fns cannot be declared `async` + //~| ERROR trait fns cannot be declared const //~| ERROR method `ft5` has an incompatible type for trait - //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. } impl Y { @@ -41,8 +41,7 @@ fn main() { unsafe fn fi2() {} // OK. const fn fi3() {} // OK. extern "C" fn fi4() {} // OK. - /* const */ async unsafe extern "C" fn fi5() {} // OK. - //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + const async unsafe extern "C" fn fi5() {} // OK. } extern { @@ -50,8 +49,6 @@ fn main() { unsafe fn fe2(); //~ ERROR functions in `extern` blocks cannot have qualifiers const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers - /* const */ async unsafe extern "C" fn fe5(); - //~^ ERROR functions in `extern` blocks cannot have qualifiers - //^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically. + const async unsafe extern "C" fn fe5(); //~ ERROR functions in `extern` blocks } } diff --git a/src/test/ui/parser/fn-header-semantic-fail.stderr b/src/test/ui/parser/fn-header-semantic-fail.stderr index 689bbdd8bab..1860106b255 100644 --- a/src/test/ui/parser/fn-header-semantic-fail.stderr +++ b/src/test/ui/parser/fn-header-semantic-fail.stderr @@ -15,13 +15,19 @@ error[E0379]: trait fns cannot be declared const LL | const fn ft3(); | ^^^^^ trait fns cannot be const -error[E0706]: trait fns cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:21:21 +error[E0379]: trait fns cannot be declared const + --> $DIR/fn-header-semantic-fail.rs:21:9 | -LL | /* const */ async unsafe extern "C" fn ft5(); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `async` because of this +LL | const async unsafe extern "C" fn ft5(); + | ^^^^^ trait fns cannot be const + +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:21:9 + | +LL | const async unsafe extern "C" fn ft5(); + | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this | = note: `async` trait functions are not currently supported = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait @@ -43,19 +49,25 @@ error[E0379]: trait fns cannot be declared const LL | const fn ft3() {} | ^^^^^ trait fns cannot be const -error[E0706]: trait fns cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:33:21 +error[E0379]: trait fns cannot be declared const + --> $DIR/fn-header-semantic-fail.rs:33:9 | -LL | /* const */ async unsafe extern "C" fn ft5() {} - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `async` because of this +LL | const async unsafe extern "C" fn ft5() {} + | ^^^^^ trait fns cannot be const + +error[E0706]: trait fns cannot be declared `async` + --> $DIR/fn-header-semantic-fail.rs:33:9 + | +LL | const async unsafe extern "C" fn ft5() {} + | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this | = note: `async` trait functions are not currently supported = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:49:18 + --> $DIR/fn-header-semantic-fail.rs:48:18 | LL | extern { | ------ in this `extern` block @@ -65,7 +77,7 @@ LL | async fn fe1(); | help: remove the qualifiers: `fn` error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:50:19 + --> $DIR/fn-header-semantic-fail.rs:49:19 | LL | extern { | ------ in this `extern` block @@ -76,7 +88,7 @@ LL | unsafe fn fe2(); | help: remove the qualifiers: `fn` error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:51:18 + --> $DIR/fn-header-semantic-fail.rs:50:18 | LL | extern { | ------ in this `extern` block @@ -87,7 +99,7 @@ LL | const fn fe3(); | help: remove the qualifiers: `fn` error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:52:23 + --> $DIR/fn-header-semantic-fail.rs:51:23 | LL | extern { | ------ in this `extern` block @@ -98,15 +110,15 @@ LL | extern "C" fn fe4(); | help: remove the qualifiers: `fn` error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:53:48 + --> $DIR/fn-header-semantic-fail.rs:52:42 | LL | extern { | ------ in this `extern` block ... -LL | /* const */ async unsafe extern "C" fn fe5(); - | ---------------------------^^^ - | | - | help: remove the qualifiers: `fn` +LL | const async unsafe extern "C" fn fe5(); + | ---------------------------------^^^ + | | + | help: remove the qualifiers: `fn` error[E0053]: method `ft1` has an incompatible type for trait --> $DIR/fn-header-semantic-fail.rs:28:24 @@ -124,21 +136,21 @@ LL | async fn ft1() {} found fn pointer `fn() -> impl std::future::Future` error[E0053]: method `ft5` has an incompatible type for trait - --> $DIR/fn-header-semantic-fail.rs:33:54 + --> $DIR/fn-header-semantic-fail.rs:33:48 | -LL | /* const */ async unsafe extern "C" fn ft5(); - | - type in trait +LL | const async unsafe extern "C" fn ft5(); + | - type in trait ... -LL | /* const */ async unsafe extern "C" fn ft5() {} - | ^ - | | - | the `Output` of this `async fn`'s found opaque type - | expected `()`, found opaque type +LL | const async unsafe extern "C" fn ft5() {} + | ^ + | | + | the `Output` of this `async fn`'s found opaque type + | expected `()`, found opaque type | = note: expected fn pointer `unsafe extern "C" fn()` found fn pointer `unsafe extern "C" fn() -> impl std::future::Future` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors Some errors have detailed explanations: E0053, E0379, E0706. For more information about an error, try `rustc --explain E0053`. diff --git a/src/test/ui/parser/fn-header-syntactic-pass.rs b/src/test/ui/parser/fn-header-syntactic-pass.rs index 145a208cb24..0557b9ef6ca 100644 --- a/src/test/ui/parser/fn-header-syntactic-pass.rs +++ b/src/test/ui/parser/fn-header-syntactic-pass.rs @@ -22,8 +22,7 @@ fn syntax() { unsafe fn f(); const fn f(); extern "C" fn f(); - /* const */ async unsafe extern "C" fn f(); - //^ FIXME(Centril): `const` should be legal syntactically. + const async unsafe extern "C" fn f(); } impl X for Y { @@ -31,8 +30,7 @@ fn syntax() { unsafe fn f(); const fn f(); extern "C" fn f(); - /* const */ async unsafe extern "C" fn f(); - //^ FIXME(Centril): `const` should be legal syntactically. + const async unsafe extern "C" fn f(); } impl Y { @@ -40,8 +38,7 @@ fn syntax() { unsafe fn f(); const fn f(); extern "C" fn f(); - /* const */ async unsafe extern "C" fn f(); - //^ FIXME(Centril): `const` should be legal syntactically. + const async unsafe extern "C" fn f(); } extern { @@ -49,7 +46,6 @@ fn syntax() { unsafe fn f(); const fn f(); extern "C" fn f(); - /* const */ async unsafe extern "C" fn f(); - //^ FIXME(Centril): `const` should be legal syntactically. + const async unsafe extern "C" fn f(); } }