swc_ecma_codegen_macros/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
extern crate proc_macro;

use proc_macro2::TokenStream;
use swc_macros_common::prelude::*;
use syn::{
    fold::Fold,
    parse::{Parse, Parser},
    token::Token,
    *,
};

///
///
/// # Example
///
/// ```ignore
/// impl MacroNode for ParamOrTsParamProp {
///     fn emit(&mut self, emitter: &mut Macro) -> Result {
///         match self {
///             ParamOrTsParamProp::Param(n) => emit!(n),
///             ParamOrTsParamProp::TsParamProp(n) => emit!(n),
///         }
///     }
/// }
/// ```
///
///
/// ## `emit!()`.
///
/// `emit!()` macro in `#[node_impl]` functions are special.
///
/// Those are replaced with `emit_with` and `adjust_span` methods, depending on
/// the context.
#[proc_macro_attribute]
pub fn node_impl(
    _attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let item: ItemImpl = syn::parse(item).expect("failed to parse input as an item");

    let mut output = TokenStream::new();

    for i in item.items {
        match i {
            ImplItem::Fn(i) => {
                let item = expand_node_impl_method(&item.self_ty, i);

                output.extend(item.to_token_stream());
            }

            _ => {
                panic!("Unexpected item: {:?}", i);
            }
        }
    }

    output.into()
}

/// Returns `(emitter_method, adjuster_method)`
fn expand_node_impl_method(node_type: &Type, src: ImplItemFn) -> ItemImpl {
    let emit_block = ReplaceEmit { emit: true }.fold_block(src.block.clone());

    parse_quote!(
        impl crate::Node for #node_type {
            fn emit_with<W, S>(&self, emitter: &mut crate::Emitter<'_, W, S>) -> crate::Result
            where
                W: crate::text_writer::WriteJs,
                S: swc_common::SourceMapper + swc_ecma_ast::SourceMapperExt,
            {
                #emit_block
            }

        }
    )
}

struct ReplaceEmit {
    emit: bool,
}

impl Fold for ReplaceEmit {
    fn fold_macro(&mut self, mut i: Macro) -> Macro {
        let name = i.path.clone().into_token_stream().to_string();

        if "emit" == &*name {
            let args: Punctuated<Expr, Token![,]> = parse_args(i.tokens);
            let args: TokenStream = args
                .into_pairs()
                .flat_map(|arg| arg.into_token_stream())
                .collect();

            let is_emit = self.emit;
            i = parse_quote!(emit_node_inner!(emitter, #is_emit, #args));
        }

        i
    }
}

fn parse_args<T, P>(tokens: TokenStream) -> Punctuated<T, P>
where
    T: Parse,
    P: Parse + Token,
{
    let parser = Punctuated::parse_separated_nonempty;
    parser.parse2(tokens).expect("failed parse args")
}