diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2ce63d011f438..673caf35ea667 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1771,8 +1771,14 @@ impl<'a> Parser<'a> { pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let sp_start = self.token.span; let constness = self.parse_constness(); + + let async_start_sp = self.token.span; let asyncness = self.parse_asyncness(); + + let unsafe_start_sp = self.token.span; let unsafety = self.parse_unsafety(); + + let ext_start_sp = self.token.span; let ext = self.parse_extern(); if let Async::Yes { span, .. } = asyncness { @@ -1787,8 +1793,35 @@ impl<'a> Parser<'a> { Ok(true) => {} Ok(false) => unreachable!(), Err(mut err) => { + // Qualifier keywords ordering check + + // This will allow the machine fix to directly place the keyword in the correct place + let current_qual_sp = if self.check_keyword(kw::Const) { + Some(async_start_sp) + } else if self.check_keyword(kw::Async) { + Some(unsafe_start_sp) + } else if self.check_keyword(kw::Unsafe) { + Some(ext_start_sp) + } else { + None + }; + + if let Some(current_qual_sp) = current_qual_sp { + let current_qual_sp = current_qual_sp.to(self.prev_token.span); + if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { + let invalid_qual_sp = self.token.uninterpolated_span(); + let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); + + err.span_suggestion( + current_qual_sp.to(invalid_qual_sp), + &format!("`{}` must come before `{}`", invalid_qual, current_qual), + format!("{} {}", invalid_qual, current_qual), + Applicability::MachineApplicable, + ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + } + } // Recover incorrect visibility order such as `async pub`. - if self.check_keyword(kw::Pub) { + else if self.check_keyword(kw::Pub) { let sp = sp_start.to(self.prev_token.span); if let Ok(snippet) = self.span_to_snippet(sp) { let vis = match self.parse_visibility(FollowedByType::No) { diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr index e324a77187ab6..ae13b90c3cfc0 100644 --- a/src/test/ui/async-await/no-async-const.stderr +++ b/src/test/ui/async-await/no-async-const.stderr @@ -2,7 +2,12 @@ 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 `extern`, `fn`, or `unsafe` + | ------^^^^^ + | | | + | | expected one of `extern`, `fn`, or `unsafe` + | help: `const` must come before `async`: `const async` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr index c97b4ff0f497e..0c362052501ed 100644 --- a/src/test/ui/async-await/no-unsafe-async.stderr +++ b/src/test/ui/async-await/no-unsafe-async.stderr @@ -5,15 +5,25 @@ LL | impl S { | - while parsing this item list starting here LL | #[cfg(FALSE)] LL | unsafe async fn g() {} - | ^^^^^ expected one of `extern` or `fn` + | -------^^^^^ + | | | + | | expected one of `extern` or `fn` + | help: `async` must come before `unsafe`: `async unsafe` LL | } | - the item list ends here + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` 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` or `fn` + | -------^^^^^ + | | | + | | expected one of `extern` or `fn` + | help: `async` must come before `unsafe`: `async unsafe` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/issue-87217-keyword-order/const-async-const.rs b/src/test/ui/parser/issue-87217-keyword-order/const-async-const.rs new file mode 100644 index 0000000000000..7c3d915a4c0d9 --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/const-async-const.rs @@ -0,0 +1,11 @@ +// edition:2018 + +// Test that even when `const` is already present, the proposed fix is `const const async`, +// like for `pub pub`. + +const async const fn test() {} +//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +//~| NOTE expected one of `extern`, `fn`, or `unsafe` +//~| HELP `const` must come before `async` +//~| SUGGESTION const async +//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issue-87217-keyword-order/const-async-const.stderr b/src/test/ui/parser/issue-87217-keyword-order/const-async-const.stderr new file mode 100644 index 0000000000000..56280912540a7 --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/const-async-const.stderr @@ -0,0 +1,13 @@ +error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const` + --> $DIR/const-async-const.rs:6:13 + | +LL | const async const fn test() {} + | ------^^^^^ + | | | + | | expected one of `extern`, `fn`, or `unsafe` + | help: `const` must come before `async`: `const async` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.rs b/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.rs new file mode 100644 index 0000000000000..86fdb78cce8ab --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.rs @@ -0,0 +1,14 @@ +// edition:2018 + +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +async unsafe const fn test() {} +//~^ ERROR expected one of `extern` or `fn`, found keyword `const` +//~| NOTE expected one of `extern` or `fn` +//~| HELP `const` must come before `async unsafe` +//~| SUGGESTION const async unsafe +//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.stderr b/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.stderr new file mode 100644 index 0000000000000..65cce77be896b --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/several-kw-jump.stderr @@ -0,0 +1,13 @@ +error: expected one of `extern` or `fn`, found keyword `const` + --> $DIR/several-kw-jump.rs:9:14 + | +LL | async unsafe const fn test() {} + | -------------^^^^^ + | | | + | | expected one of `extern` or `fn` + | help: `const` must come before `async unsafe`: `const async unsafe` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-async.rs b/src/test/ui/parser/issue-87217-keyword-order/wrong-async.rs new file mode 100644 index 0000000000000..edfb330d6713a --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-async.rs @@ -0,0 +1,14 @@ +// edition:2018 + +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +unsafe async fn test() {} +//~^ ERROR expected one of `extern` or `fn`, found keyword `async` +//~| NOTE expected one of `extern` or `fn` +//~| HELP `async` must come before `unsafe` +//~| SUGGESTION async unsafe +//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-async.stderr b/src/test/ui/parser/issue-87217-keyword-order/wrong-async.stderr new file mode 100644 index 0000000000000..3acd9e4400432 --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-async.stderr @@ -0,0 +1,13 @@ +error: expected one of `extern` or `fn`, found keyword `async` + --> $DIR/wrong-async.rs:9:8 + | +LL | unsafe async fn test() {} + | -------^^^^^ + | | | + | | expected one of `extern` or `fn` + | help: `async` must come before `unsafe`: `async unsafe` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-const.rs b/src/test/ui/parser/issue-87217-keyword-order/wrong-const.rs new file mode 100644 index 0000000000000..abd692b80d54b --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-const.rs @@ -0,0 +1,14 @@ +// edition:2018 + +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +unsafe const fn test() {} +//~^ ERROR expected one of `extern` or `fn`, found keyword `const` +//~| NOTE expected one of `extern` or `fn` +//~| HELP `const` must come before `unsafe` +//~| SUGGESTION const unsafe +//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-const.stderr b/src/test/ui/parser/issue-87217-keyword-order/wrong-const.stderr new file mode 100644 index 0000000000000..9a3e07b1e87f6 --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-const.stderr @@ -0,0 +1,13 @@ +error: expected one of `extern` or `fn`, found keyword `const` + --> $DIR/wrong-const.rs:9:8 + | +LL | unsafe const fn test() {} + | -------^^^^^ + | | | + | | expected one of `extern` or `fn` + | help: `const` must come before `unsafe`: `const unsafe` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.rs b/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.rs new file mode 100644 index 0000000000000..7f0761e99386a --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.rs @@ -0,0 +1,14 @@ +// edition:2018 + +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +extern unsafe fn test() {} +//~^ ERROR expected `fn`, found keyword `unsafe` +//~| NOTE expected `fn` +//~| HELP `unsafe` must come before `extern` +//~| SUGGESTION unsafe extern +//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.stderr b/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.stderr new file mode 100644 index 0000000000000..395ee9fedbc07 --- /dev/null +++ b/src/test/ui/parser/issue-87217-keyword-order/wrong-unsafe.stderr @@ -0,0 +1,13 @@ +error: expected `fn`, found keyword `unsafe` + --> $DIR/wrong-unsafe.rs:9:8 + | +LL | extern unsafe fn test() {} + | -------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern`: `unsafe extern` + | + = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + +error: aborting due to previous error +