blob: 019ada97432512983927d9a71912865fa19ff69e [file] [log] [blame]
David Tolnay7fb11e72018-09-06 01:02:27 -07001/// 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` and that the resulting parser be invoked with a `kw::`
8/// prefix.
9///
10/// ```
David Tolnaya1c98072018-09-06 08:58:10 -070011/// # #[macro_use]
David Tolnay7fb11e72018-09-06 01:02:27 -070012/// # extern crate syn;
13/// #
14/// mod kw {
David Tolnaya1c98072018-09-06 08:58:10 -070015/// custom_keyword!(whatever);
David Tolnay7fb11e72018-09-06 01:02:27 -070016/// }
David Tolnaya1c98072018-09-06 08:58:10 -070017/// #
18/// # fn main() {}
David Tolnay7fb11e72018-09-06 01:02:27 -070019/// ```
20///
21/// The generated syntax tree node supports the following operations just like
22/// any built-in keyword token.
23///
24/// - [Peeking] — `input.peek(kw::whatever)`
25///
26/// - [Parsing] — `input.parse::<kw::whatever>()?`
27///
28/// - [Printing] — `quote!( ... #whatever_token ... )`
29///
30/// - Construction from a [`Span`] — `let whatever_token = kw::whatever(sp)`
31///
32/// - Field access to its span — `let sp = whatever_token.span`
33///
34/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
35/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
36/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
37/// [`Span`]: struct.Span.html
38///
39/// # Example
40///
41/// This example parses input that looks like `bool = true` or `str = "value"`.
42/// The key must be either the identifier `bool` or the identifier `str`. If
43/// `bool`, the value may be either `true` or `false`. If `str`, the value may
44/// be any string literal.
45///
46/// The symbols `bool` and `str` are not reserved keywords in Rust so these are
47/// not considered keywords in the `syn::token` module. Like any other
48/// identifier that is not a keyword, these can be declared as custom keywords
49/// by crates that need to use them as such.
50///
51/// ```
David Tolnaya1c98072018-09-06 08:58:10 -070052/// #[macro_use]
53/// extern crate syn;
54///
55/// use syn::{LitBool, LitStr};
David Tolnay7fb11e72018-09-06 01:02:27 -070056/// use syn::parse::{Parse, ParseStream, Result};
57///
58/// mod kw {
David Tolnaya1c98072018-09-06 08:58:10 -070059/// custom_keyword!(bool);
60/// custom_keyword!(str);
David Tolnay7fb11e72018-09-06 01:02:27 -070061/// }
62///
63/// enum Argument {
64/// Bool {
65/// bool_token: kw::bool,
66/// eq_token: Token![=],
67/// value: LitBool,
68/// },
69/// Str {
70/// str_token: kw::str,
71/// eq_token: Token![=],
72/// value: LitStr,
73/// },
74/// }
75///
76/// impl Parse for Argument {
77/// fn parse(input: ParseStream) -> Result<Self> {
78/// let lookahead = input.lookahead1();
79/// if lookahead.peek(kw::bool) {
80/// Ok(Argument::Bool {
81/// bool_token: input.parse::<kw::bool>()?,
82/// eq_token: input.parse()?,
83/// value: input.parse()?,
84/// })
85/// } else if lookahead.peek(kw::str) {
86/// Ok(Argument::Str {
87/// str_token: input.parse::<kw::str>()?,
88/// eq_token: input.parse()?,
89/// value: input.parse()?,
90/// })
91/// } else {
92/// Err(lookahead.error())
93/// }
94/// }
95/// }
96/// #
97/// # fn main() {}
98/// ```
99#[macro_export(local_inner_macros)]
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200100macro_rules! custom_keyword {
101 ($ident:ident) => {
David Tolnay7fb11e72018-09-06 01:02:27 -0700102 pub struct $ident {
103 pub span: $crate::export::Span,
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200104 }
105
David Tolnay7fb11e72018-09-06 01:02:27 -0700106 #[doc(hidden)]
107 #[allow(non_snake_case)]
108 pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>(span: __S) -> $ident {
109 $ident {
110 span: $crate::export::IntoSpans::into_spans(span)[0],
111 }
112 }
113
114 impl $crate::export::Default for $ident {
115 fn default() -> Self {
116 $ident {
117 span: $crate::export::Span::call_site(),
118 }
119 }
120 }
121
122 impl_parse_for_custom_keyword!($ident);
123 impl_to_tokens_for_custom_keyword!($ident);
124 impl_clone_for_custom_keyword!($ident);
125 impl_extra_traits_for_custom_keyword!($ident);
126 }
127}
128
129// Not public API.
130#[cfg(feature = "parsing")]
131#[doc(hidden)]
132#[macro_export]
133macro_rules! impl_parse_for_custom_keyword {
134 ($ident:ident) => {
135 // For peek.
136 impl $crate::token::CustomKeyword for $ident {
137 fn ident() -> &'static $crate::export::str {
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200138 stringify!($ident)
139 }
140
David Tolnay7fb11e72018-09-06 01:02:27 -0700141 fn display() -> &'static $crate::export::str {
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200142 concat!("`", stringify!($ident), "`")
143 }
144 }
145
146 impl $crate::parse::Parse for $ident {
147 fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
148 input.step(|cursor| {
David Tolnay7fb11e72018-09-06 01:02:27 -0700149 if let $crate::export::Some((ident, rest)) = cursor.ident() {
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200150 if ident == stringify!($ident) {
David Tolnay7fb11e72018-09-06 01:02:27 -0700151 return $crate::export::Ok(($ident { span: ident.span() }, rest));
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200152 }
153 }
David Tolnay7fb11e72018-09-06 01:02:27 -0700154 $crate::export::Err(cursor.error(concat!("expected `", stringify!($ident), "`")))
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200155 })
156 }
157 }
David Tolnay7fb11e72018-09-06 01:02:27 -0700158 };
159}
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200160
David Tolnay7fb11e72018-09-06 01:02:27 -0700161// Not public API.
162#[cfg(not(feature = "parsing"))]
163#[doc(hidden)]
164#[macro_export]
165macro_rules! impl_parse_for_custom_keyword {
166 ($ident:ident) => {};
167}
168
169// Not public API.
170#[cfg(feature = "printing")]
171#[doc(hidden)]
172#[macro_export]
173macro_rules! impl_to_tokens_for_custom_keyword {
174 ($ident:ident) => {
175 impl $crate::export::ToTokens for $ident {
176 fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
177 let ident = $crate::Ident::new(stringify!($ident), self.span);
178 $crate::export::TokenStreamExt::append(tokens, ident);
179 }
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200180 }
David Tolnay7fb11e72018-09-06 01:02:27 -0700181 };
182}
183
184// Not public API.
185#[cfg(not(feature = "printing"))]
186#[doc(hidden)]
187#[macro_export]
188macro_rules! impl_to_tokens_for_custom_keyword {
189 ($ident:ident) => {};
190}
191
192// Not public API.
193#[cfg(feature = "clone-impls")]
194#[doc(hidden)]
195#[macro_export]
196macro_rules! impl_clone_for_custom_keyword {
197 ($ident:ident) => {
198 impl $crate::export::Copy for $ident {}
199
200 impl $crate::export::Clone for $ident {
201 fn clone(&self) -> Self {
202 *self
203 }
204 }
205 };
206}
207
208// Not public API.
209#[cfg(not(feature = "clone-impls"))]
210#[doc(hidden)]
211#[macro_export]
212macro_rules! impl_clone_for_custom_keyword {
213 ($ident:ident) => {};
214}
215
216// Not public API.
217#[cfg(feature = "extra-traits")]
218#[doc(hidden)]
219#[macro_export]
220macro_rules! impl_extra_traits_for_custom_keyword {
221 ($ident:ident) => {
222 impl $crate::export::Debug for $ident {
223 fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
224 $crate::export::Formatter::write_str(f, stringify!($ident))
225 }
226 }
227
228 impl $crate::export::Eq for $ident {}
229
230 impl $crate::export::PartialEq for $ident {
231 fn eq(&self, _other: &Self) -> $crate::export::bool {
232 true
233 }
234 }
235
236 impl $crate::export::Hash for $ident {
237 fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
238 }
239 };
240}
241
242// Not public API.
243#[cfg(not(feature = "extra-traits"))]
244#[doc(hidden)]
245#[macro_export]
246macro_rules! impl_extra_traits_for_custom_keyword {
247 ($ident:ident) => {};
Louis Kureuil Personc0beaf32018-09-05 00:12:43 +0200248}