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