8000 parse: unify function front matter parsing by Centril · Pull Request #69023 · rust-lang/rust · GitHub
[go: up one dir, main page]

Skip to content

parse: unify function front matter parsing #69023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
parser: fuse free fn parsing together.
  • Loading branch information
Centril committed Feb 13, 2020
commit a833be21626890de406e12f2561d2ffbda4aadb4
14 changes: 14 additions & 0 deletions src/librustc_ast_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

self.check_c_varadic_type(fk);

// Functions cannot both be `const async`
if let Some(FnHeader {
constness: Const::Yes(cspan),
asyncness: Async::Yes { span: aspan, .. },
..
}) = fk.header()
{
self.err_handler()
.struct_span_err(span, "functions cannot be both `const` and `async`")
.span_label(*cspan, "`const` because of this")
.span_label(*aspan, "`async` because of this")
.emit();
}

// Functions without bodies cannot have patterns.
if let FnKind::Fn(ctxt, _, sig, _, None) = fk {
Self::check_decl_no_pat(&sig.decl, |span, mut_ident| {
Expand Down
12 changes: 11 additions & 1 deletion src/librustc_ast_passes/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
if let Some(header) = fn_kind.header() {
// Stability of const fn methods are covered in `visit_assoc_item` below.
self.check_extern(header.ext);

if let (ast::Const::Yes(_), ast::Extern::Implicit)
| (ast::Const::Yes(_), ast::Extern::Explicit(_)) = (header.constness, header.ext)
{
gate_feature_post!(
&self,
const_extern_fn,
span,
"`const extern fn` definitions are unstable"
);
}
}

if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
Expand Down Expand Up @@ -595,7 +606,6 @@ pub fn check_crate(
gate_all!(async_closure, "async closures are unstable");
gate_all!(generators, "yield syntax is experimental");
gate_all!(or_patterns, "or-patterns syntax is experimental");
gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
gate_all!(raw_ref_op, "raw address of syntax is experimental");
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
gate_all!(const_trait_impl, "const trait impls are experimental");
Expand Down
111 changes: 25 additions & 86 deletions src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::BytePos;
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind};
use syntax::ast::{Async, Const, Defaultness, Extern, IsAuto, PathSegment, StrLit, Unsafe};
use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, StrLit, Unsafe};
use syntax::ast::{BindingMode, Block, FnDecl, FnSig, Mac, MacArgs, MacDelimiter, Param, SelfKind};
use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
Expand Down Expand Up @@ -96,53 +96,30 @@ impl<'a> Parser<'a> {
return Ok(Some(item));
}

if self.is_fn_front_matter() {
// FUNCTION ITEM
return self.parse_item_fn(lo, vis, attrs);
}

if self.eat_keyword(kw::Extern) {
if self.eat_keyword(kw::Crate) {
// EXTERN CRATE
return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
}

// EXTERN BLOCK
let abi = self.parse_abi();

if self.eat_keyword(kw::Fn) {
// EXTERN FUNCTION ITEM
let header = FnHeader {
unsafety: Unsafe::No,
asyncness: Async::No,
constness: Const::No,
ext: Extern::from_abi(abi),
};
return self.parse_item_fn(lo, vis, attrs, header);
} else if self.check(&token::OpenDelim(token::Brace)) {
return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
}

self.unexpected()?;
return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
}

if self.is_static_global() {
self.bump();
// STATIC ITEM
self.bump();
let m = self.parse_mutability();
let info = self.parse_item_const(Some(m))?;
return self.mk_item_with_info(attrs, lo, vis, info);
}

let constness = self.parse_constness();
if let Const::Yes(const_span) = constness {
if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
// CONST FUNCTION ITEM
let unsafety = self.parse_unsafety();

if self.check_keyword(kw::Extern) {
self.sess.gated_spans.gate(sym::const_extern_fn, lo.to(self.token.span));
}
let ext = self.parse_extern()?;
self.expect_keyword(kw::Fn)?;

let header = FnHeader { unsafety, asyncness: Async::No, constness, ext }; A3E2
return self.parse_item_fn(lo, vis, attrs, header);
}

if let Const::Yes(const_span) = self.parse_constness() {
// CONST ITEM
if self.eat_keyword(kw::Mut) {
let prev_span = self.prev_span;
Expand All @@ -161,21 +138,6 @@ impl<'a> Parser<'a> {
return self.mk_item_with_info(attrs, lo, vis, info);
}

// Parses `async unsafe? fn`.
if self.check_keyword(kw::Async) {
let async_span = self.token.span;
if self.is_keyword_ahead(1, &[kw::Fn]) || self.is_keyword_ahead(2, &[kw::Fn]) {
// ASYNC FUNCTION ITEM
let asyncness = self.parse_asyncness(); // `async`
let unsafety = self.parse_unsafety(); // `unsafe`?
self.expect_keyword(kw::Fn)?; // `fn`
self.ban_async_in_2015(async_span);
let header =
FnHeader { unsafety, asyncness, constness: Const::No, ext: Extern::None };
return self.parse_item_fn(lo, vis, attrs, header);
}
}

if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) {
// UNSAFE TRAIT ITEM
let unsafety = self.parse_unsafety();
Expand All @@ -195,26 +157,6 @@ impl<'a> Parser<'a> {
return self.mk_item_with_info(attrs, lo, vis, info);
}

if self.check_keyword(kw::Fn) {
// FUNCTION ITEM
self.bump();
let header = FnHeader::default();
return self.parse_item_fn(lo, vis, attrs, header);
}

if self.check_keyword(kw::Unsafe)
&& self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace))
{
// UNSAFE FUNCTION ITEM
let unsafety = self.parse_unsafety();
// `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
self.check(&token::OpenDelim(token::Brace));
let ext = self.parse_extern()?;
self.expect_keyword(kw::Fn)?;
let header = FnHeader { unsafety, asyncness: Async::No, constness: Const::No, ext };
return self.parse_item_fn(lo, vis, attrs, header);
}

if self.eat_keyword(kw::Mod) {
// MODULE ITEM
let info = self.parse_item_mod(&attrs[..])?;
Expand Down Expand Up @@ -1662,9 +1604,9 @@ impl<'a> Parser<'a> {
lo: Span,
vis: Visibility,
mut attrs: Vec<Attribute>,
header: FnHeader,
) -> PResult<'a, Option<P<Item>>> {
let cfg = ParamCfg { is_name_required: |_| true };
let header = self.parse_fn_front_matter()?;
let (ident, decl, generics) = self.parse_fn_sig(&cfg)?;
let body = self.parse_fn_body(&mut false, &mut attrs)?;
let kind = ItemKind::Fn(FnSig { decl, header }, generics, body);
Expand Down Expand Up @@ -1730,27 +1672,24 @@ impl<'a> Parser<'a> {
Ok(body)
}

/// Is the current token unambiguously the start of an `FnHeader`?
/// Is the current token the start of an `FnHeader` / not a valid parse?
fn is_fn_front_matter(&mut self) -> bool {
// We use an over-approximation here.
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
// This works for `async fn` and similar as `async async` is an invalid
// parse and `async fn` is never a valid parse on previous editions.
const QUALIFIER: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];

let check_qual_follow = |this: &mut Self, dist| {
this.look_ahead(dist, |t| {
// ...qualified and then `fn`, e.g. `const fn`.
t.is_keyword(kw::Fn)
// Two qualifiers. This is enough.
|| QUALIFIER.iter().any(|&kw| t.is_keyword(kw))
})
};
const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
self.check_keyword(kw::Fn) // Definitely an `fn`.
// `$qual fn` or `$qual $qual`:
|| QUALIFIER.iter().any(|&kw| self.check_keyword(kw)) && check_qual_follow(self, 1)
// `extern ABI fn` or `extern ABI $qual`; skip 1 for the ABI.
|| self.check_keyword(kw::Extern) && check_qual_follow(self, 2)
|| QUALS.iter().any(|&kw| self.check_keyword(kw))
&& self.look_ahead(1, |t| {
// ...qualified and then `fn`, e.g. `const fn`.
t.is_keyword(kw::Fn)
// Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
|| t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
})
// `extern ABI fn`
|| self.check_keyword(kw::Extern)
&& self.look_ahead(1, |t| t.can_begin_literal_or_bool())
&& self.look_ahead(2, |t| t.is_keyword(kw::Fn))
}

/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
Expand Down
8 changes: 5 additions & 3 deletions src/libsyntax/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,14 @@ impl Token {

/// Returns `true` if the token is any literal, a minus (which can prefix a literal,
/// for example a '-42', or one of the boolean idents).
///
/// Keep this in sync with `Lit::from_token`.
pub fn can_begin_literal_or_bool(&self) -> bool {
match self.kind {
Literal(..) | BinOp(Minus) => true,
Ident(name, false) if name.is_bool_lit() => true,
Interpolated(ref nt) => match **nt {
NtLiteral(..) => true,
Interpolated(ref nt) => match &**nt {
NtExpr(e) | NtLiteral(e) => matches!(e.kind, ast::ExprKind::Lit(_)),
_ => false,
},
_ => false,
Expand Down Expand Up @@ -530,7 +532,7 @@ impl Token {
}

/// Returns `true` if the token is a non-raw identifier for which `pred` holds.
fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
match self.ident() {
Some((id, false)) => pred(id),
_ => false,
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/util/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ impl Lit {
}

/// Converts arbitrary token into an AST literal.
///
/// Keep this in sync with `Token::can_begin_literal_or_bool`.
pub fn from_token(token: &Token) -> Result<Lit, LitError> {
let lit = match token.kind {
token::Ident(name, false) if name.is_bool_lit() => {
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/no-async-const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
// compile-flags: --crate-type lib

pub async const fn x() {}
//~^ ERROR expected one of `fn` or `unsafe`, found keyword `const`
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
4 changes: 2 additions & 2 deletions src/test/ui/async-await/no-async-const.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: expected one of `fn` or `unsafe`, found keyword `const`
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 `fn` or `unsafe`
| ^^^^^ expected one of `extern`, `fn`, or `unsafe`

error: aborting due to previous error

3 changes: 1 addition & 2 deletions src/test/ui/async-await/no-const-async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
// compile-flags: --crate-type lib

pub const async fn x() {}
//~^ ERROR expected identifier, found keyword `async`
//~^^ expected `:`, found keyword `fn`
//~^ ERROR functions cannot be both `const` and `async`
17 changes: 7 additions & 10 deletions src/test/ui/async-await/no-const-async.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
error: expected identifier, found keyword `async`
--> $DIR/no-const-async.rs:4:11
error: functions cannot be both `const` and `async`
--> $DIR/no-const-async.rs:4:1
|
LL | pub const async fn x() {}
| ^^^^^ expected identifier, found keyword
| ^^^^-----^-----^^^^^^^^^^
| | |
| | `async` because of this
| `const` because of this

error: expected `:`, found keyword `fn`
--> $DIR/no-const-async.rs:4:17
|
LL | pub const async fn x() {}
| ^^ expected `:`

error: aborting due to 2 previous errors
error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/async-await/no-unsafe-async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ impl S {
}

#[cfg(FALSE)]
unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found keyword `async`
unsafe async fn f() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async`
4 changes: 2 additions & 2 deletions src/test/ui/async-await/no-unsafe-async.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ error: expected one of `extern` or `fn`, found keyword `async`
LL | unsafe async fn g() {}
| ^^^^^ expected one of `extern` or `fn`

error: expected one of `extern`, `fn`, or `{`, found keyword `async`
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`, `fn`, or `{`
| ^^^^^ expected one of `extern` or `fn`

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// Check that `const extern fn` and `const unsafe extern fn` are feature-gated.

#[cfg(FALSE)] const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
#[cfg(FALSE)] const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
#[cfg(FALSE)] const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
#[cfg(FALSE)] const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
#[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
//~^ ERROR `const extern fn` definitions are unstable
#[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
//~^ ERROR `const extern fn` definitions are unstable
const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
const unsafe extern "C" fn bar2() {} //~ ERROR `const extern fn` definitions are unstable
const unsafe extern "Rust" fn bar3() {} //~ ERROR `const extern fn` definitions are unstable

fn main() {}
Loading
0