Skip to content

syn::meta::parser should not fail on empty meta list #1460

@nicopap

Description

@nicopap

My #[proc_macro_attribute] optionally accepts config flags as a meta list. So I used syn's syn::meta::parser to read the attributes.

However, it fails when the meta list is empty, with no way of controlling this behavior.

Say I have a Config struct as follow:

#[derive(Default)]
pub(crate) struct Config {
    zoobi_whoop: Option<Ident>,
    badhi_wabbi: Option<Ident>,
    bopple_beep: Option<syn::Visibility>,
}

I have a parse method on Config as follow, similar to the one in the doc example:

        match () {
            () if meta.path.is_ident("zoobi_whoop") => {
                let value = meta.value()?;
                self.zoobi_whoop = Some(value.parse()?);
            }
            // ... parse badhi_wabbi and bopple_beep
            () => {
                return Err(meta.error("Unrecognized do_woobah meta attribute"));
            }
        }
        Ok(())

I will use syn::meta::parser as follow:

#[proc_macro_attribute]
pub fn do_woobah(attrs: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let mut config = Config::default();
    let config_parser = syn::meta::parser(|meta| config.parse(meta));
    parse_macro_input!(attrs with config_parser);
    // Use `config` here
}

I can call do_woobah as follow:

#[do_woobah(zoobi_whoop = beep_bop)]
fn wobble_function() {}

However, usually, do_woobah doesn't have a meta list, the default config values are enough most of the time.

#[do_woobah]
fn wobble_function() {}

This fails to compile when using syn::meta::parser. To me, it doesn't make sense that this should fail. I don't see the interest in failing on an empty meta list.

Say you have mandatory config options. Regardless of whether the options are mandatory or not, you still need to check afterward that all mandatory config options are set, and ideally tell the user what to options are missing, so this behavior is unwishable in this case as well.

Workaround

Thankfully, TokenStream exposes a is_empty() method, and I can use it to avoid running syn::meta::parse when attrs is empty:

pub fn do_woobah(attrs: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let mut config = Config::default();

    if !attrs.is_empty() {
        let config_parser = syn::meta::parser(|meta| config.parse(meta));
        parse_macro_input!(attrs with config_parser);
    }
    // Use `config` here
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions