pulley_macros/
interp_disable_if_cfg.rs

1use proc_macro::TokenStream;
2use quote::{ToTokens, TokenStreamExt, quote};
3use syn::{
4    Attribute, Result, Signature, Visibility, braced,
5    parse::{Parse, ParseStream},
6    parse_macro_input, token,
7};
8
9pub fn run(attrs: TokenStream, item: TokenStream) -> TokenStream {
10    let mut cfg = None;
11
12    let config_parser = syn::meta::parser(|meta| {
13        cfg = Some(meta.path.require_ident()?.clone());
14        Ok(())
15    });
16
17    parse_macro_input!(attrs with config_parser);
18
19    match expand(cfg.unwrap(), parse_macro_input!(item as Fn)) {
20        Ok(tok) => tok,
21        Err(e) => e.into_compile_error().into(),
22    }
23}
24
25/// Custom function parser.
26/// Parses the function's attributes, visibility and signature, leaving the
27/// block as an opaque [`TokenStream`].
28struct Fn {
29    attrs: Vec<Attribute>,
30    visibility: Visibility,
31    sig: Signature,
32    body: Block,
33}
34
35impl Parse for Fn {
36    fn parse(input: ParseStream) -> Result<Self> {
37        let attrs = input.call(Attribute::parse_outer)?;
38        let visibility: Visibility = input.parse()?;
39        let sig: Signature = input.parse()?;
40        let body: Block = input.parse()?;
41
42        Ok(Self {
43            attrs,
44            visibility,
45            sig,
46            body,
47        })
48    }
49}
50
51impl ToTokens for Fn {
52    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
53        for attr in &self.attrs {
54            attr.to_tokens(tokens);
55        }
56        self.visibility.to_tokens(tokens);
57        self.sig.to_tokens(tokens);
58        self.body.to_tokens(tokens);
59    }
60}
61
62/// A generic function body represented as a braced [`TokenStream`].
63struct Block {
64    brace: token::Brace,
65    rest: proc_macro2::TokenStream,
66}
67
68impl Parse for Block {
69    fn parse(input: ParseStream) -> Result<Self> {
70        let content;
71        Ok(Self {
72            brace: braced!(content in input),
73            rest: content.parse()?,
74        })
75    }
76}
77
78impl ToTokens for Block {
79    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
80        self.brace.surround(tokens, |tokens| {
81            tokens.append_all(self.rest.clone());
82        });
83    }
84}
85
86fn expand(cfg: syn::Ident, func: Fn) -> Result<TokenStream> {
87    let Fn {
88        attrs,
89        visibility,
90        sig,
91        body: _,
92    } = &func;
93    let name = &sig.ident;
94    Ok(quote! {
95        #[cfg(#cfg)]
96        #(#attrs)*
97        #[allow(unused_variables)]
98        #visibility #sig {
99            self.done_trap_kind::<crate::#name>(Some(TrapKind::DisabledOpcode))
100        }
101
102        #[cfg(not(#cfg))]
103        #func
104    }
105    .into())
106}