Attributes containing keywords can't be parsed with 0.20
The code below worked in 0.14.2. It uses type inside a derive macro attribute.
As of 0.20 this yields:
Error { kind: Custom("expected identifier, found keyword `type`"), locations: ["bar"], span: Some(Span) }
use darling::{ast, FromDeriveInput, FromField, FromMeta};
use quote::quote;
use syn::{parse_quote, DeriveInput};
#[derive(Clone, Debug, FromMeta)]
#[darling(rename_all = "snake_case")]
enum Type {
A,
B,
}
#[derive(Clone, Debug, FromDeriveInput)]
#[darling(attributes(myderive), supports(struct_named))]
struct MyDeriveReceiver {
data: ast::Data<(), MyDeriveFieldReceiver>,
}
#[derive(Clone, Debug, FromField)]
#[darling(attributes(myderive))]
struct MyDeriveFieldReceiver {
#[darling(rename = "type")]
my_type: Option<Type>,
}
fn main() {
let input = parse_quote! {
#[derive(MyDerive)]
struct Foo {
#[myderive(type = "a")]
bar: (),
}
};
let receiver = MyDeriveReceiver::from_derive_input(&input).unwrap();
}
Attempting to repro this, I discovered something interesting. This errors:
let _attr: syn::Attribute = syn::parse_quote!(#[type = false]);
But this doesn't:
let _attr: syn::Attribute = syn::parse_quote!(#[hello(type = false)]);
It seems like it may matter to syn whether the keyword is the outermost item, or is inside a Meta wrapper.
Alternatively, the issue is in NestedMeta, which does some custom parsing. That seems to be looking for an identifier, so I it's possible that needs to allow for keywords instead. @jonasbb is that something you'd be able to take a look at?
I've filed dtolnay/syn#1458 to request that syn handle this scenario without errors.
I can take another look next week, but it looks a bit like https://github.com/dtolnay/syn/issues/1414
Basically, type = false is not a valid Meta syntax in Rust and syn. I guess the DeriveInput gets recursively parsed into a Meta or NestedMeta somewhere. No chance of getting any support from syn, because of its maintainer.
This is how I worked around that for serde_with: jonasbb/serde_with@c22f165 (#578), replace as with r#as such that it is valid Meta.
As noted in #1414 it seems that keyword identifiers are fine as long as they're inside the List portion; is there a way that NestedMeta could reimplement Meta parsing to preserve that behavior?
NestedMeta works.
let nm: darling::ast::NestedMeta = syn::parse_quote!(myderive(type = "a"));
You just cannot expect that the inside is a well-formed meta again. Probably from_derive_input assumes that (recursive parsing). darling has no from_tokenstream for directly parsing. syn has no type supporting keywords.
Dropping Meta and doing something separate from syn would work.
It's possible to hand-construct a syn::Meta that has an Ident in its path field which is a keyword, right? The issue is that attempting to construct it using Parse fails?
If so, then would a darling-local copy of the Meta parsing code which used a different path parsing function work?
It's possible to hand-construct a
syn::Metathat has anIdentin itspathfield which is a keyword, right?
I am not sure if that is true, but yes if it is, then copying and adapting the Meta parsing code could work. I would be a bit wary about it. dtolnay likes breaking code that works and I feel that using Meta in such a form is something that could break at any point.
Instead of using Meta it might work to rely more on syn::parse::Parse to directly process the TokenStream that is embedded in a MetaList.
Is there a current hack to get around this issue?