about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2020-01-30 08:31:31 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2020-02-13 10:39:24 +0100
commit36a17e4067d2e67223cd9a172476ee5503d6b44b (patch)
tree27715b8214a734d40f705a0e9f48a0d50d8b6a62
parentc30f068dc8b2ef58678b9846ba834dd6dea3fe44 (diff)
downloadrust-36a17e4067d2e67223cd9a172476ee5503d6b44b.tar.gz
rust-36a17e4067d2e67223cd9a172476ee5503d6b44b.zip
parser_fn_front_matter: allow `const .. extern`
-rw-r--r--src/librustc_parse/parser/item.rs105
-rw-r--r--src/librustc_parse/parser/stmt.rs2
-rw-r--r--src/test/ui/issues/issue-60075.rs1
-rw-r--r--src/test/ui/issues/issue-60075.stderr8
-rw-r--r--src/test/ui/parser/fn-header-semantic-fail.rs15
-rw-r--r--src/test/ui/parser/fn-header-semantic-fail.stderr68
-rw-r--r--src/test/ui/parser/fn-header-syntactic-pass.rs12
7 files changed, 104 insertions, 107 deletions
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[E0379]: trait fns cannot be declared const
+  --> $DIR/fn-header-semantic-fail.rs:21: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:21:21
+  --> $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();
+   |         ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |               |
+   |               `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[E0379]: trait fns cannot be declared const
+  --> $DIR/fn-header-semantic-fail.rs:33: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:21
+  --> $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() {}
+   |         ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |               |
+   |               `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();
     }
 }