Skip to content

Commit

Permalink
Deny async fn in 2015 edition
Browse files Browse the repository at this point in the history
Fix style issues and update diagnostic messages

Update src/librustc_passes/diagnostics.rs

Co-Authored-By: doctorn <me@nathancorbyn.com>

Deny nested `async fn` in Rust 2015 edition

Deny nested `async fn` in Rust 2015 edition

Deny nested `async fn` in Rust 2015 edition
  • Loading branch information
doctorn committed Feb 24, 2019
1 parent 7f19f16 commit 8300f51
Show file tree
Hide file tree
Showing 22 changed files with 215 additions and 62 deletions.
10 changes: 5 additions & 5 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2906,7 +2906,7 @@ impl<'a> LoweringContext<'a> {
// `impl Future<Output = T>` here because lower_body
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
let body_id = this.lower_async_body(decl, header.asyncness, body);
let body_id = this.lower_async_body(decl, header.asyncness.node, body);

let (generics, fn_decl) = this.add_in_band_defs(
generics,
Expand All @@ -2916,7 +2916,7 @@ impl<'a> LoweringContext<'a> {
decl,
Some((fn_def_id, idty)),
true,
header.asyncness.opt_return_id()
header.asyncness.node.opt_return_id()
),
);

Expand Down Expand Up @@ -3410,14 +3410,14 @@ impl<'a> LoweringContext<'a> {
)
}
ImplItemKind::Method(ref sig, ref body) => {
let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness, body);
let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness.node, body);
let impl_trait_return_allow = !self.is_in_trait_impl;
let (generics, sig) = self.lower_method_sig(
&i.generics,
sig,
impl_item_def_id,
impl_trait_return_allow,
sig.header.asyncness.opt_return_id(),
sig.header.asyncness.node.opt_return_id(),
);
(generics, hir::ImplItemKind::Method(sig, body_id))
}
Expand Down Expand Up @@ -3637,7 +3637,7 @@ impl<'a> LoweringContext<'a> {
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
hir::FnHeader {
unsafety: self.lower_unsafety(h.unsafety),
asyncness: self.lower_asyncness(h.asyncness),
asyncness: self.lower_asyncness(h.asyncness.node),
constness: self.lower_constness(h.constness),
abi: h.abi,
}
Expand Down
10 changes: 5 additions & 5 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl<'a> DefCollector<'a> {
decl: &'a FnDecl,
body: &'a Block,
) {
let (closure_id, return_impl_trait_id) = match header.asyncness {
let (closure_id, return_impl_trait_id) = match header.asyncness.node {
IsAsync::Async {
closure_id,
return_impl_trait_id,
Expand Down Expand Up @@ -129,10 +129,10 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
}
ItemKind::Fn(
ref decl,
ref header @ FnHeader { asyncness: IsAsync::Async { .. }, .. },
ref header,
ref generics,
ref body,
) => {
) if header.asyncness.node.is_async() => {
return self.visit_async_fn(
i.id,
i.ident.name,
Expand Down Expand Up @@ -242,9 +242,9 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
fn visit_impl_item(&mut self, ii: &'a ImplItem) {
let def_data = match ii.node {
ImplItemKind::Method(MethodSig {
header: ref header @ FnHeader { asyncness: IsAsync::Async { .. }, .. },
ref header,
ref decl,
}, ref body) => {
}, ref body) if header.asyncness.node.is_async() => {
return self.visit_async_fn(
ii.id,
ii.ident.name,
Expand Down
14 changes: 11 additions & 3 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.invalid_visibility(&impl_item.vis, None);
if let ImplItemKind::Method(ref sig, _) = impl_item.node {
self.check_trait_fn_not_const(sig.header.constness);
self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness);
self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node);
}
}
}
Expand All @@ -482,9 +482,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.note("only trait implementations may be annotated with default").emit();
}
}
ItemKind::Fn(_, header, ref generics, _) => {
ItemKind::Fn(_, ref header, ref generics, _) => {
// We currently do not permit const generics in `const fn`, as
// this is tantamount to allowing compile-time dependent typing.
self.visit_fn_header(header);
if header.constness.node == Constness::Const {
// Look for const generics and error if we find any.
for param in &generics.params {
Expand Down Expand Up @@ -535,7 +536,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.no_questions_in_bounds(bounds, "supertraits", true);
for trait_item in trait_items {
if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness);
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness.node);
self.check_trait_fn_not_const(sig.header.constness);
if block.is_none() {
self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
Expand Down Expand Up @@ -702,6 +703,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.span_bug(mac.span, "macro invocation missed in expansion; did you forget to override \
the relevant `fold_*()` method in `PlaceholderExpander`?");
}

fn visit_fn_header(&mut self, header: &'a FnHeader) {
if header.asyncness.node.is_async() && self.session.rust_2015() {
struct_span_err!(self.session, header.asyncness.span, E0670,
"`async fn` is not permitted in the 2015 edition").emit();
}
}
}

pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
Expand Down
12 changes: 12 additions & 0 deletions src/librustc_passes/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,18 @@ loop {
break;
}
```
"##,

E0670: r##"
Rust 2015 does not permit the use of `async fn`.
Example of erroneous code:
```compile_fail,E0670
async fn foo() {}
```
Switch to the Rust 2018 edition to use `async fn`.
"##
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,9 +806,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
debug!("(resolving function) entering function");
let (rib_kind, asyncness) = match function_kind {
FnKind::ItemFn(_, ref header, ..) =>
(ItemRibKind, header.asyncness),
(ItemRibKind, header.asyncness.node),
FnKind::Method(_, ref sig, _, _) =>
(TraitOrImplItemRibKind, sig.header.asyncness),
(TraitOrImplItemRibKind, sig.header.asyncness.node),
FnKind::Closure(_) =>
// Async closures aren't resolved through `visit_fn`-- they're
// processed separately
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_save_analysis/sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ impl Sig for ast::Item {
if header.constness.node == ast::Constness::Const {
text.push_str("const ");
}
if header.asyncness.is_async() {
if header.asyncness.node.is_async() {
text.push_str("async ");
}
if header.unsafety == ast::Unsafety::Unsafe {
Expand Down Expand Up @@ -936,7 +936,7 @@ fn make_method_signature(
if m.header.constness.node == ast::Constness::Const {
text.push_str("const ");
}
if m.header.asyncness.is_async() {
if m.header.asyncness.node.is_async() {
text.push_str("async ");
}
if m.header.unsafety == ast::Unsafety::Unsafe {
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2216,7 +2216,7 @@ impl Item {
#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
pub struct FnHeader {
pub unsafety: Unsafety,
pub asyncness: IsAsync,
pub asyncness: Spanned<IsAsync>,
pub constness: Spanned<Constness>,
pub abi: Abi,
}
Expand All @@ -2225,7 +2225,7 @@ impl Default for FnHeader {
fn default() -> FnHeader {
FnHeader {
unsafety: Unsafety::Normal,
asyncness: IsAsync::NotAsync,
asyncness: dummy_spanned(IsAsync::NotAsync),
constness: dummy_spanned(Constness::NotConst),
abi: Abi::Rust,
}
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
ast::ItemKind::Fn(self.fn_decl(inputs, ast::FunctionRetTy::Ty(output)),
ast::FnHeader {
unsafety: ast::Unsafety::Normal,
asyncness: ast::IsAsync::NotAsync,
asyncness: dummy_spanned(ast::IsAsync::NotAsync),
constness: dummy_spanned(ast::Constness::NotConst),
abi: Abi::Rust,
},
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1894,7 +1894,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match fn_kind {
FnKind::ItemFn(_, header, _, _) => {
// Check for const fn and async fn declarations.
if header.asyncness.is_async() {
if header.asyncness.node.is_async() {
gate_feature_post!(&self, async_await, span, "async fn is unstable");
}
// Stability of const fn methods are covered in
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ pub fn noop_flat_map_impl_item<T: MutVisitor>(mut item: ImplItem, visitor: &mut

pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
let FnHeader { unsafety: _, asyncness, constness: _, abi: _ } = header;
vis.visit_asyncness(asyncness);
vis.visit_asyncness(&mut asyncness.node);
}

pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
Expand Down
39 changes: 28 additions & 11 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5001,6 +5001,11 @@ impl<'a> Parser<'a> {
)
}

fn is_async_fn(&mut self) -> bool {
self.token.is_keyword(keywords::Async) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Fn))
}

fn is_do_catch_block(&mut self) -> bool {
self.token.is_keyword(keywords::Do) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Catch)) &&
Expand Down Expand Up @@ -5133,7 +5138,8 @@ impl<'a> Parser<'a> {
!self.is_union_item() &&
!self.is_crate_vis() &&
!self.is_existential_type_decl() &&
!self.is_auto_trait_item() {
!self.is_auto_trait_item() &&
!self.is_async_fn() {
let pth = self.parse_path(PathStyle::Expr)?;

if !self.eat(&token::Not) {
Expand Down Expand Up @@ -6346,7 +6352,7 @@ impl<'a> Parser<'a> {
/// Parses an item-position function declaration.
fn parse_item_fn(&mut self,
unsafety: Unsafety,
asyncness: IsAsync,
asyncness: Spanned<IsAsync>,
constness: Spanned<Constness>,
abi: Abi)
-> PResult<'a, ItemInfo> {
Expand Down Expand Up @@ -6378,14 +6384,15 @@ impl<'a> Parser<'a> {
-> PResult<'a, (
Spanned<Constness>,
Unsafety,
IsAsync,
Spanned<IsAsync>,
Abi
)>
{
let is_const_fn = self.eat_keyword(keywords::Const);
let const_span = self.prev_span;
let unsafety = self.parse_unsafety();
let asyncness = self.parse_asyncness();
let asyncness = respan(self.prev_span, asyncness);
let (constness, unsafety, abi) = if is_const_fn {
(respan(const_span, Constness::Const), unsafety, Abi::Rust)
} else {
Expand Down Expand Up @@ -7796,7 +7803,7 @@ impl<'a> Parser<'a> {
let abi = opt_abi.unwrap_or(Abi::C);
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -7840,7 +7847,7 @@ impl<'a> Parser<'a> {
self.bump();
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::NotAsync,
respan(const_span, IsAsync::NotAsync),
respan(const_span, Constness::Const),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -7888,14 +7895,15 @@ impl<'a> Parser<'a> {
// ASYNC FUNCTION ITEM
let unsafety = self.parse_unsafety();
self.expect_keyword(keywords::Async)?;
let async_span = self.prev_span;
self.expect_keyword(keywords::Fn)?;
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::Async {
respan(async_span, IsAsync::Async {
closure_id: ast::DUMMY_NODE_ID,
return_impl_trait_id: ast::DUMMY_NODE_ID,
},
}),
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand All @@ -7904,6 +7912,13 @@ impl<'a> Parser<'a> {
item_,
visibility,
maybe_append(attrs, extra_attrs));
if self.span.rust_2015() {
self.diagnostic().struct_span_err_with_code(
async_span,
"`async fn` is not permitted in the 2015 edition",
DiagnosticId::Error("E0670".into())
).emit();
}
return Ok(Some(item));
}
if self.check_keyword(keywords::Unsafe) &&
Expand Down Expand Up @@ -7951,7 +7966,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand All @@ -7977,7 +7992,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Unsafe,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -8244,7 +8259,8 @@ impl<'a> Parser<'a> {
lo: Span,
visibility: Visibility
) -> PResult<'a, Option<P<Item>>> {
if macros_allowed && self.token.is_path_start() {
if macros_allowed && self.token.is_path_start() &&
!(self.is_async_fn() && self.span.rust_2015()) {
// MACRO INVOCATION ITEM

let prev_span = self.prev_span;
Expand Down Expand Up @@ -8299,7 +8315,8 @@ impl<'a> Parser<'a> {
fn parse_assoc_macro_invoc(&mut self, item_kind: &str, vis: Option<&Visibility>,
at_end: &mut bool) -> PResult<'a, Option<Mac>>
{
if self.token.is_path_start() {
if self.token.is_path_start() &&
!(self.is_async_fn() && self.span.rust_2015()) {
let prev_span = self.prev_span;
let lo = self.span;
let pth = self.parse_path(PathStyle::Mod)?;
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3195,7 +3195,7 @@ impl<'a> State<'a> {
ast::Constness::Const => self.word_nbsp("const")?
}

self.print_asyncness(header.asyncness)?;
self.print_asyncness(header.asyncness.node)?;
self.print_unsafety(header.unsafety)?;

if header.abi != Abi::Rust {
Expand Down Expand Up @@ -3247,7 +3247,7 @@ mod tests {
ast::FnHeader {
unsafety: ast::Unsafety::Normal,
constness: source_map::dummy_spanned(ast::Constness::NotConst),
asyncness: ast::IsAsync::NotAsync,
asyncness: source_map::dummy_spanned(ast::IsAsync::NotAsync),
abi: Abi::Rust,
},
abba_ident,
Expand Down
Loading

0 comments on commit 8300f51

Please sign in to comment.