2
votes

I am experimenting with Rust procedural macros.

I would like to be able to create a macro that would be used to generate JNI invocation boilerplate. Something like

jni_method!{com.purplefrog.rust_callable.Widget, fn echo_str(&str)->String}

I have the following code so far (playground):

#[macro_use]
extern crate syn; // 1.0.33

use syn::parse::{Parse, ParseStream};
use syn::Signature;

struct Arguments {
    name: proc_macro2::Ident,
    signature: Signature,
}

impl Parse for Arguments {
    fn parse(tokens: ParseStream) -> Result<Arguments, syn::Error> {
        let name: proc_macro2::Ident = tokens.parse()?;
        let comma: Token![,] = tokens.parse()?;
        let signature: Signature = //tokens.parse()?;
            syn::item::parsing::parse_signature(tokens)?;

        Ok(Arguments {
            name: name,
            signature,
        })
    }
}

Unfortunately, the parse_signature call is in error:

error[E0603]: module `item` is private
   --> src/lib.rs:17:18
    |
17  |             syn::item::parsing::parse_signature(tokens)?;
    |                  ^^^^ private module
    |
note: the module `item` is defined here
   --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.33/src/lib.rs:363:1
    |
363 | mod item;
    | ^^^^^^^^^

What is the proper way to parse a Signature from a ParseStream?

2

2 Answers

1
votes

Why do you need a Signature? Depending on what you are actually trying to parse, you should use one of the following:

  • Fn* trait signature (ex. FnMut(usize) -> bool)

    Parse into syn::TraitBound (in order to catch lifetime bounds not present in a just a path), then you can get the inputs/output from the parenthesized arguments of the last segment of the trait bound's path.

  • Bare function, aka function pointer (ex. fn(usize) -> bool)

    Parse into syn::TypeBareFn, then you can get the inputs/output directly.

  • Function definition, including a body (ex. fn foo(x: usize) -> bool { x > 5 })

    Parse into syn::ItemFn, which includes a signature.

  • Foreign function definition (ex. fn foo(x: usize) -> bool)

    Parse into Struct syn::ForeignItemFn, which includes a signature. Note that this is meant for declarations in extern blocks, so chances are this is not actually what you are looking for.

0
votes

I eventually found an example of how to work around this problem ( https://github.com/dtolnay/syn/blob/master/examples/lazy-static/lazy-static/src/lib.rs ). You are supposed to create your own struct and impl Parse for it. I was able to build my own syntax from Parseable elements.

struct MySignature {
    pub parameter_types: Vec<Type>,
}

impl Parse for MySignature {
    fn parse(tokens: ParseStream) -> Result<Self, syn::Error> {
        let mut parameter_types: Vec<Type> = Vec::new();

        let arg_types: ParseBuffer;
        parenthesized!(arg_types in tokens);

        while !arg_types.is_empty() {
            let arg_type: Type = arg_types.parse()?;
            parameter_types.push(arg_type);

            if !arg_types.is_empty() {
                let _comma: Token![,] = arg_types.parse()?;
            }
        }

        Ok(MySignature { parameter_types })
    }
}