Skip to content

Commit 54e7946

Browse files
authored
Rollup merge of #87901 - poliorcetics:pub-pub-pub, r=jackh726
Fix suggestion of additional `pub` when using `pub pub fn ...` Fix #87694. Marked as draft to start with because I want to explore doing the same fix for `const const fn` and other repeated-but-valid keywords. `@rustbot` label A-diagnostics D-invalid-suggestion T-compiler
2 parents dde825d + be33ca7 commit 54e7946

10 files changed

+154
-45
lines changed

compiler/rustc_parse/src/parser/item.rs

+83-29
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl<'a> Parser<'a> {
223223
(Ident::empty(), ItemKind::Use(tree))
224224
} else if self.check_fn_front_matter(def_final) {
225225
// FUNCTION ITEM
226-
let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo)?;
226+
let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?;
227227
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body })))
228228
} else if self.eat_keyword(kw::Extern) {
229229
if self.eat_keyword(kw::Crate) {
@@ -1511,9 +1511,16 @@ impl<'a> Parser<'a> {
15111511
let (ident, is_raw) = self.ident_or_err()?;
15121512
if !is_raw && ident.is_reserved() {
15131513
let err = if self.check_fn_front_matter(false) {
1514+
let inherited_vis = Visibility {
1515+
span: rustc_span::DUMMY_SP,
1516+
kind: VisibilityKind::Inherited,
1517+
tokens: None,
1518+
};
15141519
// We use `parse_fn` to get a span for the function
15151520
let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
1516-
if let Err(mut db) = self.parse_fn(&mut Vec::new(), fn_parse_mode, lo) {
1521+
if let Err(mut db) =
1522+
self.parse_fn(&mut Vec::new(), fn_parse_mode, lo, &inherited_vis)
1523+
{
15171524
db.delay_as_bug();
15181525
}
15191526
let mut err = self.struct_span_err(
@@ -1793,8 +1800,9 @@ impl<'a> Parser<'a> {
17931800
attrs: &mut Vec<Attribute>,
17941801
fn_parse_mode: FnParseMode,
17951802
sig_lo: Span,
1803+
vis: &Visibility,
17961804
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
1797-
let header = self.parse_fn_front_matter()?; // `const ... fn`
1805+
let header = self.parse_fn_front_matter(vis)?; // `const ... fn`
17981806
let ident = self.parse_ident()?; // `foo`
17991807
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
18001808
let decl =
@@ -1903,12 +1911,15 @@ impl<'a> Parser<'a> {
19031911
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
19041912
/// up to and including the `fn` keyword. The formal grammar is:
19051913
///
1906-
/// ```
1914+
/// ```text
19071915
/// Extern = "extern" StringLit? ;
19081916
/// FnQual = "const"? "async"? "unsafe"? Extern? ;
19091917
/// FnFrontMatter = FnQual "fn" ;
19101918
/// ```
1911-
pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
1919+
///
1920+
/// `vis` represents the visibility that was already parsed, if any. Use
1921+
/// `Visibility::Inherited` when no visibility is known.
1922+
pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> {
19121923
let sp_start = self.token.span;
19131924
let constness = self.parse_constness();
19141925

@@ -1934,51 +1945,94 @@ impl<'a> Parser<'a> {
19341945
Ok(false) => unreachable!(),
19351946
Err(mut err) => {
19361947
// Qualifier keywords ordering check
1948+
enum WrongKw {
1949+
Duplicated(Span),
1950+
Misplaced(Span),
1951+
}
19371952

1938-
// This will allow the machine fix to directly place the keyword in the correct place
1939-
let current_qual_sp = if self.check_keyword(kw::Const) {
1940-
Some(async_start_sp)
1953+
// This will allow the machine fix to directly place the keyword in the correct place or to indicate
1954+
// that the keyword is already present and the second instance should be removed.
1955+
let wrong_kw = if self.check_keyword(kw::Const) {
1956+
match constness {
1957+
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
1958+
Const::No => Some(WrongKw::Misplaced(async_start_sp)),
1959+
}
19411960
} else if self.check_keyword(kw::Async) {
1942-
Some(unsafe_start_sp)
1961+
match asyncness {
1962+
Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)),
1963+
Async::No => Some(WrongKw::Misplaced(unsafe_start_sp)),
1964+
}
19431965
} else if self.check_keyword(kw::Unsafe) {
1944-
Some(ext_start_sp)
1966+
match unsafety {
1967+
Unsafe::Yes(sp) => Some(WrongKw::Duplicated(sp)),
1968+
Unsafe::No => Some(WrongKw::Misplaced(ext_start_sp)),
1969+
}
19451970
} else {
19461971
None
19471972
};
19481973

1949-
if let Some(current_qual_sp) = current_qual_sp {
1950-
let current_qual_sp = current_qual_sp.to(self.prev_token.span);
1951-
if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) {
1952-
let invalid_qual_sp = self.token.uninterpolated_span();
1953-
let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap();
1974+
// The keyword is already present, suggest removal of the second instance
1975+
if let Some(WrongKw::Duplicated(original_sp)) = wrong_kw {
1976+
let original_kw = self
1977+
.span_to_snippet(original_sp)
1978+
.expect("Span extracted directly from keyword should always work");
1979+
1980+
err.span_suggestion(
1981+
self.token.uninterpolated_span(),
1982+
&format!("`{}` already used earlier, remove this one", original_kw),
1983+
"".to_string(),
1984+
Applicability::MachineApplicable,
1985+
)
1986+
.span_note(original_sp, &format!("`{}` first seen here", original_kw));
1987+
}
1988+
// The keyword has not been seen yet, suggest correct placement in the function front matter
1989+
else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw {
1990+
let correct_pos_sp = correct_pos_sp.to(self.prev_token.span);
1991+
if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) {
1992+
let misplaced_qual_sp = self.token.uninterpolated_span();
1993+
let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap();
19541994

19551995
err.span_suggestion(
1956-
current_qual_sp.to(invalid_qual_sp),
1957-
&format!("`{}` must come before `{}`", invalid_qual, current_qual),
1958-
format!("{} {}", invalid_qual, current_qual),
1959-
Applicability::MachineApplicable,
1960-
).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
1996+
correct_pos_sp.to(misplaced_qual_sp),
1997+
&format!("`{}` must come before `{}`", misplaced_qual, current_qual),
1998+
format!("{} {}", misplaced_qual, current_qual),
1999+
Applicability::MachineApplicable,
2000+
).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
19612001
}
19622002
}
1963-
// Recover incorrect visibility order such as `async pub`.
2003+
// Recover incorrect visibility order such as `async pub`
19642004
else if self.check_keyword(kw::Pub) {
19652005
let sp = sp_start.to(self.prev_token.span);
19662006
if let Ok(snippet) = self.span_to_snippet(sp) {
1967-
let vis = match self.parse_visibility(FollowedByType::No) {
2007+
let current_vis = match self.parse_visibility(FollowedByType::No) {
19682008
Ok(v) => v,
19692009
Err(mut d) => {
19702010
d.cancel();
19712011
return Err(err);
19722012
}
19732013
};
1974-
let vs = pprust::vis_to_string(&vis);
2014+
let vs = pprust::vis_to_string(&current_vis);
19752015
let vs = vs.trim_end();
1976-
err.span_suggestion(
1977-
sp_start.to(self.prev_token.span),
1978-
&format!("visibility `{}` must come before `{}`", vs, snippet),
1979-
format!("{} {}", vs, snippet),
1980-
Applicability::MachineApplicable,
1981-
);
2016+
2017+
// There was no explicit visibility
2018+
if matches!(orig_vis.kind, VisibilityKind::Inherited) {
2019+
err.span_suggestion(
2020+
sp_start.to(self.prev_token.span),
2021+
&format!("visibility `{}` must come before `{}`", vs, snippet),
2022+
format!("{} {}", vs, snippet),
2023+
Applicability::MachineApplicable,
2024+
);
2025+
}
2026+
// There was an explicit visibility
2027+
else {
2028+
err.span_suggestion(
2029+
current_vis.span,
2030+
"there is already a visibility modifier, remove one",
2031+
"".to_string(),
2032+
Applicability::MachineApplicable,
2033+
)
2034+
.span_note(orig_vis.span, "explicit visibility first seen here");
2035+
}
19822036
}
19832037
}
19842038
return Err(err);

compiler/rustc_parse/src/parser/ty.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,13 @@ impl<'a> Parser<'a> {
474474
params: Vec<GenericParam>,
475475
recover_return_sign: RecoverReturnSign,
476476
) -> PResult<'a, TyKind> {
477-
let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?;
477+
let inherited_vis = rustc_ast::Visibility {
478+
span: rustc_span::DUMMY_SP,
479+
kind: rustc_ast::VisibilityKind::Inherited,
480+
tokens: None,
481+
};
482+
let ast::FnHeader { ext, unsafety, constness, asyncness } =
483+
self.parse_fn_front_matter(&inherited_vis)?;
478484
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
479485
let whole_span = lo.to(self.prev_token.span);
480486
if let ast::Const::Yes(span) = constness {
+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
fn main() {}
22

3-
extern "C" {
3+
extern "C" { //~ NOTE while parsing this item list starting here
44
pub pub fn foo();
55
//~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub`
6-
}
6+
//~| NOTE expected one of 9 possible tokens
7+
//~| HELP there is already a visibility modifier, remove one
8+
//~| NOTE explicit visibility first seen here
9+
} //~ NOTE the item list ends here

src/test/ui/parser/duplicate-visibility.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ LL | pub pub fn foo();
77
| ^^^
88
| |
99
| expected one of 9 possible tokens
10-
| help: visibility `pub` must come before `pub pub`: `pub pub pub`
11-
LL |
10+
| help: there is already a visibility modifier, remove one
11+
...
1212
LL | }
1313
| - the item list ends here
14+
|
15+
note: explicit visibility first seen here
16+
--> $DIR/duplicate-visibility.rs:4:5
17+
|
18+
LL | pub pub fn foo();
19+
| ^^^
1420

1521
error: aborting due to previous error
1622

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub const pub fn test() {}
2+
//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub`
3+
//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe`
4+
//~| HELP there is already a visibility modifier, remove one
5+
//~| NOTE explicit visibility first seen here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub`
2+
--> $DIR/issue-87694-duplicated-pub.rs:1:11
3+
|
4+
LL | pub const pub fn test() {}
5+
| ^^^
6+
| |
7+
| expected one of `async`, `extern`, `fn`, or `unsafe`
8+
| help: there is already a visibility modifier, remove one
9+
|
10+
note: explicit visibility first seen here
11+
--> $DIR/issue-87694-duplicated-pub.rs:1:1
12+
|
13+
LL | pub const pub fn test() {}
14+
| ^^^
15+
16+
error: aborting due to previous error
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const pub fn test() {}
2+
//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub`
3+
//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe`
4+
//~| HELP visibility `pub` must come before `const`
5+
//~| SUGGESTION pub const
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-87694-misplaced-pub.rs:1:7
3+
|
4+
LL | const pub fn test() {}
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
@@ -1,11 +1,9 @@
11
// edition:2018
22

3-
// Test that even when `const` is already present, the proposed fix is `const const async`,
4-
// like for `pub pub`.
3+
// Test that even when `const` is already present, the proposed fix is to remove the second `const`
54

65
const async const fn test() {}
76
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
87
//~| NOTE expected one of `extern`, `fn`, or `unsafe`
9-
//~| HELP `const` must come before `async`
10-
//~| SUGGESTION const async
11-
//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
8+
//~| HELP `const` already used earlier, remove this one
9+
//~| NOTE `const` first seen here
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
2-
--> $DIR/const-async-const.rs:6:13
2+
--> $DIR/const-async-const.rs:5:13
33
|
44
LL | const async const fn test() {}
5-
| ------^^^^^
6-
| | |
7-
| | expected one of `extern`, `fn`, or `unsafe`
8-
| help: `const` must come before `async`: `const async`
5+
| ^^^^^
6+
| |
7+
| expected one of `extern`, `fn`, or `unsafe`
8+
| help: `const` already used earlier, remove this one
99
|
10-
= note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
10+
note: `const` first seen here
11+
--> $DIR/const-async-const.rs:5:1
12+
|
13+
LL | const async const fn test() {}
14+
| ^^^^^
1115

1216
error: aborting due to previous error
1317

0 commit comments

Comments
 (0)