Hello I'm creating a proc macro that I can use in my declarative macros that checks if 2 types are the same, and if so, it will select the first block, otherwise it should select the second block. However I'm getting this error
error: macro expansion ignores token `{` and any following
--> src\event_listener\macros.rs:25:13
|
25 | / funny_if!{
26 | | (),
27 | | (),
28 | | {
... |
33 | | }
34 | | }
| | ^ caused by the macro expansion here
| |_____________|
|
|
= note: the usage of `funny_if!` is likely invalid in item context
which means I'm doing some thing wrong, and I'm not sure at all what that is. If anyone could help that'd be great.
struct FunnyIf {
type_to_match: Type,
input_type: Type,
true_block: Block,
false_block: Block,
}
impl Parse for FunnyIf {
fn parse(input: ParseStream) -> Result<Self> {
let type_to_match: Type = input.parse()?;
input.parse::<Token![,]>()?;
let input_type: Type = input.parse()?;
input.parse::<Token![,]>()?;
let true_block: Block = input.parse()?;
input.parse::<Token![,]>()?;
let false_block: Block = input.parse()?;
Ok(FunnyIf {
type_to_match,
input_type,
true_block,
false_block,
})
}
}
/// Creates a compile time if statement on types
#[proc_macro]
pub fn funny_if(input: TokenStream) -> TokenStream {
let FunnyIf {
type_to_match,
input_type,
true_block,
false_block,
} = parse_macro_input!(input as FunnyIf);
let used_block = if type_to_match.to_token_stream().to_string()
== input_type.to_token_stream().to_string()
{
true_block
} else {
false_block
};
let expanded = quote! {{
#used_block
}};
expanded.into()
}
An example of this macro's usage if it's correctly implemented
macro_rules! example {
($typ:ty) => {
funny_if! {
(),
$typ,
{ println!("type was ()"); },
{ println!("type wasn't ()"); }
}
}
}
fn main() {
example!(()); // should print "type was ()"
example!(u8); // should print "type wasn't ()"
}
I figured it out with the help of people on the rust community discord server! The code below is my latest iteration. The problem in my original code was that I was just returning the block, and putting it into the root scope, however that usage is invalid, I solved it by taking each statement from the block, and writing it to a TokenStream
which I then returned from the function. I also created 2 other similar macros for types, and expressions if anyone is interested.
struct If<T: Parse> {
type_to_match: Type,
input_type: Type,
true_branch: T,
false_branch: T,
}
impl<T: Parse> Parse for If<T> {
fn parse(input: ParseStream) -> Result<Self> {
let type_to_match: Type = input.parse()?;
input.parse::<Token![,]>()?;
let input_type: Type = input.parse()?;
input.parse::<Token![,]>()?;
let true_branch: T = input.parse()?;
input.parse::<Token![,]>()?;
let false_branch: T = input.parse()?;
Ok(If {
type_to_match,
input_type,
true_branch,
false_branch,
})
}
}
/// Creates a compile time if statement
/// that takes checks if 2 types are the same
/// and if returns one of the branches
#[proc_macro]
pub fn block_if(input: TokenStream) -> TokenStream {
let If {
type_to_match,
input_type,
true_branch,
false_branch,
} = parse_macro_input!(input as If<Block>);
let used_branch = if type_to_match.to_token_stream().to_string()
== input_type.to_token_stream().to_string()
{
true_branch
} else {
false_branch
}
.stmts;
let mut strm = TokenStream2::new();
for stmt in used_branch {
stmt.to_tokens(&mut strm)
}
strm.into()
}
/// Creates a compile time if statement
/// that takes checks if 2 types are the same
/// and if returns one of the branches
#[proc_macro]
pub fn type_if(input: TokenStream) -> TokenStream {
let If {
type_to_match,
input_type,
true_branch,
false_branch,
} = parse_macro_input!(input as If<Type>);
let used_branch = if type_to_match.to_token_stream().to_string()
== input_type.to_token_stream().to_string()
{
true_branch
} else {
false_branch
};
used_branch.into_token_stream().into()
}
/// Creates a compile time if statement
/// that takes checks if 2 types are the same
/// and if returns one of the branches
#[proc_macro]
pub fn expr_if(input: TokenStream) -> TokenStream {
let If {
type_to_match,
input_type,
true_branch,
false_branch,
} = parse_macro_input!(input as If<syn::Expr>);
let used_branch = if type_to_match.to_token_stream().to_string()
== input_type.to_token_stream().to_string()
{
true_branch
} else {
false_branch
};
used_branch.into_token_stream().into()
}