• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>>(
100             span: __S,
101         ) -> $ident {
102             $ident {
103                 span: $crate::__private::IntoSpans::into_spans(span),
104             }
105         }
106 
107         const _: () = {
108             impl $crate::__private::Default for $ident {
109                 fn default() -> Self {
110                     $ident {
111                         span: $crate::__private::Span::call_site(),
112                     }
113                 }
114             }
115 
116             $crate::impl_parse_for_custom_keyword!($ident);
117             $crate::impl_to_tokens_for_custom_keyword!($ident);
118             $crate::impl_clone_for_custom_keyword!($ident);
119             $crate::impl_extra_traits_for_custom_keyword!($ident);
120         };
121     };
122 }
123 
124 // Not public API.
125 #[cfg(feature = "parsing")]
126 #[doc(hidden)]
127 #[macro_export]
128 macro_rules! impl_parse_for_custom_keyword {
129     ($ident:ident) => {
130         // For peek.
131         impl $crate::__private::CustomToken for $ident {
132             fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool {
133                 if let $crate::__private::Some((ident, _rest)) = cursor.ident() {
134                     ident == $crate::__private::stringify!($ident)
135                 } else {
136                     false
137                 }
138             }
139 
140             fn display() -> &'static $crate::__private::str {
141                 $crate::__private::concat!("`", $crate::__private::stringify!($ident), "`")
142             }
143         }
144 
145         impl $crate::parse::Parse for $ident {
146             fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
147                 input.step(|cursor| {
148                     if let $crate::__private::Some((ident, rest)) = cursor.ident() {
149                         if ident == $crate::__private::stringify!($ident) {
150                             return $crate::__private::Ok(($ident { span: ident.span() }, rest));
151                         }
152                     }
153                     $crate::__private::Err(cursor.error($crate::__private::concat!(
154                         "expected `",
155                         $crate::__private::stringify!($ident),
156                         "`",
157                     )))
158                 })
159             }
160         }
161     };
162 }
163 
164 // Not public API.
165 #[cfg(not(feature = "parsing"))]
166 #[doc(hidden)]
167 #[macro_export]
168 macro_rules! impl_parse_for_custom_keyword {
169     ($ident:ident) => {};
170 }
171 
172 // Not public API.
173 #[cfg(feature = "printing")]
174 #[doc(hidden)]
175 #[macro_export]
176 macro_rules! impl_to_tokens_for_custom_keyword {
177     ($ident:ident) => {
178         impl $crate::__private::ToTokens for $ident {
179             fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) {
180                 let ident = $crate::Ident::new($crate::__private::stringify!($ident), self.span);
181                 $crate::__private::TokenStreamExt::append(tokens, ident);
182             }
183         }
184     };
185 }
186 
187 // Not public API.
188 #[cfg(not(feature = "printing"))]
189 #[doc(hidden)]
190 #[macro_export]
191 macro_rules! impl_to_tokens_for_custom_keyword {
192     ($ident:ident) => {};
193 }
194 
195 // Not public API.
196 #[cfg(feature = "clone-impls")]
197 #[doc(hidden)]
198 #[macro_export]
199 macro_rules! impl_clone_for_custom_keyword {
200     ($ident:ident) => {
201         impl $crate::__private::Copy for $ident {}
202 
203         #[allow(clippy::expl_impl_clone_on_copy)]
204         impl $crate::__private::Clone for $ident {
205             fn clone(&self) -> Self {
206                 *self
207             }
208         }
209     };
210 }
211 
212 // Not public API.
213 #[cfg(not(feature = "clone-impls"))]
214 #[doc(hidden)]
215 #[macro_export]
216 macro_rules! impl_clone_for_custom_keyword {
217     ($ident:ident) => {};
218 }
219 
220 // Not public API.
221 #[cfg(feature = "extra-traits")]
222 #[doc(hidden)]
223 #[macro_export]
224 macro_rules! impl_extra_traits_for_custom_keyword {
225     ($ident:ident) => {
226         impl $crate::__private::Debug for $ident {
227             fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::FmtResult {
228                 $crate::__private::Formatter::write_str(
229                     f,
230                     $crate::__private::concat!(
231                         "Keyword [",
232                         $crate::__private::stringify!($ident),
233                         "]",
234                     ),
235                 )
236             }
237         }
238 
239         impl $crate::__private::Eq for $ident {}
240 
241         impl $crate::__private::PartialEq for $ident {
242             fn eq(&self, _other: &Self) -> $crate::__private::bool {
243                 true
244             }
245         }
246 
247         impl $crate::__private::Hash for $ident {
248             fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {}
249         }
250     };
251 }
252 
253 // Not public API.
254 #[cfg(not(feature = "extra-traits"))]
255 #[doc(hidden)]
256 #[macro_export]
257 macro_rules! impl_extra_traits_for_custom_keyword {
258     ($ident:ident) => {};
259 }
260