about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2020-01-30 13:02:06 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2020-02-13 10:39:24 +0100
commita833be21626890de406e12f2561d2ffbda4aadb4 (patch)
treebd75ba7b37902e0ba9d09e775bdd413f9c868201
parent36a17e4067d2e67223cd9a172476ee5503d6b44b (diff)
downloadrust-a833be21626890de406e12f2561d2ffbda4aadb4.tar.gz
rust-a833be21626890de406e12f2561d2ffbda4aadb4.zip
parser: fuse free `fn` parsing together.
-rw-r--r--src/librustc_ast_passes/ast_validation.rs14
-rw-r--r--src/librustc_ast_passes/feature_gate.rs12
-rw-r--r--src/librustc_parse/parser/item.rs111
-rw-r--r--src/libsyntax/token.rs8
-rw-r--r--src/libsyntax/util/literal.rs2
-rw-r--r--src/test/ui/async-await/no-async-const.rs2
-rw-r--r--src/test/ui/async-await/no-async-const.stderr4
-rw-r--r--src/test/ui/async-await/no-const-async.rs3
-rw-r--r--src/test/ui/async-await/no-const-async.stderr17
-rw-r--r--src/test/ui/async-await/no-unsafe-async.rs2
-rw-r--r--src/test/ui/async-await/no-unsafe-async.stderr4
-rw-r--r--src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs14
-rw-r--r--src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.stderr36
-rw-r--r--src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs1
-rw-r--r--src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.stderr12
-rw-r--r--src/test/ui/label/label_break_value_illegal_uses.rs2
-rw-r--r--src/test/ui/label/label_break_value_illegal_uses.stderr4
-rw-r--r--src/test/ui/parser/extern-crate-unexpected-token.rs2
-rw-r--r--src/test/ui/parser/extern-crate-unexpected-token.stderr4
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.rs5
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.stderr6
-rw-r--r--src/test/ui/parser/fn-header-semantic-fail.rs10
-rw-r--r--src/test/ui/parser/fn-header-semantic-fail.stderr69
-rw-r--r--src/test/ui/parser/fn-header-syntactic-pass.rs6
-rw-r--r--src/test/ui/parser/issue-19398.rs2
-rw-r--r--src/test/ui/parser/issue-19398.stderr8
-rw-r--r--src/test/ui/unsafe/unsafe-block-without-braces.rs2
-rw-r--r--src/test/ui/unsafe/unsafe-block-without-braces.stderr4
28 files changed, 181 insertions, 185 deletions
diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs
index e0217de1d3b..0bc6179dabf 100644
--- a/src/librustc_ast_passes/ast_validation.rs
+++ b/src/librustc_ast_passes/ast_validation.rs
@@ -1089,6 +1089,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         self.check_c_varadic_type(fk);
 
+        // Functions cannot both be `const async`
+        if let Some(FnHeader {
+            constness: Const::Yes(cspan),
+            asyncness: Async::Yes { span: aspan, .. },
+            ..
+        }) = fk.header()
+        {
+            self.err_handler()
+                .struct_span_err(span, "functions cannot be both `const` and `async`")
+                .span_label(*cspan, "`const` because of this")
+                .span_label(*aspan, "`async` because of this")
+                .emit();
+        }
+
         // Functions without bodies cannot have patterns.
         if let FnKind::Fn(ctxt, _, sig, _, None) = fk {
             Self::check_decl_no_pat(&sig.decl, |span, mut_ident| {
diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs
index cfab54925b1..0b21de4d78b 100644
--- a/src/librustc_ast_passes/feature_gate.rs
+++ b/src/librustc_ast_passes/feature_gate.rs
@@ -496,6 +496,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         if let Some(header) = fn_kind.header() {
             // Stability of const fn methods are covered in `visit_assoc_item` below.
             self.check_extern(header.ext);
+
+            if let (ast::Const::Yes(_), ast::Extern::Implicit)
+            | (ast::Const::Yes(_), ast::Extern::Explicit(_)) = (header.constness, header.ext)
+            {
+                gate_feature_post!(
+                    &self,
+                    const_extern_fn,
+                    span,
+                    "`const extern fn` definitions are unstable"
+                );
+            }
         }
 
         if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
@@ -595,7 +606,6 @@ pub fn check_crate(
     gate_all!(async_closure, "async closures are unstable");
     gate_all!(generators, "yield syntax is experimental");
     gate_all!(or_patterns, "or-patterns syntax is experimental");
-    gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
     gate_all!(raw_ref_op, "raw address of syntax is experimental");
     gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
     gate_all!(const_trait_impl, "const trait impls are experimental");
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index d2bcf31cb05..906b82f746b 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -11,7 +11,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::BytePos;
 use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
 use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind};
-use syntax::ast::{Async, Const, Defaultness, Extern, IsAuto, PathSegment, StrLit, Unsafe};
+use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, StrLit, Unsafe};
 use syntax::ast::{BindingMode, Block, FnDecl, FnSig, Mac, MacArgs, MacDelimiter, Param, SelfKind};
 use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
 use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
@@ -96,53 +96,30 @@ impl<'a> Parser<'a> {
             return Ok(Some(item));
         }
 
+        if self.is_fn_front_matter() {
+            // FUNCTION ITEM
+            return self.parse_item_fn(lo, vis, attrs);
+        }
+
         if self.eat_keyword(kw::Extern) {
             if self.eat_keyword(kw::Crate) {
+                // EXTERN CRATE
                 return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
             }
-
+            // EXTERN BLOCK
             let abi = self.parse_abi();
-
-            if self.eat_keyword(kw::Fn) {
-                // EXTERN FUNCTION ITEM
-                let header = FnHeader {
-                    unsafety: Unsafe::No,
-                    asyncness: Async::No,
-                    constness: Const::No,
-                    ext: Extern::from_abi(abi),
-                };
-                return self.parse_item_fn(lo, vis, attrs, header);
-            } else if self.check(&token::OpenDelim(token::Brace)) {
-                return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
-            }
-
-            self.unexpected()?;
+            return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
         }
 
         if self.is_static_global() {
-            self.bump();
             // STATIC ITEM
+            self.bump();
             let m = self.parse_mutability();
             let info = self.parse_item_const(Some(m))?;
             return self.mk_item_with_info(attrs, lo, vis, info);
         }
 
-        let constness = self.parse_constness();
-        if let Const::Yes(const_span) = constness {
-            if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
-                // CONST FUNCTION ITEM
-                let unsafety = self.parse_unsafety();
-
-                if self.check_keyword(kw::Extern) {
-                    self.sess.gated_spans.gate(sym::const_extern_fn, lo.to(self.token.span));
-                }
-                let ext = self.parse_extern()?;
-                self.expect_keyword(kw::Fn)?;
-
-                let header = FnHeader { unsafety, asyncness: Async::No, constness, ext };
-                return self.parse_item_fn(lo, vis, attrs, header);
-            }
-
+        if let Const::Yes(const_span) = self.parse_constness() {
             // CONST ITEM
             if self.eat_keyword(kw::Mut) {
                 let prev_span = self.prev_span;
@@ -161,21 +138,6 @@ impl<'a> Parser<'a> {
             return self.mk_item_with_info(attrs, lo, vis, info);
         }
 
-        // Parses `async unsafe? fn`.
-        if self.check_keyword(kw::Async) {
-            let async_span = self.token.span;
-            if self.is_keyword_ahead(1, &[kw::Fn]) || self.is_keyword_ahead(2, &[kw::Fn]) {
-                // ASYNC FUNCTION ITEM
-                let asyncness = self.parse_asyncness(); // `async`
-                let unsafety = self.parse_unsafety(); // `unsafe`?
-                self.expect_keyword(kw::Fn)?; // `fn`
-                self.ban_async_in_2015(async_span);
-                let header =
-                    FnHeader { unsafety, asyncness, constness: Const::No, ext: Extern::None };
-                return self.parse_item_fn(lo, vis, attrs, header);
-            }
-        }
-
         if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) {
             // UNSAFE TRAIT ITEM
             let unsafety = self.parse_unsafety();
@@ -195,26 +157,6 @@ impl<'a> Parser<'a> {
             return self.mk_item_with_info(attrs, lo, vis, info);
         }
 
-        if self.check_keyword(kw::Fn) {
-            // FUNCTION ITEM
-            self.bump();
-            let header = FnHeader::default();
-            return self.parse_item_fn(lo, vis, attrs, header);
-        }
-
-        if self.check_keyword(kw::Unsafe)
-            && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace))
-        {
-            // UNSAFE FUNCTION ITEM
-            let unsafety = self.parse_unsafety();
-            // `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
-            self.check(&token::OpenDelim(token::Brace));
-            let ext = self.parse_extern()?;
-            self.expect_keyword(kw::Fn)?;
-            let header = FnHeader { unsafety, asyncness: Async::No, constness: Const::No, ext };
-            return self.parse_item_fn(lo, vis, attrs, header);
-        }
-
         if self.eat_keyword(kw::Mod) {
             // MODULE ITEM
             let info = self.parse_item_mod(&attrs[..])?;
@@ -1662,9 +1604,9 @@ impl<'a> Parser<'a> {
         lo: Span,
         vis: Visibility,
         mut attrs: Vec<Attribute>,
-        header: FnHeader,
     ) -> PResult<'a, Option<P<Item>>> {
         let cfg = ParamCfg { is_name_required: |_| true };
+        let header = self.parse_fn_front_matter()?;
         let (ident, decl, generics) = self.parse_fn_sig(&cfg)?;
         let body = self.parse_fn_body(&mut false, &mut attrs)?;
         let kind = ItemKind::Fn(FnSig { decl, header }, generics, body);
@@ -1730,27 +1672,24 @@ impl<'a> Parser<'a> {
         Ok(body)
     }
 
-    /// Is the current token unambiguously the start of an `FnHeader`?
+    /// Is the current token the start of an `FnHeader` / not a valid parse?
     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))
-            })
-        };
+        const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
         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)
+            || QUALS.iter().any(|&kw| self.check_keyword(kw))
+                && self.look_ahead(1, |t| {
+                    // ...qualified and then `fn`, e.g. `const fn`.
+                    t.is_keyword(kw::Fn)
+                    // Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
+                    || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
+                })
+            // `extern ABI fn`
+            || self.check_keyword(kw::Extern)
+                && self.look_ahead(1, |t| t.can_begin_literal_or_bool())
+                && self.look_ahead(2, |t| t.is_keyword(kw::Fn))
     }
 
     /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
diff --git a/src/libsyntax/token.rs b/src/libsyntax/token.rs
index 3045f147698..862934300e0 100644
--- a/src/libsyntax/token.rs
+++ b/src/libsyntax/token.rs
@@ -402,12 +402,14 @@ impl Token {
 
     /// Returns `true` if the token is any literal, a minus (which can prefix a literal,
     /// for example a '-42', or one of the boolean idents).
+    ///
+    /// Keep this in sync with `Lit::from_token`.
     pub fn can_begin_literal_or_bool(&self) -> bool {
         match self.kind {
             Literal(..) | BinOp(Minus) => true,
             Ident(name, false) if name.is_bool_lit() => true,
-            Interpolated(ref nt) => match **nt {
-                NtLiteral(..) => true,
+            Interpolated(ref nt) => match &**nt {
+                NtExpr(e) | NtLiteral(e) => matches!(e.kind, ast::ExprKind::Lit(_)),
                 _ => false,
             },
             _ => false,
@@ -530,7 +532,7 @@ impl Token {
     }
 
     /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
-    fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
+    pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
         match self.ident() {
             Some((id, false)) => pred(id),
             _ => false,
diff --git a/src/libsyntax/util/literal.rs b/src/libsyntax/util/literal.rs
index dd06c25b4de..0c611adc06b 100644
--- a/src/libsyntax/util/literal.rs
+++ b/src/libsyntax/util/literal.rs
@@ -188,6 +188,8 @@ impl Lit {
     }
 
     /// Converts arbitrary token into an AST literal.
+    ///
+    /// Keep this in sync with `Token::can_begin_literal_or_bool`.
     pub fn from_token(token: &Token) -> Result<Lit, LitError> {
         let lit = match token.kind {
             token::Ident(name, false) if name.is_bool_lit() => {
diff --git a/src/test/ui/async-await/no-async-const.rs b/src/test/ui/async-await/no-async-const.rs
index 64322990d0a..963460c1182 100644
--- a/src/test/ui/async-await/no-async-const.rs
+++ b/src/test/ui/async-await/no-async-const.rs
@@ -2,4 +2,4 @@
 // compile-flags: --crate-type lib
 
 pub async const fn x() {}
-//~^ ERROR expected one of `fn` or `unsafe`, found keyword `const`
+//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr
index d5b8b344abe..e324a77187a 100644
--- a/src/test/ui/async-await/no-async-const.stderr
+++ b/src/test/ui/async-await/no-async-const.stderr
@@ -1,8 +1,8 @@
-error: expected one of `fn` or `unsafe`, found keyword `const`
+error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
   --> $DIR/no-async-const.rs:4:11
    |
 LL | pub async const fn x() {}
-   |           ^^^^^ expected one of `fn` or `unsafe`
+   |           ^^^^^ expected one of `extern`, `fn`, or `unsafe`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/async-await/no-const-async.rs b/src/test/ui/async-await/no-const-async.rs
index 55b27bd3fa1..b3c59734e03 100644
--- a/src/test/ui/async-await/no-const-async.rs
+++ b/src/test/ui/async-await/no-const-async.rs
@@ -2,5 +2,4 @@
 // compile-flags: --crate-type lib
 
 pub const async fn x() {}
-//~^ ERROR expected identifier, found keyword `async`
-//~^^ expected `:`, found keyword `fn`
+//~^ ERROR functions cannot be both `const` and `async`
diff --git a/src/test/ui/async-await/no-const-async.stderr b/src/test/ui/async-await/no-const-async.stderr
index 62cd5c45d19..f6ae0f1447c 100644
--- a/src/test/ui/async-await/no-const-async.stderr
+++ b/src/test/ui/async-await/no-const-async.stderr
@@ -1,14 +1,11 @@
-error: expected identifier, found keyword `async`
-  --> $DIR/no-const-async.rs:4:11
+error: functions cannot be both `const` and `async`
+  --> $DIR/no-const-async.rs:4:1
    |
 LL | pub const async fn x() {}
-   |           ^^^^^ expected identifier, found keyword
+   | ^^^^-----^-----^^^^^^^^^^
+   |     |     |
+   |     |     `async` because of this
+   |     `const` because of this
 
-error: expected `:`, found keyword `fn`
-  --> $DIR/no-const-async.rs:4:17
-   |
-LL | pub const async fn x() {}
-   |                 ^^ expected `:`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
diff --git a/src/test/ui/async-await/no-unsafe-async.rs b/src/test/ui/async-await/no-unsafe-async.rs
index 1ac1bdffda9..f40154e16f3 100644
--- a/src/test/ui/async-await/no-unsafe-async.rs
+++ b/src/test/ui/async-await/no-unsafe-async.rs
@@ -8,4 +8,4 @@ impl S {
 }
 
 #[cfg(FALSE)]
-unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found keyword `async`
+unsafe async fn f() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async`
diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr
index bbeb3427849..2651588d597 100644
--- a/src/test/ui/async-await/no-unsafe-async.stderr
+++ b/src/test/ui/async-await/no-unsafe-async.stderr
@@ -4,11 +4,11 @@ error: expected one of `extern` or `fn`, found keyword `async`
 LL |     unsafe async fn g() {}
    |            ^^^^^ expected one of `extern` or `fn`
 
-error: expected one of `extern`, `fn`, or `{`, found keyword `async`
+error: expected one of `extern` or `fn`, found keyword `async`
   --> $DIR/no-unsafe-async.rs:11:8
    |
 LL | unsafe async fn f() {}
-   |        ^^^^^ expected one of `extern`, `fn`, or `{`
+   |        ^^^^^ expected one of `extern` or `fn`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs b/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs
index d39f2c1fe27..5667d553527 100644
--- a/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs
+++ b/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs
@@ -1,12 +1,10 @@
 // Check that `const extern fn` and `const unsafe extern fn` are feature-gated.
 
-#[cfg(FALSE)] const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
-#[cfg(FALSE)] const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
-#[cfg(FALSE)] const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
-#[cfg(FALSE)] const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
-#[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
-//~^ ERROR `const extern fn` definitions are unstable
-#[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
-//~^ ERROR `const extern fn` definitions are unstable
+const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
+const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
+const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
+const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
+const unsafe extern "C" fn bar2() {} //~ ERROR `const extern fn` definitions are unstable
+const unsafe extern "Rust" fn bar3() {} //~ ERROR `const extern fn` definitions are unstable
 
 fn main() {}
diff --git a/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.stderr b/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.stderr
index ed5e0c84a16..bd5940a3fd6 100644
--- a/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.stderr
+++ b/src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.stderr
@@ -1,53 +1,53 @@
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:3:15
+  --> $DIR/feature-gate-const_extern_fn.rs:3:1
    |
-LL | #[cfg(FALSE)] const extern fn foo1() {}
-   |               ^^^^^^^^^^^^
+LL | const extern fn foo1() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
 
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:4:15
+  --> $DIR/feature-gate-const_extern_fn.rs:4:1
    |
-LL | #[cfg(FALSE)] const extern "C" fn foo2() {}
-   |               ^^^^^^^^^^^^
+LL | const extern "C" fn foo2() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
 
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:5:15
+  --> $DIR/feature-gate-const_extern_fn.rs:5:1
    |
-LL | #[cfg(FALSE)] const extern "Rust" fn foo3() {}
-   |               ^^^^^^^^^^^^
+LL | const extern "Rust" fn foo3() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
 
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:6:15
+  --> $DIR/feature-gate-const_extern_fn.rs:6:1
    |
-LL | #[cfg(FALSE)] const unsafe extern fn bar1() {}
-   |               ^^^^^^^^^^^^^^^^^^^
+LL | const unsafe extern fn bar1() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
 
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:7:15
+  --> $DIR/feature-gate-const_extern_fn.rs:7:1
    |
-LL | #[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
-   |               ^^^^^^^^^^^^^^^^^^^
+LL | const unsafe extern "C" fn bar2() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
 
 error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/feature-gate-const_extern_fn.rs:9:15
+  --> $DIR/feature-gate-const_extern_fn.rs:8:1
    |
-LL | #[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
-   |               ^^^^^^^^^^^^^^^^^^^
+LL | const unsafe extern "Rust" fn bar3() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
    = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
diff --git a/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs b/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs
index 1886bfccb4e..6f575d055a2 100644
--- a/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs
+++ b/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs
@@ -4,5 +4,4 @@ fn main() {}
 fn container() {
     const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
     //~^ ERROR expected `fn`
-    //~| ERROR `const extern fn` definitions are unstable
 }
diff --git a/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.stderr b/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.stderr
index d36b781d292..ec415ec9d02 100644
--- a/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.stderr
+++ b/src/test/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.stderr
@@ -4,15 +4,5 @@ error: expected `fn`, found `PUT_ANYTHING_YOU_WANT_HERE`
 LL |     const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `fn`
 
-error[E0658]: `const extern fn` definitions are unstable
-  --> $DIR/issue-68062-const-extern-fns-dont-need-fn-specifier.rs:5:5
-   |
-LL |     const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
-   |     ^^^^^^^^^^^^
-   |
-   = note: see issue #64926 <https://github.com/rust-lang/rust/issues/64926> for more information
-   = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/label/label_break_value_illegal_uses.rs b/src/test/ui/label/label_break_value_illegal_uses.rs
index 81cb1774380..9d4c72410a6 100644
--- a/src/test/ui/label/label_break_value_illegal_uses.rs
+++ b/src/test/ui/label/label_break_value_illegal_uses.rs
@@ -3,7 +3,7 @@
 // These are forbidden occurrences of label-break-value
 
 fn labeled_unsafe() {
-    unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{`
+    unsafe 'b: {} //~ ERROR expected `{`, found `'b`
 }
 
 fn labeled_if() {
diff --git a/src/test/ui/label/label_break_value_illegal_uses.stderr b/src/test/ui/label/label_break_value_illegal_uses.stderr
index 46b53c65b48..fd8850dd8da 100644
--- a/src/test/ui/label/label_break_value_illegal_uses.stderr
+++ b/src/test/ui/label/label_break_value_illegal_uses.stderr
@@ -1,8 +1,8 @@
-error: expected one of `extern`, `fn`, or `{`, found `'b`
+error: expected `{`, found `'b`
   --> $DIR/label_break_value_illegal_uses.rs:6:12
    |
 LL |     unsafe 'b: {}
-   |            ^^ expected one of `extern`, `fn`, or `{`
+   |            ^^ expected `{`
 
 error: expected `{`, found `'b`
   --> $DIR/label_break_value_illegal_uses.rs:10:13
diff --git a/src/test/ui/parser/extern-crate-unexpected-token.rs b/src/test/ui/parser/extern-crate-unexpected-token.rs
index 58b2fa1afdf..7687f5e6409 100644
--- a/src/test/ui/parser/extern-crate-unexpected-token.rs
+++ b/src/test/ui/parser/extern-crate-unexpected-token.rs
@@ -1 +1 @@
-extern crte foo; //~ ERROR expected one of `crate`, `fn`, or `{`, found `crte`
+extern crte foo; //~ ERROR expected one of `crate` or `{`, found `crte`
diff --git a/src/test/ui/parser/extern-crate-unexpected-token.stderr b/src/test/ui/parser/extern-crate-unexpected-token.stderr
index 0e745dc582f..e9d287ac0e9 100644
--- a/src/test/ui/parser/extern-crate-unexpected-token.stderr
+++ b/src/test/ui/parser/extern-crate-unexpected-token.stderr
@@ -1,8 +1,8 @@
-error: expected one of `crate`, `fn`, or `{`, found `crte`
+error: expected one of `crate` or `{`, found `crte`
   --> $DIR/extern-crate-unexpected-token.rs:1:8
    |
 LL | extern crte foo;
-   |        ^^^^ expected one of `crate`, `fn`, or `{`
+   |        ^^^^ expected one of `crate` or `{`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.rs b/src/test/ui/parser/extern-expected-fn-or-brace.rs
index 907de0d8f91..1dcea17445e 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.rs
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.rs
@@ -1,4 +1,3 @@
-// Verifies that the expected token errors for `extern crate` are
-// raised
+// Verifies that the expected token errors for `extern crate` are raised.
 
-extern "C" mod foo; //~ERROR expected one of `fn` or `{`, found keyword `mod`
+extern "C" mod foo; //~ERROR expected `{`, found keyword `mod`
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.stderr b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
index 0ebe9a0d3ea..258a2c2680a 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.stderr
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
@@ -1,8 +1,8 @@
-error: expected one of `fn` or `{`, found keyword `mod`
-  --> $DIR/extern-expected-fn-or-brace.rs:4:12
+error: expected `{`, found keyword `mod`
+  --> $DIR/extern-expected-fn-or-brace.rs:3:12
    |
 LL | extern "C" mod foo;
-   |            ^^^ expected one of `fn` or `{`
+   |            ^^^ expected `{`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/fn-header-semantic-fail.rs b/src/test/ui/parser/fn-header-semantic-fail.rs
index 98ff2b6d2e8..a05ea277eec 100644
--- a/src/test/ui/parser/fn-header-semantic-fail.rs
+++ b/src/test/ui/parser/fn-header-semantic-fail.rs
@@ -10,8 +10,8 @@ fn main() {
     unsafe fn ff2() {} // OK.
     const fn ff3() {} // OK.
     extern "C" fn ff4() {} // OK.
-    const /* async */ unsafe extern "C" fn ff5() {} // OK.
-    //^ FIXME(Centril): `async` should be legal syntactically, ensure it's illegal semantically.
+    const async unsafe extern "C" fn ff5() {} // OK.
+    //~^ ERROR functions cannot be both `const` and `async`
 
     trait X {
         async fn ft1(); //~ ERROR trait fns cannot be declared `async`
@@ -21,6 +21,7 @@ fn main() {
         const async unsafe extern "C" fn ft5();
         //~^ ERROR trait fns cannot be declared `async`
         //~| ERROR trait fns cannot be declared const
+        //~| ERROR functions cannot be both `const` and `async`
     }
 
     struct Y;
@@ -34,6 +35,7 @@ fn main() {
         //~^ ERROR trait fns cannot be declared `async`
         //~| ERROR trait fns cannot be declared const
         //~| ERROR method `ft5` has an incompatible type for trait
+        //~| ERROR functions cannot be both `const` and `async`
     }
 
     impl Y {
@@ -41,7 +43,8 @@ fn main() {
         unsafe fn fi2() {} // OK.
         const fn fi3() {} // OK.
         extern "C" fn fi4() {} // OK.
-        const async unsafe extern "C" fn fi5() {} // OK.
+        const async unsafe extern "C" fn fi5() {}
+        //~^ ERROR functions cannot be both `const` and `async`
     }
 
     extern {
@@ -50,5 +53,6 @@ fn main() {
         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
+        //~^ ERROR functions cannot be both `const` and `async`
     }
 }
diff --git a/src/test/ui/parser/fn-header-semantic-fail.stderr b/src/test/ui/parser/fn-header-semantic-fail.stderr
index 1860106b255..a9e4a183347 100644
--- a/src/test/ui/parser/fn-header-semantic-fail.stderr
+++ b/src/test/ui/parser/fn-header-semantic-fail.stderr
@@ -1,3 +1,12 @@
+error: functions cannot be both `const` and `async`
+  --> $DIR/fn-header-semantic-fail.rs:13:5
+   |
+LL |     const async unsafe extern "C" fn ff5() {} // OK.
+   |     -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     |     |
+   |     |     `async` because of this
+   |     `const` because of this
+
 error[E0706]: trait fns cannot be declared `async`
   --> $DIR/fn-header-semantic-fail.rs:17:9
    |
@@ -32,8 +41,17 @@ LL |         const async unsafe extern "C" fn ft5();
    = note: `async` trait functions are not currently supported
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
+error: functions cannot be both `const` and `async`
+  --> $DIR/fn-header-semantic-fail.rs:21:9
+   |
+LL |         const async unsafe extern "C" fn ft5();
+   |         -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |     |
+   |         |     `async` because of this
+   |         `const` because of this
+
 error[E0706]: trait fns cannot be declared `async`
-  --> $DIR/fn-header-semantic-fail.rs:28:9
+  --> $DIR/fn-header-semantic-fail.rs:29:9
    |
 LL |         async fn ft1() {}
    |         -----^^^^^^^^^^^^
@@ -44,19 +62,19 @@ LL |         async fn ft1() {}
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
 error[E0379]: trait fns cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:31:9
+  --> $DIR/fn-header-semantic-fail.rs:32:9
    |
 LL |         const fn ft3() {}
    |         ^^^^^ trait fns cannot be const
 
 error[E0379]: trait fns cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:33:9
+  --> $DIR/fn-header-semantic-fail.rs:34:9
    |
 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
+  --> $DIR/fn-header-semantic-fail.rs:34:9
    |
 LL |         const async unsafe extern "C" fn ft5() {}
    |         ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,8 +84,26 @@ LL |         const async unsafe extern "C" fn ft5() {}
    = note: `async` trait functions are not currently supported
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
+error: functions cannot be both `const` and `async`
+  --> $DIR/fn-header-semantic-fail.rs:34:9
+   |
+LL |         const async unsafe extern "C" fn ft5() {}
+   |         -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |     |
+   |         |     `async` because of this
+   |         `const` because of this
+
+error: functions cannot be both `const` and `async`
+  --> $DIR/fn-header-semantic-fail.rs:46:9
+   |
+LL |         const async unsafe extern "C" fn fi5() {}
+   |         -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |     |
+   |         |     `async` because of this
+   |         `const` because of this
+
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:48:18
+  --> $DIR/fn-header-semantic-fail.rs:51:18
    |
 LL |     extern {
    |     ------ in this `extern` block
@@ -77,7 +113,7 @@ LL |         async fn fe1();
    |         help: remove the qualifiers: `fn`
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:49:19
+  --> $DIR/fn-header-semantic-fail.rs:52:19
    |
 LL |     extern {
    |     ------ in this `extern` block
@@ -88,7 +124,7 @@ LL |         unsafe fn fe2();
    |         help: remove the qualifiers: `fn`
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:50:18
+  --> $DIR/fn-header-semantic-fail.rs:53:18
    |
 LL |     extern {
    |     ------ in this `extern` block
@@ -99,7 +135,7 @@ LL |         const fn fe3();
    |         help: remove the qualifiers: `fn`
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:51:23
+  --> $DIR/fn-header-semantic-fail.rs:54:23
    |
 LL |     extern {
    |     ------ in this `extern` block
@@ -110,7 +146,7 @@ LL |         extern "C" fn fe4();
    |         help: remove the qualifiers: `fn`
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:52:42
+  --> $DIR/fn-header-semantic-fail.rs:55:42
    |
 LL |     extern {
    |     ------ in this `extern` block
@@ -120,8 +156,17 @@ LL |         const async unsafe extern "C" fn fe5();
    |         |
    |         help: remove the qualifiers: `fn`
 
+error: functions cannot be both `const` and `async`
+  --> $DIR/fn-header-semantic-fail.rs:55:9
+   |
+LL |         const async unsafe extern "C" fn fe5();
+   |         -----^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |     |
+   |         |     `async` because of this
+   |         `const` because of this
+
 error[E0053]: method `ft1` has an incompatible type for trait
-  --> $DIR/fn-header-semantic-fail.rs:28:24
+  --> $DIR/fn-header-semantic-fail.rs:29:24
    |
 LL |         async fn ft1();
    |                       - type in trait
@@ -136,7 +181,7 @@ 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:48
+  --> $DIR/fn-header-semantic-fail.rs:34:48
    |
 LL |         const async unsafe extern "C" fn ft5();
    |                                               - type in trait
@@ -150,7 +195,7 @@ LL |         const async unsafe extern "C" fn ft5() {}
    = note: expected fn pointer `unsafe extern "C" fn()`
               found fn pointer `unsafe extern "C" fn() -> impl std::future::Future`
 
-error: aborting due to 15 previous errors
+error: aborting due to 20 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 0557b9ef6ca..9e44541993d 100644
--- a/src/test/ui/parser/fn-header-syntactic-pass.rs
+++ b/src/test/ui/parser/fn-header-syntactic-pass.rs
@@ -3,9 +3,6 @@
 // check-pass
 // edition:2018
 
-#![feature(const_extern_fn)]
-//^ FIXME(Centril): move check to ast_validation.
-
 fn main() {}
 
 #[cfg(FALSE)]
@@ -14,8 +11,7 @@ fn syntax() {
     unsafe fn f();
     const fn f();
     extern "C" fn f();
-    const /* async */ unsafe extern "C" fn f();
-    //^ FIXME(Centril): `async` should be legal syntactically.
+    const async unsafe extern "C" fn f();
 
     trait X {
         async fn f();
diff --git a/src/test/ui/parser/issue-19398.rs b/src/test/ui/parser/issue-19398.rs
index 90221039b41..2158a2fd6c1 100644
--- a/src/test/ui/parser/issue-19398.rs
+++ b/src/test/ui/parser/issue-19398.rs
@@ -1,5 +1,5 @@
 trait T {
-    extern "Rust" unsafe fn foo(); //~ ERROR expected `fn`, found keyword `unsafe`
+    extern "Rust" unsafe fn foo(); //~ ERROR expected one of `async`, `const`
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-19398.stderr b/src/test/ui/parser/issue-19398.stderr
index 41ec4f3ced4..201a6b2d66a 100644
--- a/src/test/ui/parser/issue-19398.stderr
+++ b/src/test/ui/parser/issue-19398.stderr
@@ -1,8 +1,10 @@
-error: expected `fn`, found keyword `unsafe`
-  --> $DIR/issue-19398.rs:2:19
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `extern`
+  --> $DIR/issue-19398.rs:2:5
    |
+LL | trait T {
+   |          - expected one of 10 possible tokens
 LL |     extern "Rust" unsafe fn foo();
-   |                   ^^^^^^ expected `fn`
+   |     ^^^^^^ unexpected token
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/unsafe/unsafe-block-without-braces.rs b/src/test/ui/unsafe/unsafe-block-without-braces.rs
index a291eb2eed7..4e461161854 100644
--- a/src/test/ui/unsafe/unsafe-block-without-braces.rs
+++ b/src/test/ui/unsafe/unsafe-block-without-braces.rs
@@ -3,4 +3,4 @@ fn main() {
         std::mem::transmute::<f32, u32>(1.0);
     //}
 }
-//~^^^ ERROR expected one of `extern`, `fn`, or `{`, found `std`
+//~^^^ ERROR expected `{`, found `std`
diff --git a/src/test/ui/unsafe/unsafe-block-without-braces.stderr b/src/test/ui/unsafe/unsafe-block-without-braces.stderr
index 637fdeead36..13e0c3681fa 100644
--- a/src/test/ui/unsafe/unsafe-block-without-braces.stderr
+++ b/src/test/ui/unsafe/unsafe-block-without-braces.stderr
@@ -1,8 +1,8 @@
-error: expected one of `extern`, `fn`, or `{`, found `std`
+error: expected `{`, found `std`
   --> $DIR/unsafe-block-without-braces.rs:3:9
    |
 LL |     unsafe //{
-   |           - expected one of `extern`, `fn`, or `{`
+   |           - expected `{`
 LL |         std::mem::transmute::<f32, u32>(1.0);
    |         ^^^ unexpected token