Skip to content

Commit 89f5dcf

Browse files
authored
Rollup merge of rust-lang#68728 - Centril:towards-fn-merge, r=petrochenkov
parse: merge `fn` syntax + cleanup item parsing Here we continue the work in rust-lang#67131 in particular to merge the grammars of `fn` items in various positions. A list of *language level* changes (as sanctioned by the language team in rust-lang#65041 (comment) and rust-lang#67131): - `self` parameters are now *syntactically* allowed as the first parameter irrespective of item context (and in function pointers). Instead, semantic validation (`ast_validation`) is used. - Syntactically, `fn` items in `extern { ... }` blocks can now have bodies (`fn foo() { ... }` as opposed to `fn foo();`). As above, we use semantic restrictions instead. - Syntactically, `fn` items in free contexts (directly in a file or a module) can now be without bodies (`fn foo();` as opposed to `fn foo() { ... }`. As above, we use semantic restrictions instead, including for non-ident parameter patterns. - `const extern fn` feature gating is now done post-expansion such that we do not have conditional compatibilities of function qualifiers *in parsing*. - The `FnFrontMatter` grammar becomes: ```rust Extern = "extern" StringLit ; FnQual = "const"? "async"? "unsafe"? Extern? ; FnFrontMatter = FnQual "fn" ; ``` That is, all item contexts now *syntactically* allow `const async unsafe extern "C" fn` and use semantic restrictions to rule out combinations previously prevented syntactically. The semantic restrictions include in particular: - `fn`s in `extern { ... }` can have no qualifiers. - `const` and `async` cannot be combined. - To fuse the list-of-items parsing in the 4 contexts that items are allowed, we now must permit inner attributes (`#![attr]`) inside `trait Foo { ... }` definitions. That is, we now allow e.g. `trait Foo { #![attr] }`. This was probably an oversight due to not using a uniform parsing mechanism, which we now do have (`fn parse_item_list`). The semantic support (including e.g. for linting) falls out directly from the attributes infrastructure. To ensure this, we include a test for lints. Put together, these grammar changes allow us to substantially reduce the complexity of item parsing and its grammar. There are however some other non-language improvements that allow the compression to take place. A list of *compiler-internal* changes (in particular noting the parser-external data-structure changes): - We use `enum AllowPlus/RecoverQPath/AllowCVariadic { Yes, No }` in `parser/ty.rs` instead of passing around 3 different `bool`s. I felt this was necessary as it was becoming mentally taxing to track which-is-which. - `fn visit_trait_item` and `fn visit_impl_item` are merged into `fn visit_assoc_item` which now is passed an `AssocCtxt` to check which one it is. - We change `FnKind` to: ```rust pub enum FnKind<'a> { Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, Option<&'a Block>), Closure(&'a FnDecl, &'a Expr), } ``` with: ```rust pub enum FnCtxt { Free, Foreign, Assoc(AssocCtxt), } ``` This is then taken advantage of in tweaking the various semantic restrictions as well as in pretty printing. - In `ItemKind::Fn`, we change `P<Block>` to `Option<P<Block>>`. - In `ForeignItemKind::Fn`, we change `P<FnDecl>` to `FnSig` and `P<Block>` to `Option<P<Block>>`. - We change `ast::{Unsafety, Spanned<Constness>}>` into `enum ast::{Unsafe, Const} { Yes(Span), No }` respectively. This change in formulation allow us to exclude `Span` in the case of `No`, which facilitates parsing. Moreover, we also add a `Span` to `IsAsync` which is renamed to `Async`. The new `Span`s in `Unsafety` and `Async` are then taken advantage of for better diagnostics. A reason this change was made is to have a more uniform and clear naming scheme. The HIR keeps the structures in AST (with those definitions moved into HIR) for now to avoid regressing perf. - Various cleanups, bug fixes, and diagnostics improvements are made along the way. It is probably best to understand those via the diffs. I would recommend reviewing this commit-by-commit with whitespace changes hidden. r? @estebank @petrochenkov
2 parents d538b80 + ad72c3a commit 89f5dcf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+596
-723
lines changed

src/librustc_ast_pretty/pprust.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,7 @@ impl<'a> State<'a> {
12691269
self.print_where_clause(&generics.where_clause);
12701270
self.s.word(" ");
12711271
self.bopen();
1272+
self.print_inner_attributes(&item.attrs);
12721273
for trait_item in trait_items {
12731274
self.print_assoc_item(trait_item);
12741275
}

src/librustc_expand/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ pub fn parse_ast_fragment<'a>(
867867
AstFragmentKind::ForeignItems => {
868868
let mut items = SmallVec::new();
869869
while this.token != token::Eof {
870-
items.push(this.parse_foreign_item()?);
870+
items.push(this.parse_foreign_item(&mut false)?);
871871
}
872872
AstFragment::ForeignItems(items)
873873
}

src/librustc_parse/config.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -562,14 +562,9 @@ fn is_cfg(attr: &Attribute) -> bool {
562562

563563
/// Process the potential `cfg` attributes on a module.
564564
/// Also determine if the module should be included in this configuration.
565-
pub fn process_configure_mod(
566-
sess: &ParseSess,
567-
cfg_mods: bool,
568-
attrs: &[Attribute],
569-
) -> (bool, Vec<Attribute>) {
565+
pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec<Attribute>) -> bool {
570566
// Don't perform gated feature checking.
571567
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
572-
let mut attrs = attrs.to_owned();
573-
strip_unconfigured.process_cfg_attrs(&mut attrs);
574-
(!cfg_mods || strip_unconfigured.in_cfg(&attrs), attrs)
568+
strip_unconfigured.process_cfg_attrs(attrs);
569+
!cfg_mods || strip_unconfigured.in_cfg(&attrs)
575570
}

src/librustc_parse/parser/item.rs

+424-554
Large diffs are not rendered by default.

src/librustc_parse/parser/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,11 @@ impl<'a> Parser<'a> {
572572
if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) }
573573
}
574574

575+
/// Is the given keyword `kw` followed by a non-reserved identifier?
576+
fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
577+
self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
578+
}
579+
575580
fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool {
576581
if ok {
577582
true

src/librustc_parse/parser/module.rs

+12-14
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,34 @@ impl<'a> Parser<'a> {
4040
}
4141

4242
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
43-
pub(super) fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> {
44-
let (in_cfg, outer_attrs) =
45-
crate::config::process_configure_mod(self.sess, self.cfg_mods, outer_attrs);
43+
pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
44+
let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs);
4645

4746
let id_span = self.token.span;
4847
let id = self.parse_ident()?;
49-
if self.eat(&token::Semi) {
48+
let (module, mut inner_attrs) = if self.eat(&token::Semi) {
5049
if in_cfg && self.recurse_into_file_modules {
5150
// This mod is in an external file. Let's go get it!
5251
let ModulePathSuccess { path, directory_ownership } =
53-
self.submod_path(id, &outer_attrs, id_span)?;
54-
let (module, attrs) =
55-
self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?;
56-
Ok((id, ItemKind::Mod(module), Some(attrs)))
52+
self.submod_path(id, &attrs, id_span)?;
53+
self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?
5754
} else {
58-
let placeholder = ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false };
59-
Ok((id, ItemKind::Mod(placeholder), None))
55+
(ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new())
6056
}
6157
} else {
6258
let old_directory = self.directory.clone();
63-
self.push_directory(id, &outer_attrs);
59+
self.push_directory(id, &attrs);
6460

6561
self.expect(&token::OpenDelim(token::Brace))?;
6662
let mod_inner_lo = self.token.span;
67-
let attrs = self.parse_inner_attributes()?;
63+
let inner_attrs = self.parse_inner_attributes()?;
6864
let module = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?;
6965

7066
self.directory = old_directory;
71-
Ok((id, ItemKind::Mod(module), Some(attrs)))
72-
}
67+
(module, inner_attrs)
68+
};
69+
attrs.append(&mut inner_attrs);
70+
Ok((id, ItemKind::Mod(module)))
7371
}
7472

7573
/// Given a termination token, parses all of the items in a module.

src/librustc_parse/parser/stmt.rs

+8-32
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use crate::maybe_whole;
77
use crate::DirectoryOwnership;
88

99
use rustc_errors::{Applicability, PResult};
10-
use rustc_span::source_map::{respan, BytePos, Span};
11-
use rustc_span::symbol::{kw, sym, Symbol};
10+
use rustc_span::source_map::{BytePos, Span};
11+
use rustc_span::symbol::{kw, sym};
1212
use syntax::ast;
13-
use syntax::ast::{AttrStyle, AttrVec, Attribute, Mac, MacStmtStyle, VisibilityKind};
13+
use syntax::ast::{AttrStyle, AttrVec, Attribute, Mac, MacStmtStyle};
1414
use syntax::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID};
1515
use syntax::ptr::P;
1616
use syntax::token::{self, TokenKind};
@@ -55,21 +55,11 @@ impl<'a> Parser<'a> {
5555
return self.recover_stmt_local(lo, attrs.into(), msg, "let");
5656
}
5757

58-
let mac_vis = respan(lo, VisibilityKind::Inherited);
59-
if let Some(macro_def) = self.eat_macro_def(&attrs, &mac_vis, lo)? {
60-
return Ok(Some(self.mk_stmt(lo.to(self.prev_span), StmtKind::Item(macro_def))));
61-
}
62-
63-
// Starts like a simple path, being careful to avoid contextual keywords
64-
// such as a union items, item with `crate` visibility or auto trait items.
65-
// Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
66-
// like a path (1 token), but it fact not a path.
67-
if self.token.is_path_start()
68-
&& !self.token.is_qpath_start()
69-
&& !self.is_union_item() // `union::b::c` - path, `union U { ... }` - not a path.
70-
&& !self.is_crate_vis() // `crate::b::c` - path, `crate struct S;` - not a path.
71-
&& !self.is_auto_trait_item()
72-
&& !self.is_async_fn()
58+
// Starts like a simple path, being careful to avoid contextual keywords,
59+
// e.g., `union`, items with `crate` visibility, or `auto trait` items.
60+
// We aim to parse an arbitrary path `a::b` but not something that starts like a path
61+
// (1 token), but it fact not a path. Also, we avoid stealing syntax from `parse_item_`.
62+
if self.token.is_path_start() && !self.token.is_qpath_start() && !self.is_path_start_item()
7363
{
7464
let path = self.parse_path(PathStyle::Expr)?;
7565

@@ -199,10 +189,6 @@ impl<'a> Parser<'a> {
199189
}
200190
}
201191

202-
fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
203-
self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
204-
}
205-
206192
fn recover_stmt_local(
207193
&mut self,
208194
lo: Span,
@@ -299,16 +285,6 @@ impl<'a> Parser<'a> {
299285
}
300286
}
301287

302-
fn is_auto_trait_item(&self) -> bool {
303-
// auto trait
304-
(self.token.is_keyword(kw::Auto) &&
305-
self.is_keyword_ahead(1, &[kw::Trait]))
306-
|| // unsafe auto trait
307-
(self.token.is_keyword(kw::Unsafe) &&
308-
self.is_keyword_ahead(1, &[kw::Auto]) &&
309-
self.is_keyword_ahead(2, &[kw::Trait]))
310-
}
311-
312288
/// Parses a block. No inner attributes are allowed.
313289
pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
314290
maybe_whole!(self, NtBlock, |x| x);

src/test/pretty/trait-inner-attr.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// pp-exact
2+
3+
trait Foo {
4+
#![allow(bar)]
5+
}
6+
7+
fn main() { }

src/test/ui/issues/issue-58856-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ LL | fn how_are_you(&self -> Empty {
77
| | help: `)` may belong here
88
| unclosed delimiter
99

10-
error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `)`
10+
error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, `}`, or identifier, found `)`
1111
--> $DIR/issue-58856-2.rs:11:1
1212
|
1313
LL | }
14-
| - expected one of 10 possible tokens
14+
| - expected one of 11 possible tokens
1515
LL | }
1616
| ^ unexpected token
1717

src/test/ui/issues/issue-60075.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `}`
44
LL | });
55
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
66

7-
error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `;`
7+
error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, `}`, or identifier, found `;`
88
--> $DIR/issue-60075.rs:6:11
99
|
1010
LL | fn qux() -> Option<usize> {

src/test/ui/macros/issue-54441.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
macro_rules! m {
2+
//~^ ERROR missing `fn`, `type`, or `static` for extern-item declaration
23
() => {
3-
let //~ ERROR expected
4+
let
45
};
56
}
67

src/test/ui/macros/issue-54441.stderr

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
error: expected one of `async`, `const`, `crate`, `extern`, `fn`, `pub`, `static`, `type`, or `unsafe`, found keyword `let`
2-
--> $DIR/issue-54441.rs:3:9
1+
error: missing `fn`, `type`, or `static` for extern-item declaration
2+
--> $DIR/issue-54441.rs:1:1
33
|
4-
LL | let
5-
| ^^^ expected one of 9 possible tokens
6-
...
7-
LL | m!();
8-
| ----- in this macro invocation
9-
|
10-
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
4+
LL | / macro_rules! m {
5+
LL | |
6+
LL | | () => {
7+
LL | | let
8+
| |________^ missing `fn`, `type`, or `static`
119

1210
error: aborting due to previous error
1311

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: expected item after attributes
2-
--> $DIR/attr-before-eof.rs:3:16
2+
--> $DIR/attr-before-eof.rs:3:1
33
|
44
LL | #[derive(Debug)]
5-
| ^
5+
| ^^^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: expected item after attributes
2-
--> $DIR/attr-dangling-in-mod.rs:6:14
2+
--> $DIR/attr-dangling-in-mod.rs:6:1
33
|
44
LL | #[foo = "bar"]
5-
| ^
5+
| ^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
// Constants (static variables) can be used to match in patterns, but mutable
2-
// statics cannot. This ensures that there's some form of error if this is
3-
// attempted.
1+
// Make sure there's an error when given `extern { ... #[attr] }`.
42

5-
extern crate libc;
3+
fn main() {}
64

75
extern {
8-
static mut rust_dbg_static_mut: libc::c_int;
9-
pub fn rust_dbg_static_mut_check_four();
106
#[cfg(stage37)] //~ ERROR expected item after attributes
117
}
12-
13-
pub fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: expected item after attributes
2-
--> $DIR/attrs-after-extern-mod.rs:10:19
2+
--> $DIR/attrs-after-extern-mod.rs:6:5
33
|
44
LL | #[cfg(stage37)]
5-
| ^
5+
| ^^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

src/test/ui/parser/default.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ impl Foo for u16 {
1919
}
2020

2121
impl Foo for u32 { //~ ERROR not all trait items implemented, missing: `foo`
22-
default pub fn foo<T: Default>() -> T { T::default() } //~ ERROR expected one of
22+
default pub fn foo<T: Default>() -> T { T::default() }
23+
//~^ ERROR missing `fn`, `type`, or `const` for associated-item declaration
2324
}
2425

2526
fn main() {}

src/test/ui/parser/default.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found keyword `pub`
2-
--> $DIR/default.rs:22:13
1+
error: missing `fn`, `type`, or `const` for associated-item declaration
2+
--> $DIR/default.rs:22:12
33
|
44
LL | default pub fn foo<T: Default>() -> T { T::default() }
5-
| ^^^ expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`
5+
| ^ missing `fn`, `type`, or `const`
66

77
error[E0449]: unnecessary visibility qualifier
88
--> $DIR/default.rs:16:5
+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
error: expected item after attributes
2-
--> $DIR/doc-before-attr.rs:4:16
2+
--> $DIR/doc-before-attr.rs:4:1
33
|
4+
LL | /// hi
5+
| ------ other attributes here
46
LL | #[derive(Debug)]
5-
| ^
7+
| ^^^^^^^^^^^^^^^^
68

79
error: aborting due to previous error
810

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
fn main() {}
2+
13
extern {
24
/// hi
3-
//~^ ERROR expected item after doc comment
5+
//~^ ERROR found a documentation comment that doesn't document anything
46
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error: expected item after doc comment
2-
--> $DIR/doc-before-extern-rbrace.rs:2:5
1+
error[E0584]: found a documentation comment that doesn't document anything
2+
--> $DIR/doc-before-extern-rbrace.rs:4:5
33
|
44
LL | /// hi
55
| ^^^^^^ this doc comment doesn't document anything
6+
|
7+
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
68

79
error: aborting due to previous error
810

11+
For more information about this error, try `rustc --explain E0584`.

src/test/ui/parser/doc-inside-trait-item.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0584]: found a documentation comment that doesn't document anything
22
--> $DIR/doc-inside-trait-item.rs:3:5
33
|
44
LL | /// empty doc
5-
| ^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^ this doc comment doesn't document anything
66
|
77
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
88

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn`
1+
fn main() {}
2+
23
extern {
34
pub pub fn foo();
5+
//~^ ERROR missing `fn`, `type`, or `static` for extern-item declaration
46
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `(`, `async`, `const`, `extern`, `fn`, `static`, `type`, or `unsafe`, found keyword `pub`
2-
--> $DIR/duplicate-visibility.rs:3:9
1+
error: missing `fn`, `type`, or `static` for extern-item declaration
2+
--> $DIR/duplicate-visibility.rs:4:8
33
|
44
LL | pub pub fn foo();
5-
| ^^^ expected one of 8 possible tokens
5+
| ^ missing `fn`, `type`, or `static`
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// check-pass
2+
3+
#![deny(non_camel_case_types)]
4+
5+
fn main() {}
6+
7+
trait foo_bar {
8+
#![allow(non_camel_case_types)]
9+
}

src/test/ui/parser/issue-19398.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
trait T {
2-
extern "Rust" unsafe fn foo(); //~ ERROR expected one of `async`, `const`
2+
//~^ ERROR missing `fn`, `type`, or `const` for associated-item declaration
3+
extern "Rust" unsafe fn foo();
34
}
45

56
fn main() {}

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `extern`
2-
--> $DIR/issue-19398.rs:2:5
1+
error: missing `fn`, `type`, or `const` for associated-item declaration
2+
--> $DIR/issue-19398.rs:1:10
33
|
4-
LL | trait T {
5-
| - expected one of 10 possible tokens
6-
LL | extern "Rust" unsafe fn foo();
7-
| ^^^^^^ unexpected token
4+
LL | trait T {
5+
| __________^
6+
LL | |
7+
LL | | extern "Rust" unsafe fn foo();
8+
| |____^ missing `fn`, `type`, or `const`
89

910
error: aborting due to previous error
1011

0 commit comments

Comments
 (0)