1 | /// Define a type that supports parsing and printing a given identifier as if it |
2 | /// were a keyword. |
3 | /// |
4 | /// # Usage |
5 | /// |
6 | /// As a convention, it is recommended that this macro be invoked within a |
7 | /// module called `kw` or `keyword` and that the resulting parser be invoked |
8 | /// with a `kw::` or `keyword::` prefix. |
9 | /// |
10 | /// ``` |
11 | /// mod kw { |
12 | /// syn::custom_keyword!(whatever); |
13 | /// } |
14 | /// ``` |
15 | /// |
16 | /// The generated syntax tree node supports the following operations just like |
17 | /// any built-in keyword token. |
18 | /// |
19 | /// - [Peeking] — `input.peek(kw::whatever)` |
20 | /// |
21 | /// - [Parsing] — `input.parse::<kw::whatever>()?` |
22 | /// |
23 | /// - [Printing] — `quote!( ... #whatever_token ... )` |
24 | /// |
25 | /// - Construction from a [`Span`] — `let whatever_token = kw::whatever(sp)` |
26 | /// |
27 | /// - Field access to its span — `let sp = whatever_token.span` |
28 | /// |
29 | /// [Peeking]: crate::parse::ParseBuffer::peek |
30 | /// [Parsing]: crate::parse::ParseBuffer::parse |
31 | /// [Printing]: quote::ToTokens |
32 | /// [`Span`]: proc_macro2::Span |
33 | /// |
34 | /// # Example |
35 | /// |
36 | /// This example parses input that looks like `bool = true` or `str = "value"`. |
37 | /// The key must be either the identifier `bool` or the identifier `str`. If |
38 | /// `bool`, the value may be either `true` or `false`. If `str`, the value may |
39 | /// be any string literal. |
40 | /// |
41 | /// The symbols `bool` and `str` are not reserved keywords in Rust so these are |
42 | /// not considered keywords in the `syn::token` module. Like any other |
43 | /// identifier that is not a keyword, these can be declared as custom keywords |
44 | /// by crates that need to use them as such. |
45 | /// |
46 | /// ``` |
47 | /// use syn::{LitBool, LitStr, Result, Token}; |
48 | /// use syn::parse::{Parse, ParseStream}; |
49 | /// |
50 | /// mod kw { |
51 | /// syn::custom_keyword!(bool); |
52 | /// syn::custom_keyword!(str); |
53 | /// } |
54 | /// |
55 | /// enum Argument { |
56 | /// Bool { |
57 | /// bool_token: kw::bool, |
58 | /// eq_token: Token![=], |
59 | /// value: LitBool, |
60 | /// }, |
61 | /// Str { |
62 | /// str_token: kw::str, |
63 | /// eq_token: Token![=], |
64 | /// value: LitStr, |
65 | /// }, |
66 | /// } |
67 | /// |
68 | /// impl Parse for Argument { |
69 | /// fn parse(input: ParseStream) -> Result<Self> { |
70 | /// let lookahead = input.lookahead1(); |
71 | /// if lookahead.peek(kw::bool) { |
72 | /// Ok(Argument::Bool { |
73 | /// bool_token: input.parse::<kw::bool>()?, |
74 | /// eq_token: input.parse()?, |
75 | /// value: input.parse()?, |
76 | /// }) |
77 | /// } else if lookahead.peek(kw::str) { |
78 | /// Ok(Argument::Str { |
79 | /// str_token: input.parse::<kw::str>()?, |
80 | /// eq_token: input.parse()?, |
81 | /// value: input.parse()?, |
82 | /// }) |
83 | /// } else { |
84 | /// Err(lookahead.error()) |
85 | /// } |
86 | /// } |
87 | /// } |
88 | /// ``` |
89 | #[macro_export ] |
90 | macro_rules! custom_keyword { |
91 | ($ident:ident) => { |
92 | #[allow(non_camel_case_types)] |
93 | pub struct $ident { |
94 | pub span: $crate::__private::Span, |
95 | } |
96 | |
97 | #[doc(hidden)] |
98 | #[allow(dead_code, non_snake_case)] |
99 | pub fn $ident<__S: $crate::__private::IntoSpans<[$crate::__private::Span; 1]>>( |
100 | span: __S, |
101 | ) -> $ident { |
102 | $ident { |
103 | span: $crate::__private::IntoSpans::into_spans(span)[0], |
104 | } |
105 | } |
106 | |
107 | impl $crate::__private::Default for $ident { |
108 | fn default() -> Self { |
109 | $ident { |
110 | span: $crate::__private::Span::call_site(), |
111 | } |
112 | } |
113 | } |
114 | |
115 | $crate::impl_parse_for_custom_keyword!($ident); |
116 | $crate::impl_to_tokens_for_custom_keyword!($ident); |
117 | $crate::impl_clone_for_custom_keyword!($ident); |
118 | $crate::impl_extra_traits_for_custom_keyword!($ident); |
119 | }; |
120 | } |
121 | |
122 | // Not public API. |
123 | #[cfg (feature = "parsing" )] |
124 | #[doc (hidden)] |
125 | #[macro_export ] |
126 | macro_rules! impl_parse_for_custom_keyword { |
127 | ($ident:ident) => { |
128 | // For peek. |
129 | impl $crate::token::CustomToken for $ident { |
130 | fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool { |
131 | if let $crate::__private::Some((ident, _rest)) = cursor.ident() { |
132 | ident == stringify!($ident) |
133 | } else { |
134 | false |
135 | } |
136 | } |
137 | |
138 | fn display() -> &'static $crate::__private::str { |
139 | concat!("`" , stringify!($ident), "`" ) |
140 | } |
141 | } |
142 | |
143 | impl $crate::parse::Parse for $ident { |
144 | fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { |
145 | input.step(|cursor| { |
146 | if let $crate::__private::Some((ident, rest)) = cursor.ident() { |
147 | if ident == stringify!($ident) { |
148 | return $crate::__private::Ok(($ident { span: ident.span() }, rest)); |
149 | } |
150 | } |
151 | $crate::__private::Err(cursor.error(concat!( |
152 | "expected `" , |
153 | stringify!($ident), |
154 | "`" |
155 | ))) |
156 | }) |
157 | } |
158 | } |
159 | }; |
160 | } |
161 | |
162 | // Not public API. |
163 | #[cfg (not(feature = "parsing" ))] |
164 | #[doc (hidden)] |
165 | #[macro_export ] |
166 | macro_rules! impl_parse_for_custom_keyword { |
167 | ($ident:ident) => {}; |
168 | } |
169 | |
170 | // Not public API. |
171 | #[cfg (feature = "printing" )] |
172 | #[doc (hidden)] |
173 | #[macro_export ] |
174 | macro_rules! impl_to_tokens_for_custom_keyword { |
175 | ($ident:ident) => { |
176 | impl $crate::__private::ToTokens for $ident { |
177 | fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { |
178 | let ident = $crate::Ident::new(stringify!($ident), self.span); |
179 | $crate::__private::TokenStreamExt::append(tokens, ident); |
180 | } |
181 | } |
182 | }; |
183 | } |
184 | |
185 | // Not public API. |
186 | #[cfg (not(feature = "printing" ))] |
187 | #[doc (hidden)] |
188 | #[macro_export ] |
189 | macro_rules! impl_to_tokens_for_custom_keyword { |
190 | ($ident:ident) => {}; |
191 | } |
192 | |
193 | // Not public API. |
194 | #[cfg (feature = "clone-impls" )] |
195 | #[doc (hidden)] |
196 | #[macro_export ] |
197 | macro_rules! impl_clone_for_custom_keyword { |
198 | ($ident:ident) => { |
199 | impl $crate::__private::Copy for $ident {} |
200 | |
201 | #[allow(clippy::expl_impl_clone_on_copy)] |
202 | impl $crate::__private::Clone for $ident { |
203 | fn clone(&self) -> Self { |
204 | *self |
205 | } |
206 | } |
207 | }; |
208 | } |
209 | |
210 | // Not public API. |
211 | #[cfg (not(feature = "clone-impls" ))] |
212 | #[doc (hidden)] |
213 | #[macro_export ] |
214 | macro_rules! impl_clone_for_custom_keyword { |
215 | ($ident:ident) => {}; |
216 | } |
217 | |
218 | // Not public API. |
219 | #[cfg (feature = "extra-traits" )] |
220 | #[doc (hidden)] |
221 | #[macro_export ] |
222 | macro_rules! impl_extra_traits_for_custom_keyword { |
223 | ($ident:ident) => { |
224 | impl $crate::__private::Debug for $ident { |
225 | fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::fmt::Result { |
226 | $crate::__private::Formatter::write_str( |
227 | f, |
228 | concat!("Keyword [" , stringify!($ident), "]" ), |
229 | ) |
230 | } |
231 | } |
232 | |
233 | impl $crate::__private::Eq for $ident {} |
234 | |
235 | impl $crate::__private::PartialEq for $ident { |
236 | fn eq(&self, _other: &Self) -> $crate::__private::bool { |
237 | true |
238 | } |
239 | } |
240 | |
241 | impl $crate::__private::Hash for $ident { |
242 | fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} |
243 | } |
244 | }; |
245 | } |
246 | |
247 | // Not public API. |
248 | #[cfg (not(feature = "extra-traits" ))] |
249 | #[doc (hidden)] |
250 | #[macro_export ] |
251 | macro_rules! impl_extra_traits_for_custom_keyword { |
252 | ($ident:ident) => {}; |
253 | } |
254 | |