How to use syn to parse punctuated types?

I'm looking for methods to parse args of a proc_macro_attribute (possibly by the crate syn), which is a list of punctuated types?

#[my_macro(i32, S<U>, [f64; 5])]
fn f() {}

The definition of proc_macro_attribute is like:

#[proc_macro_attribute]
pub fn my_macro(args: TokenStream, func: TokenStream) -> TokenStream {}

In the above example, I want to parse i32, S<U>, [f64; 5] from args: TokenStream, are there any convenient ways to do so?

IIRC when you slap a new attr macro on top of the attr macros already present on an item, that new attr macro get access to the entire source of the item, including the already-present attr macros.
But you'll have to parse the types yourself from the token stream.

You're probably looking for Punctuated::parse_terminated (or some of the function near it).

1 Like

I tried to use the following function:

struct Types(Vec<Ident>);

impl Parse for Types {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let result = Punctuated::<Ident, Token![,]>::parse_terminated(input);
        todo!()
    }
}

#[proc_macro_attribute]
pub fn my_macro(args: TokenStream, func: TokenStream) -> TokenStream {
    let types = parse_macro_input!(args as Types);
    todo!()
}

But it cannot handle the < symbol

Hi,

I‘m using


#[proc_macro_attribute]
#[allow(non_snake_case)]
pub fn MyMacro(attr: TokenStream, item: TokenStream) -> TokenStream {

let args: AttributeArgs = parse_macro_input!(attr as AttributeArgs);
let arg = args.get(0);

To access the proc macro attributes in my code. This requires further parsing/pattern matching on the argument but I guess you‘ll be quite close already.

- let result = Punctuated::<Ident, Token![,]>::parse_terminated(input);
+ let result = Punctuated::<Type, Token![,]>::parse_terminated(input);
  • EDIT: this refers to ::syn::Type, not ::syn::token::Type (more generally, the items in the ::syn::token::… module are very rarely supposed to be used directly; they're rather implicitly accessed through the Token![…] macro, such as Token![type]).

Btw, there is a way to write the whole thing a bit more succintly:

#[proc_macro_attribute] pub
fn my_macro (args: TokenStream, func: TokenStream)
  -> TokenStream
{
    let types: Vec<Type> =
        parse_macro_input!(args with Punctuated::<Type, Token![,]>::parse_terminated)
            .into_iter()
            .collect()
    ;
    // …
}

This is not the type you're passing, since neither S<U> nor [f64; 5] are idents. It seems that you're trying to pass the punctuated list of types, so the item to be parsed should be Punctuated::<Type, Token![,]>.

1 Like

I tried the following code:

#[proc_macro_attribute]
pub fn my_macro(args: TokenStream, func: TokenStream) -> TokenStream {
    let _types: Vec<Type> =
        parse_macro_input!(args with Punctuated::<Type, Token![,]>::parse_terminated)
            .into_iter()
            .collect();
    func
}
#[my_macro(i32, f64, String)]
fn f1() {}

but still got the error:

error: expected `type`
 --> tests/test.rs:9:12
  |
9 | #[my_macro(i32, f64, String)]
  |            ^^^

I checked the definition of syn::token::Type, it seems that Type is only for parsing the keyword type.

In the post above, I've left a link - you want syn::Type (which is for types), not syn::token::Type (which, as the module name suggests, is indeed for token type only).

3 Likes