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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#[cfg_attr(
    not(any(feature = "full", feature = "derive")),
    allow(unknown_lints, unused_macro_rules)
)]
macro_rules! ast_struct {
    (
        $(#[$attr:meta])*
        $pub:ident $struct:ident $name:ident #full $body:tt
    ) => {
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(struct $struct);

        #[cfg(feature = "full")]
        $(#[$attr])* $pub $struct $name $body

        #[cfg(not(feature = "full"))]
        $(#[$attr])* $pub $struct $name {
            _noconstruct: ::std::marker::PhantomData<::proc_macro2::Span>,
        }

        #[cfg(all(not(feature = "full"), feature = "printing"))]
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, _: &mut ::proc_macro2::TokenStream) {
                unreachable!()
            }
        }
    };

    (
        $(#[$attr:meta])*
        $pub:ident $struct:ident $name:ident $body:tt
    ) => {
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(struct $struct);

        $(#[$attr])* $pub $struct $name $body
    };
}

#[cfg(any(feature = "full", feature = "derive"))]
macro_rules! ast_enum {
    (
        $(#[$enum_attr:meta])*
        $pub:ident $enum:ident $name:ident $body:tt
    ) => {
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(enum $enum);

        $(#[$enum_attr])* $pub $enum $name $body
    };
}

macro_rules! ast_enum_of_structs {
    (
        $(#[$enum_attr:meta])*
        $pub:ident $enum:ident $name:ident $body:tt
    ) => {
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(enum $enum);

        $(#[$enum_attr])* $pub $enum $name $body

        ast_enum_of_structs_impl!($name $body);

        #[cfg(feature = "printing")]
        generate_to_tokens!(() tokens $name $body);
    };
}

macro_rules! ast_enum_of_structs_impl {
    (
        $name:ident {
            $(
                $(#[cfg $cfg_attr:tt])*
                $(#[doc $($doc_attr:tt)*])*
                $variant:ident $( ($member:ident) )*,
            )*
        }
    ) => {
        $($(
            ast_enum_from_struct!($name::$variant, $member);
        )*)*
    };
}

macro_rules! ast_enum_from_struct {
    // No From<TokenStream> for verbatim variants.
    ($name:ident::Verbatim, $member:ident) => {};

    ($name:ident::$variant:ident, $member:ident) => {
        impl From<$member> for $name {
            fn from(e: $member) -> $name {
                $name::$variant(e)
            }
        }
    };
}

#[cfg(feature = "printing")]
macro_rules! generate_to_tokens {
    (
        ($($arms:tt)*) $tokens:ident $name:ident {
            $(#[cfg $cfg_attr:tt])*
            $(#[doc $($doc_attr:tt)*])*
            $variant:ident,
            $($next:tt)*
        }
    ) => {
        generate_to_tokens!(
            ($($arms)* $(#[cfg $cfg_attr])* $name::$variant => {})
            $tokens $name { $($next)* }
        );
    };

    (
        ($($arms:tt)*) $tokens:ident $name:ident {
            $(#[cfg $cfg_attr:tt])*
            $(#[doc $($doc_attr:tt)*])*
            $variant:ident($member:ident),
            $($next:tt)*
        }
    ) => {
        generate_to_tokens!(
            ($($arms)* $(#[cfg $cfg_attr])* $name::$variant(_e) => _e.to_tokens($tokens),)
            $tokens $name { $($next)* }
        );
    };

    (($($arms:tt)*) $tokens:ident $name:ident {}) => {
        #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, $tokens: &mut ::proc_macro2::TokenStream) {
                match self {
                    $($arms)*
                }
            }
        }
    };
}

// Rustdoc bug: does not respect the doc(hidden) on some items.
#[cfg(all(doc, feature = "parsing"))]
macro_rules! pub_if_not_doc {
    ($(#[$m:meta])* $pub:ident $($item:tt)*) => {
        check_keyword_matches!(pub $pub);

        $(#[$m])*
        $pub(crate) $($item)*
    };
}

#[cfg(all(not(doc), feature = "parsing"))]
macro_rules! pub_if_not_doc {
    ($(#[$m:meta])* $pub:ident $($item:tt)*) => {
        check_keyword_matches!(pub $pub);

        $(#[$m])*
        $pub $($item)*
    };
}

macro_rules! check_keyword_matches {
    (enum enum) => {};
    (pub pub) => {};
    (struct struct) => {};
}