• 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; 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 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