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