about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/src/parser/item.rs9
-rw-r--r--tests/ui/parser/issues/issue-19398.rs6
-rw-r--r--tests/ui/parser/issues/issue-19398.stderr14
-rw-r--r--tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs16
-rw-r--r--tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr13
5 files changed, 47 insertions, 11 deletions
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9aaf4b99243..112855e6d1f 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> {
     /// `check_pub` adds additional `pub` to the checks in case users place it
     /// wrongly, can be used to ensure `pub` never comes after `default`.
     pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
+        const ALL_QUALS: &[Symbol] =
+            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern];
+
         // We use an over-approximation here.
         // `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
         // `pub` is added in case users got confused with the ordering like `async pub fn`,
         // only if it wasn't preceded by `default` as `default pub` is invalid.
         let quals: &[Symbol] = if check_pub {
-            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
+            ALL_QUALS
         } else {
             &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
         };
@@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> {
             || self.check_keyword_case(kw::Extern, case)
                 && self.look_ahead(1, |t| t.can_begin_string_literal())
                 && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
-                    // this branch is only for better diagnostic in later, `pub` is not allowed here
+                    // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here
                     (self.may_recover()
-                        && self.look_ahead(2, |t| t.is_keyword(kw::Pub))
+                        && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw)))
                         && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
     }
 
diff --git a/tests/ui/parser/issues/issue-19398.rs b/tests/ui/parser/issues/issue-19398.rs
index 46eb320a172..358f65f1da5 100644
--- a/tests/ui/parser/issues/issue-19398.rs
+++ b/tests/ui/parser/issues/issue-19398.rs
@@ -1,6 +1,10 @@
 trait T {
     extern "Rust" unsafe fn foo();
-    //~^ ERROR expected `{`, found keyword `unsafe`
+    //~^ ERROR expected `fn`, found keyword `unsafe`
+    //~| NOTE expected `fn`
+    //~| HELP `unsafe` must come before `extern "Rust"`
+    //~| SUGGESTION unsafe extern "Rust"
+    //~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
 }
 
 fn main() {}
diff --git a/tests/ui/parser/issues/issue-19398.stderr b/tests/ui/parser/issues/issue-19398.stderr
index 236fac673b6..2b97ec50c91 100644
--- a/tests/ui/parser/issues/issue-19398.stderr
+++ b/tests/ui/parser/issues/issue-19398.stderr
@@ -1,13 +1,13 @@
-error: expected `{`, found keyword `unsafe`
+error: expected `fn`, found keyword `unsafe`
   --> $DIR/issue-19398.rs:2:19
    |
-LL | trait T {
-   |         - while parsing this item list starting here
 LL |     extern "Rust" unsafe fn foo();
-   |                   ^^^^^^ expected `{`
-LL |
-LL | }
-   | - the item list ends here
+   |     --------------^^^^^^
+   |     |             |
+   |     |             expected `fn`
+   |     help: `unsafe` must come before `extern "Rust"`: `unsafe extern "Rust"`
+   |
+   = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs
new file mode 100644
index 00000000000..794ac635c76
--- /dev/null
+++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs
@@ -0,0 +1,16 @@
+//@ edition:2018
+
+// There is an order to respect for keywords before a function:
+// `<visibility>, const, async, unsafe, extern, "<ABI>"`
+//
+// This test ensures the compiler is helpful about them being misplaced.
+// Visibilities are tested elsewhere.
+
+extern "C" unsafe fn test() {}
+//~^ ERROR expected `fn`, found keyword `unsafe`
+//~| NOTE expected `fn`
+//~| HELP `unsafe` must come before `extern "C"`
+//~| SUGGESTION unsafe extern "C"
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
+
+fn main() {}
diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr
new file mode 100644
index 00000000000..8ed037869c8
--- /dev/null
+++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr
@@ -0,0 +1,13 @@
+error: expected `fn`, found keyword `unsafe`
+  --> $DIR/wrong-unsafe-abi.rs:9:12
+   |
+LL | extern "C" unsafe fn test() {}
+   | -----------^^^^^^
+   | |          |
+   | |          expected `fn`
+   | help: `unsafe` must come before `extern "C"`: `unsafe extern "C"`
+   |
+   = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
+
+error: aborting due to 1 previous error
+