Skip to content

Commit

Permalink
Parse .await inside of macro invocation
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Aug 7, 2019
1 parent 67ac550 commit 5198fe4
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 2 deletions.
19 changes: 17 additions & 2 deletions futures-async-stream-macro/src/visitor.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use quote::{quote, quote_spanned};
use quote::{quote, quote_spanned, ToTokens};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
visit_mut::{self, VisitMut},
Expr, ExprCall, ExprField, ExprForLoop, ExprYield, Item, Member,
Expr, ExprCall, ExprField, ExprForLoop, ExprYield, Item, Member, Token,
};

use crate::{
Expand Down Expand Up @@ -131,6 +133,14 @@ impl Visitor {

/// Visits `async_stream_block!` macro.
fn visit_macro(&mut self, expr: &mut Expr) {
struct Exprs(Punctuated<Expr, Token![,]>);

impl Parse for Exprs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
input.parse_terminated(Expr::parse).map(Self)
}
}

replace_expr(expr, |expr| {
if let Expr::Macro(mut expr) = expr {
if expr.mac.path.is_ident("async_stream_block") {
Expand All @@ -139,6 +149,11 @@ impl Visitor {
e.attrs.append(&mut expr.attrs);
Expr::Call(e)
} else {
// We can't tell in general whether `.await` inside a macro invocation.
if let Ok(mut exprs) = syn::parse2::<Exprs>(expr.mac.tts.clone()) {
exprs.0.iter_mut().for_each(|expr| self.visit_expr_mut(expr));
expr.mac.tts = exprs.0.into_token_stream();
}
Expr::Macro(expr)
}
} else {
Expand Down
12 changes: 12 additions & 0 deletions tests/async_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ pub async fn array() {
yield [1, 2, 3, 4];
}

macro_rules! foo {
($($tt:tt)*) => {
$($tt)*
};
}

#[async_stream(item = i32)]
pub async fn inside_of_macro_invocation() {
foo!(yield 0);
foo!(yield foo!(async { 1 }.await));
}

pub struct A(i32);

impl A {
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/macro-invocation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// compile-fail

#![deny(warnings)]
#![feature(async_await, generators)]

use futures_async_stream::async_stream;

macro_rules! foo {
($($tt:tt)*) => {
$($tt)*
};
}

#[async_stream(item = i32)]
pub async fn inside_of_macro_invocation() {
foo!(yield 0);
foo!(yield foo!(async { 1 }.await,));
}

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/macro-invocation.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error: macro expansion ignores token `,` and any following
--> $DIR/macro-invocation.rs:17:38
|
17 | foo!(yield foo!(async { 1 }.await,));
| ----------------------^-- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
|
= note: the usage of `foo!` is likely invalid in expression context

error: aborting due to previous error

0 comments on commit 5198fe4

Please sign in to comment.