#is there a clean way to go from Literal to Ident in proc_macro2?
1 messages · Page 1 of 1 (latest)
I tried going from TokenTree::Literal to a TokenStream in hopes that parse_macro_input(my_token_stream as Ident) would solve my issue but I am getting conflicting errors
let ss = tti.next().expect("Expect <field identifier>");
let ts: TokenStream2 = ss.to_token_stream();
let ts: TokenStream = proc_macro::TokenStream::from(ts);
let ident = parse_macro_input!(ts as Ident);
ss is a TokenTree::Literal
As you can see I make sure to pass proc_macro::TokenStream and not from proc_macro2 but if I do that the compiler says expected proc_macro2::TokenStream. if I pass in that, the macro wants proc_macro::TokenStream
I don't understand how can both types fail here
// TokenTree::Literal(Literal { .. })
let ss : TokenTree = tti.next().expect("Expect <field identifier>");
let ts2: TokenStream2 = ss.to_token_stream();
let ts: TokenStream = proc_macro::TokenStream::from(ts2);
// Doesn't work with either ts2 or ts
let ident = parse_macro_input!(ts as Ident);
you're being bamboozled by the magic of parse_macro_input!
The real rule of thumb is to have:
use ::syn::Result;
// Input uses `TokenStream2` types.
fn function_impl(…) -> Result<TokenStream2> {
… /* use `?` for Fun And Profit™ */
}
// Input uses `TokenStream` types.
#[proc_macro…] pub
fn function(…) -> TokenStream {
function_impl(….into() …)
.unwrap_or_else(::syn::Error::into_compile_error)
.into()
}
No parse_macro_input! magic, just straight honest code
(parse_macro_input! is designed to be used exactly within a #[proc_macro…]-annotated fn, since it expects the return type to be a ::proc_macro::TokenStream)
And then, the no-shenanigans equivalent of parse_macro_input!, that is, the function which returns a Result when trying to parse something, is ::syn::parse
(but you'll often rather use ::syn::parse2 at this point)
So, with all those remarks being said, the OP question is "just" (assuming you've parsed a ::syn::LitStr rather than a too-generic Literal):
let my_str_literal: LitStr = …;
let my_ident: Ident = my_str_literal.parse()?;
(https://docs.rs/syn/1.0.101/syn/struct.LitStr.html#method.parse)
Finally, you may want to consider directly receiving an ident rather than a string literal
I am chewing through all the information you just laid and it makes a bit of sense. How would you directly receive an ident though? I currently have a Punctuated<NestedMeta, Comma> at hand. Any examples I could look at cz something tells me it's way too common to expect a proc macro to make an Ident out of a tokentree.
Apparently I am unable to use it inside a closure thats inside a #[proc_macro] annotated fn
yeah, basically parse_macro_input! does kind of generate return ::proc_macro::TokenStream… and there is nothing you can do about it (modulo using yet another closure level to "catch" that return. That's why I prefer ::syn::parse{,2}(…), it yields a Result and you handle it
Yeah, Meta is another "anti-pattern", imho, in syn
It comes from the old way attributes were shaped, which was quite restrictive and required string literals
Which is why, for instance, serde does that too
If you've already written the code using Meta, then don't bother changing that nit (since it would require more effort than it may be worth), and use that .parse() on the LitStr
What I personally use for attributes is just another layer of a custom Parse impl
// if you can write a proc-macro able to handle
stuff!(foo, bar(helper)); // <- you wouldn't parse this input as `Meta`
// then you can use that same logic to have an attribute able to handle
#[stuff(foo, bar(helper))]
So you're preferring working with a TokenStream rather than "structured" Meta? How would you go about this because what I'd do is when syn would give a Punctuated and I usually do it like this
// p is some Punctuated<T,P>
let nv = p.first().to_owned().expect("Attribute parse error");
let tts = nv.into_token_stream();
let tt1 = tts.next()?;
// iter through each tt$ and do something with it
So, the easiest thing here is that you provide the kind of input you'd like to parse 🙂
(since said Parse impl would depend on it)
Currently I am going through dtolnay's workshop and exercise 7 wants you to parse this :
#[builder(each = "arg")]
Ah, their workshop may be designed with Meta in mind, so it will probably be easier to stick with it
Well, either ways, I'd like to not restrict myself to using Metas cuz I find myself writing too many if lets
I'd prefer the token_stream.next() way
So, usually you'd have an attr: &Attribute referring to builder(each = "arg"), if using syn
Then, you can checker it starts with builder by checking that attr.is_ident("builder") holds