Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
};
Expand Down Expand Up @@ -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))))
}

Expand Down
6 changes: 5 additions & 1 deletion tests/ui/parser/issues/issue-19398.rs
Original file line number Diff line number Diff line change
@@ -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() {}
14 changes: 7 additions & 7 deletions tests/ui/parser/issues/issue-19398.stderr
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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