swc_ecma_codegen_macros/
fold.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
109
110
111
use quote::quote_spanned;
use swc_macros_common::prelude::*;
use syn::{
    fold::Fold,
    parse::{Parse, Parser},
    spanned::Spanned,
    token::Token,
    *,
};

pub(crate) struct InjectSelf {
    pub parser: Option<Ident>,
}

fn get_joined_span(t: &dyn ToTokens) -> Span {
    let tts: TokenStream = t.into_token_stream();
    let mut first = None;
    for tt in tts {
        if first.is_none() {
            first = Some(tt.span());
        }

        // last = Some(tt.span());
    }
    let cs = Span::call_site();
    // first.unwrap_or(cs).join(last.unwrap_or(cs)).unwrap_or(cs)
    first.unwrap_or(cs)
}

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")
}

impl Fold for InjectSelf {
    fn fold_signature(&mut self, i: Signature) -> Signature {
        self.parser = i.inputs.first().cloned().and_then(|arg| match arg {
            FnArg::Receiver(Receiver {
                self_token,
                mutability: Some(..),
                ..
            })
            | FnArg::Receiver(Receiver { self_token, .. }) => {
                Some(Ident::new("self", self_token.span()))
            }
            _ => None,
        });
        i
    }

    fn fold_macro(&mut self, i: Macro) -> Macro {
        let parser = match self.parser {
            Some(ref s) => s.clone(),
            _ => {
                // If we are not in parser, don't do anything.
                return i;
            }
        };

        let name = i.path.clone().into_token_stream().to_string();
        let span = get_joined_span(&i.path);

        match &*name {
            "smallvec" | "vec" | "unreachable" | "tok" | "op" | "js_word" => i,
            "println" | "print" | "format" | "assert" | "assert_eq" | "assert_ne"
            | "debug_assert" | "debug_assert_eq" | "debug_assert_ne" | "dbg" => {
                let mut args: Punctuated<Expr, token::Comma> = parse_args(i.tokens);
                args = args
                    .into_pairs()
                    .map(|el| el.map_item(|expr| self.fold_expr(expr)))
                    .collect();
                Macro {
                    tokens: args.into_token_stream(),
                    ..i
                }
            }

            "trace" | "debug" | "info" | "warn" | "error" => i,
            //TODO
            "unimplemented" => i,

            //TODO: Collect expect and give that list to unexpected
            "keyword" | "emit" | "punct" | "semi" | "formatting_semi" | "space"
            | "formatting_space" | "operator" | "opt" | "opt_leading_space" | "srcmap" => {
                let tokens = if i.tokens.is_empty() {
                    quote_spanned!(span => #parser)
                } else {
                    let args: Punctuated<Expr, token::Comma> = parse_args(i.tokens);
                    let args = args
                        .into_pairs()
                        .map(|el| el.map_item(|expr| self.fold_expr(expr)))
                        .flat_map(|arg| arg.into_token_stream());

                    quote_spanned!(span => #parser,)
                        .into_iter()
                        .chain(args)
                        .collect()
                };

                Macro { tokens, ..i }
            }
            _ => {
                unimplemented!("Macro: {:#?}", i);
            }
        }
    }
}