Skip to content

Commit 9321efd

Browse files
committed
Detect pub fn attr wrong order like async pub
Redirects `const? async? unsafe? pub` to `pub const? async? unsafe?`. Fix #76437
1 parent f5d8117 commit 9321efd

18 files changed

+156
-42
lines changed

compiler/rustc_parse/src/parser/item.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,8 @@ impl<'a> Parser<'a> {
16391639
pub(super) fn check_fn_front_matter(&mut self) -> bool {
16401640
// We use an over-approximation here.
16411641
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
1642-
const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
1642+
// `pub` is added in case users got confused with the ordering like `async pub fn`.
1643+
const QUALS: [Symbol; 5] = [kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern];
16431644
self.check_keyword(kw::Fn) // Definitely an `fn`.
16441645
// `$qual fn` or `$qual $qual`:
16451646
|| QUALS.iter().any(|&kw| self.check_keyword(kw))
@@ -1668,6 +1669,7 @@ impl<'a> Parser<'a> {
16681669
/// FnFrontMatter = FnQual "fn" ;
16691670
/// ```
16701671
pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
1672+
let sp_start = self.token.span;
16711673
let constness = self.parse_constness();
16721674
let asyncness = self.parse_asyncness();
16731675
let unsafety = self.parse_unsafety();
@@ -1681,8 +1683,27 @@ impl<'a> Parser<'a> {
16811683
// It is possible for `expect_one_of` to recover given the contents of
16821684
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
16831685
// account for this.
1684-
if !self.expect_one_of(&[], &[])? {
1685-
unreachable!()
1686+
match self.expect_one_of(&[], &[]) {
1687+
Ok(true) => {}
1688+
Ok(false) => unreachable!(),
1689+
Err(mut err) => {
1690+
// Recover incorrect visibility order such as `async pub`.
1691+
if self.check_keyword(kw::Pub) {
1692+
let sp = sp_start.to(self.prev_token.span);
1693+
if let Ok(snippet) = self.span_to_snippet(sp) {
1694+
let vis = self.parse_visibility(FollowedByType::No)?;
1695+
let vs = pprust::vis_to_string(&vis);
1696+
let vs = vs.trim_end();
1697+
err.span_suggestion(
1698+
sp_start.to(self.prev_token.span),
1699+
&format!("visibility `{}` must come before `{}`", vs, snippet),
1700+
format!("{} {}", vs, snippet),
1701+
Applicability::MachineApplicable,
1702+
);
1703+
}
1704+
}
1705+
return Err(err);
1706+
}
16861707
}
16871708
}
16881709

src/test/ui/parser/default.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-linelength
12
// Test successful and unsuccessful parsing of the `default` contextual keyword
23

34
#![feature(specialization)]
@@ -21,8 +22,7 @@ impl Foo for u16 {
2122

2223
impl Foo for u32 { //~ ERROR not all trait items implemented, missing: `foo`
2324
default pub fn foo<T: Default>() -> T { T::default() }
24-
//~^ ERROR `default` is not followed by an item
25-
//~| ERROR non-item in item list
25+
//~^ ERROR expected one of `async`, `const`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
2626
}
2727

2828
fn main() {}

src/test/ui/parser/default.stderr

+13-18
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
1-
error: `default` is not followed by an item
2-
--> $DIR/default.rs:23:5
3-
|
4-
LL | default pub fn foo<T: Default>() -> T { T::default() }
5-
| ^^^^^^^ the `default` qualifier
6-
|
7-
= note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
8-
9-
error: non-item in item list
10-
--> $DIR/default.rs:23:13
1+
error: expected one of `async`, `const`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
2+
--> $DIR/default.rs:24:13
113
|
124
LL | impl Foo for u32 {
13-
| - item list starts here
5+
| - while parsing this item list starting here
146
LL | default pub fn foo<T: Default>() -> T { T::default() }
15-
| ^^^ non-item starts here
16-
...
7+
| ^^^
8+
| |
9+
| expected one of 7 possible tokens
10+
| help: visibility `pub` must come before `default pub`: `pub default pub`
11+
LL |
1712
LL | }
18-
| - item list ends here
13+
| - the item list ends here
1914

2015
error[E0449]: unnecessary visibility qualifier
21-
--> $DIR/default.rs:17:5
16+
--> $DIR/default.rs:18:5
2217
|
2318
LL | pub default fn foo<T: Default>() -> T {
2419
| ^^^ `pub` not permitted here because it's implied
2520

2621
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
27-
--> $DIR/default.rs:3:12
22+
--> $DIR/default.rs:4:12
2823
|
2924
LL | #![feature(specialization)]
3025
| ^^^^^^^^^^^^^^
@@ -34,15 +29,15 @@ LL | #![feature(specialization)]
3429
= help: consider using `min_specialization` instead, which is more stable and complete
3530

3631
error[E0046]: not all trait items implemented, missing: `foo`
37-
--> $DIR/default.rs:22:1
32+
--> $DIR/default.rs:23:1
3833
|
3934
LL | fn foo<T: Default>() -> T;
4035
| -------------------------- `foo` from trait
4136
...
4237
LL | impl Foo for u32 {
4338
| ^^^^^^^^^^^^^^^^ missing `foo` in implementation
4439

45-
error: aborting due to 4 previous errors; 1 warning emitted
40+
error: aborting due to 3 previous errors; 1 warning emitted
4641

4742
Some errors have detailed explanations: E0046, E0449.
4843
For more information about an error, try `rustc --explain E0046`.
+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
// ignore-tidy-linelength
2+
13
fn main() {}
24

35
extern "C" {
46
pub pub fn foo();
5-
//~^ ERROR visibility `pub` is not followed by an item
6-
//~| ERROR non-item in item list
7+
//~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
78
}
+10-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
error: visibility `pub` is not followed by an item
2-
--> $DIR/duplicate-visibility.rs:4:5
3-
|
4-
LL | pub pub fn foo();
5-
| ^^^ the visibility
6-
|
7-
= help: you likely meant to define an item, e.g., `pub fn foo() {}`
8-
9-
error: non-item in item list
10-
--> $DIR/duplicate-visibility.rs:4:9
1+
error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
2+
--> $DIR/duplicate-visibility.rs:6:9
113
|
124
LL | extern "C" {
13-
| - item list starts here
5+
| - while parsing this item list starting here
146
LL | pub pub fn foo();
15-
| ^^^ non-item starts here
16-
...
7+
| ^^^
8+
| |
9+
| expected one of 9 possible tokens
10+
| help: visibility `pub` must come before `pub pub`: `pub pub pub`
11+
LL |
1712
LL | }
18-
| - item list ends here
13+
| - the item list ends here
1914

20-
error: aborting due to 2 previous errors
15+
error: aborting due to previous error
2116

src/test/ui/parser/issue-63116.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: this file contains an unclosed delimiter
1+
=rror: this file contains an unclosed delimiter
22
--> $DIR/issue-63116.rs:3:18
33
|
44
LL | impl W <s(f;Y(;]
@@ -12,7 +12,7 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;`
1212
LL | impl W <s(f;Y(;]
1313
| ^ expected one of 7 possible tokens
1414

15-
error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `:`, `<`, `=`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;`
15+
error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, lifetime, or path, found `;`
1616
--> $DIR/issue-63116.rs:3:15
1717
|
1818
LL | impl W <s(f;Y(;]
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
async pub fn t() {}
5+
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub`
2+
--> $DIR/issue-76437-async.rs:4:11
3+
|
4+
LL | async pub fn t() {}
5+
| ------^^^
6+
| | |
7+
| | expected one of `extern`, `fn`, or `unsafe`
8+
| help: visibility `pub` must come before `async`: `pub async`
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
const async unsafe pub fn t() {}
5+
//~^ ERROR expected one of `extern` or `fn`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `extern` or `fn`, found keyword `pub`
2+
--> $DIR/issue-76437-const-async-unsafe.rs:4:24
3+
|
4+
LL | const async unsafe pub fn t() {}
5+
| -------------------^^^
6+
| | |
7+
| | expected one of `extern` or `fn`
8+
| help: visibility `pub` must come before `const async unsafe`: `pub const async unsafe`
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
const async pub fn t() {}
5+
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub`
2+
--> $DIR/issue-76437-const-async.rs:4:17
3+
|
4+
LL | const async pub fn t() {}
5+
| ------------^^^
6+
| | |
7+
| | expected one of `extern`, `fn`, or `unsafe`
8+
| help: visibility `pub` must come before `const async`: `pub const async`
9+
10+
error: aborting due to previous error
11+
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
const pub fn t() {}
5+
//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub`
2+
--> $DIR/issue-76437-const.rs:4:11
3+
|
4+
LL | const pub fn t() {}
5+
| ------^^^
6+
| | |
7+
| | expected one of `async`, `extern`, `fn`, or `unsafe`
8+
| help: visibility `pub` must come before `const`: `pub const`
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
unsafe pub(crate) fn t() {}
5+
//~^ ERROR expected one of `extern` or `fn`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `extern` or `fn`, found keyword `pub`
2+
--> $DIR/issue-76437-pub-crate-unsafe.rs:4:12
3+
|
4+
LL | unsafe pub(crate) fn t() {}
5+
| -------^^^-------
6+
| | |
7+
| | expected one of `extern` or `fn`
8+
| help: visibility `pub(crate)` must come before `unsafe`: `pub(crate) unsafe`
9+
10+
error: aborting due to previous error
11+
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// edition:2018
2+
3+
mod t {
4+
unsafe pub fn t() {}
5+
//~^ ERROR expected one of `extern` or `fn`, found keyword `pub`
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: expected one of `extern` or `fn`, found keyword `pub`
2+
--> $DIR/issue-76437-unsafe.rs:4:12
3+
|
4+
LL | unsafe pub fn t() {}
5+
| -------^^^
6+
| | |
7+
| | expected one of `extern` or `fn`
8+
| help: visibility `pub` must come before `unsafe`: `pub unsafe`
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)