]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | use super::*; |
2 | use crate::punctuated::Punctuated; | |
3 | ||
4 | ast_struct! { | |
5 | /// An enum variant. | |
6 | /// | |
7 | /// *This type is available if Syn is built with the `"derive"` or `"full"` | |
8 | /// feature.* | |
9 | pub struct Variant { | |
10 | /// Attributes tagged on the variant. | |
11 | pub attrs: Vec<Attribute>, | |
12 | ||
13 | /// Name of the variant. | |
14 | pub ident: Ident, | |
15 | ||
16 | /// Content stored in the variant. | |
17 | pub fields: Fields, | |
18 | ||
19 | /// Explicit discriminant: `Variant = 1` | |
20 | pub discriminant: Option<(Token![=], Expr)>, | |
21 | } | |
22 | } | |
23 | ||
24 | ast_enum_of_structs! { | |
25 | /// Data stored within an enum variant or struct. | |
26 | /// | |
27 | /// *This type is available if Syn is built with the `"derive"` or `"full"` | |
28 | /// feature.* | |
29 | /// | |
30 | /// # Syntax tree enum | |
31 | /// | |
32 | /// This type is a [syntax tree enum]. | |
33 | /// | |
34 | /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums | |
35 | // | |
36 | // TODO: change syntax-tree-enum link to an intra rustdoc link, currently | |
37 | // blocked on https://github.com/rust-lang/rust/issues/62833 | |
38 | pub enum Fields { | |
39 | /// Named fields of a struct or struct variant such as `Point { x: f64, | |
40 | /// y: f64 }`. | |
41 | Named(FieldsNamed), | |
42 | ||
43 | /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`. | |
44 | Unnamed(FieldsUnnamed), | |
45 | ||
46 | /// Unit struct or unit variant such as `None`. | |
47 | Unit, | |
48 | } | |
49 | } | |
50 | ||
51 | ast_struct! { | |
52 | /// Named fields of a struct or struct variant such as `Point { x: f64, | |
53 | /// y: f64 }`. | |
54 | /// | |
55 | /// *This type is available if Syn is built with the `"derive"` or | |
56 | /// `"full"` feature.* | |
57 | pub struct FieldsNamed { | |
58 | pub brace_token: token::Brace, | |
59 | pub named: Punctuated<Field, Token![,]>, | |
60 | } | |
61 | } | |
62 | ||
63 | ast_struct! { | |
64 | /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`. | |
65 | /// | |
66 | /// *This type is available if Syn is built with the `"derive"` or | |
67 | /// `"full"` feature.* | |
68 | pub struct FieldsUnnamed { | |
69 | pub paren_token: token::Paren, | |
70 | pub unnamed: Punctuated<Field, Token![,]>, | |
71 | } | |
72 | } | |
73 | ||
74 | impl Fields { | |
75 | /// Get an iterator over the borrowed [`Field`] items in this object. This | |
76 | /// iterator can be used to iterate over a named or unnamed struct or | |
77 | /// variant's fields uniformly. | |
78 | pub fn iter(&self) -> punctuated::Iter<Field> { | |
79 | match self { | |
80 | Fields::Unit => crate::punctuated::empty_punctuated_iter(), | |
81 | Fields::Named(f) => f.named.iter(), | |
82 | Fields::Unnamed(f) => f.unnamed.iter(), | |
83 | } | |
84 | } | |
85 | ||
86 | /// Get an iterator over the mutably borrowed [`Field`] items in this | |
87 | /// object. This iterator can be used to iterate over a named or unnamed | |
88 | /// struct or variant's fields uniformly. | |
89 | pub fn iter_mut(&mut self) -> punctuated::IterMut<Field> { | |
90 | match self { | |
91 | Fields::Unit => crate::punctuated::empty_punctuated_iter_mut(), | |
92 | Fields::Named(f) => f.named.iter_mut(), | |
93 | Fields::Unnamed(f) => f.unnamed.iter_mut(), | |
94 | } | |
95 | } | |
60c5eb7d XL |
96 | |
97 | /// Returns the number of fields. | |
98 | pub fn len(&self) -> usize { | |
99 | match self { | |
100 | Fields::Unit => 0, | |
101 | Fields::Named(f) => f.named.len(), | |
102 | Fields::Unnamed(f) => f.unnamed.len(), | |
103 | } | |
104 | } | |
105 | ||
106 | /// Returns `true` if there are zero fields. | |
107 | pub fn is_empty(&self) -> bool { | |
108 | match self { | |
109 | Fields::Unit => true, | |
110 | Fields::Named(f) => f.named.is_empty(), | |
111 | Fields::Unnamed(f) => f.unnamed.is_empty(), | |
112 | } | |
113 | } | |
e74abb32 XL |
114 | } |
115 | ||
116 | impl IntoIterator for Fields { | |
117 | type Item = Field; | |
118 | type IntoIter = punctuated::IntoIter<Field>; | |
119 | ||
120 | fn into_iter(self) -> Self::IntoIter { | |
121 | match self { | |
122 | Fields::Unit => Punctuated::<Field, ()>::new().into_iter(), | |
123 | Fields::Named(f) => f.named.into_iter(), | |
124 | Fields::Unnamed(f) => f.unnamed.into_iter(), | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | impl<'a> IntoIterator for &'a Fields { | |
130 | type Item = &'a Field; | |
131 | type IntoIter = punctuated::Iter<'a, Field>; | |
132 | ||
133 | fn into_iter(self) -> Self::IntoIter { | |
134 | self.iter() | |
135 | } | |
136 | } | |
137 | ||
138 | impl<'a> IntoIterator for &'a mut Fields { | |
139 | type Item = &'a mut Field; | |
140 | type IntoIter = punctuated::IterMut<'a, Field>; | |
141 | ||
142 | fn into_iter(self) -> Self::IntoIter { | |
143 | self.iter_mut() | |
144 | } | |
145 | } | |
146 | ||
147 | ast_struct! { | |
148 | /// A field of a struct or enum variant. | |
149 | /// | |
150 | /// *This type is available if Syn is built with the `"derive"` or `"full"` | |
151 | /// feature.* | |
152 | pub struct Field { | |
153 | /// Attributes tagged on the field. | |
154 | pub attrs: Vec<Attribute>, | |
155 | ||
156 | /// Visibility of the field. | |
157 | pub vis: Visibility, | |
158 | ||
159 | /// Name of the field, if any. | |
160 | /// | |
161 | /// Fields of tuple structs have no names. | |
162 | pub ident: Option<Ident>, | |
163 | ||
164 | pub colon_token: Option<Token![:]>, | |
165 | ||
166 | /// Type of the field. | |
167 | pub ty: Type, | |
168 | } | |
169 | } | |
170 | ||
171 | ast_enum_of_structs! { | |
172 | /// The visibility level of an item: inherited or `pub` or | |
173 | /// `pub(restricted)`. | |
174 | /// | |
175 | /// *This type is available if Syn is built with the `"derive"` or `"full"` | |
176 | /// feature.* | |
177 | /// | |
178 | /// # Syntax tree enum | |
179 | /// | |
180 | /// This type is a [syntax tree enum]. | |
181 | /// | |
182 | /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums | |
183 | // | |
184 | // TODO: change syntax-tree-enum link to an intra rustdoc link, currently | |
185 | // blocked on https://github.com/rust-lang/rust/issues/62833 | |
186 | pub enum Visibility { | |
187 | /// A public visibility level: `pub`. | |
188 | Public(VisPublic), | |
189 | ||
190 | /// A crate-level visibility: `crate`. | |
191 | Crate(VisCrate), | |
192 | ||
193 | /// A visibility level restricted to some path: `pub(self)` or | |
194 | /// `pub(super)` or `pub(crate)` or `pub(in some::module)`. | |
195 | Restricted(VisRestricted), | |
196 | ||
197 | /// An inherited visibility, which usually means private. | |
198 | Inherited, | |
199 | } | |
200 | } | |
201 | ||
202 | ast_struct! { | |
203 | /// A public visibility level: `pub`. | |
204 | /// | |
205 | /// *This type is available if Syn is built with the `"derive"` or | |
206 | /// `"full"` feature.* | |
207 | pub struct VisPublic { | |
208 | pub pub_token: Token![pub], | |
209 | } | |
210 | } | |
211 | ||
212 | ast_struct! { | |
213 | /// A crate-level visibility: `crate`. | |
214 | /// | |
215 | /// *This type is available if Syn is built with the `"derive"` or | |
216 | /// `"full"` feature.* | |
217 | pub struct VisCrate { | |
218 | pub crate_token: Token![crate], | |
219 | } | |
220 | } | |
221 | ||
222 | ast_struct! { | |
223 | /// A visibility level restricted to some path: `pub(self)` or | |
224 | /// `pub(super)` or `pub(crate)` or `pub(in some::module)`. | |
225 | /// | |
226 | /// *This type is available if Syn is built with the `"derive"` or | |
227 | /// `"full"` feature.* | |
228 | pub struct VisRestricted { | |
229 | pub pub_token: Token![pub], | |
230 | pub paren_token: token::Paren, | |
231 | pub in_token: Option<Token![in]>, | |
232 | pub path: Box<Path>, | |
233 | } | |
234 | } | |
235 | ||
236 | #[cfg(feature = "parsing")] | |
237 | pub mod parsing { | |
238 | use super::*; | |
239 | ||
240 | use crate::ext::IdentExt; | |
60c5eb7d | 241 | use crate::parse::discouraged::Speculative; |
e74abb32 XL |
242 | use crate::parse::{Parse, ParseStream, Result}; |
243 | ||
244 | impl Parse for Variant { | |
245 | fn parse(input: ParseStream) -> Result<Self> { | |
246 | Ok(Variant { | |
247 | attrs: input.call(Attribute::parse_outer)?, | |
248 | ident: input.parse()?, | |
249 | fields: { | |
250 | if input.peek(token::Brace) { | |
251 | Fields::Named(input.parse()?) | |
252 | } else if input.peek(token::Paren) { | |
253 | Fields::Unnamed(input.parse()?) | |
254 | } else { | |
255 | Fields::Unit | |
256 | } | |
257 | }, | |
258 | discriminant: { | |
259 | if input.peek(Token![=]) { | |
260 | let eq_token: Token![=] = input.parse()?; | |
261 | let discriminant: Expr = input.parse()?; | |
262 | Some((eq_token, discriminant)) | |
263 | } else { | |
264 | None | |
265 | } | |
266 | }, | |
267 | }) | |
268 | } | |
269 | } | |
270 | ||
271 | impl Parse for FieldsNamed { | |
272 | fn parse(input: ParseStream) -> Result<Self> { | |
273 | let content; | |
274 | Ok(FieldsNamed { | |
275 | brace_token: braced!(content in input), | |
276 | named: content.parse_terminated(Field::parse_named)?, | |
277 | }) | |
278 | } | |
279 | } | |
280 | ||
281 | impl Parse for FieldsUnnamed { | |
282 | fn parse(input: ParseStream) -> Result<Self> { | |
283 | let content; | |
284 | Ok(FieldsUnnamed { | |
285 | paren_token: parenthesized!(content in input), | |
286 | unnamed: content.parse_terminated(Field::parse_unnamed)?, | |
287 | }) | |
288 | } | |
289 | } | |
290 | ||
291 | impl Field { | |
292 | /// Parses a named (braced struct) field. | |
293 | pub fn parse_named(input: ParseStream) -> Result<Self> { | |
294 | Ok(Field { | |
295 | attrs: input.call(Attribute::parse_outer)?, | |
296 | vis: input.parse()?, | |
297 | ident: Some(input.parse()?), | |
298 | colon_token: Some(input.parse()?), | |
299 | ty: input.parse()?, | |
300 | }) | |
301 | } | |
302 | ||
303 | /// Parses an unnamed (tuple struct) field. | |
304 | pub fn parse_unnamed(input: ParseStream) -> Result<Self> { | |
305 | Ok(Field { | |
306 | attrs: input.call(Attribute::parse_outer)?, | |
307 | vis: input.parse()?, | |
308 | ident: None, | |
309 | colon_token: None, | |
310 | ty: input.parse()?, | |
311 | }) | |
312 | } | |
313 | } | |
314 | ||
315 | impl Parse for Visibility { | |
316 | fn parse(input: ParseStream) -> Result<Self> { | |
317 | if input.peek(Token![pub]) { | |
318 | Self::parse_pub(input) | |
319 | } else if input.peek(Token![crate]) { | |
320 | Self::parse_crate(input) | |
321 | } else { | |
322 | Ok(Visibility::Inherited) | |
323 | } | |
324 | } | |
325 | } | |
326 | ||
327 | impl Visibility { | |
328 | fn parse_pub(input: ParseStream) -> Result<Self> { | |
329 | let pub_token = input.parse::<Token![pub]>()?; | |
330 | ||
331 | if input.peek(token::Paren) { | |
e74abb32 | 332 | let ahead = input.fork(); |
e74abb32 | 333 | |
60c5eb7d XL |
334 | let content; |
335 | let paren_token = parenthesized!(content in ahead); | |
e74abb32 XL |
336 | if content.peek(Token![crate]) |
337 | || content.peek(Token![self]) | |
338 | || content.peek(Token![super]) | |
339 | { | |
60c5eb7d XL |
340 | let path = content.call(Ident::parse_any)?; |
341 | ||
342 | // Ensure there are no additional tokens within `content`. | |
343 | // Without explicitly checking, we may misinterpret a tuple | |
344 | // field as a restricted visibility, causing a parse error. | |
345 | // e.g. `pub (crate::A, crate::B)` (Issue #720). | |
346 | if content.is_empty() { | |
347 | input.advance_to(&ahead); | |
348 | return Ok(Visibility::Restricted(VisRestricted { | |
349 | pub_token, | |
350 | paren_token, | |
351 | in_token: None, | |
352 | path: Box::new(Path::from(path)), | |
353 | })); | |
354 | } | |
e74abb32 | 355 | } else if content.peek(Token![in]) { |
60c5eb7d XL |
356 | let in_token: Token![in] = content.parse()?; |
357 | let path = content.call(Path::parse_mod_style)?; | |
358 | ||
359 | input.advance_to(&ahead); | |
e74abb32 XL |
360 | return Ok(Visibility::Restricted(VisRestricted { |
361 | pub_token, | |
60c5eb7d XL |
362 | paren_token, |
363 | in_token: Some(in_token), | |
364 | path: Box::new(path), | |
e74abb32 XL |
365 | })); |
366 | } | |
367 | } | |
368 | ||
369 | Ok(Visibility::Public(VisPublic { pub_token })) | |
370 | } | |
371 | ||
372 | fn parse_crate(input: ParseStream) -> Result<Self> { | |
373 | if input.peek2(Token![::]) { | |
374 | Ok(Visibility::Inherited) | |
375 | } else { | |
376 | Ok(Visibility::Crate(VisCrate { | |
377 | crate_token: input.parse()?, | |
378 | })) | |
379 | } | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | #[cfg(feature = "printing")] | |
385 | mod printing { | |
386 | use super::*; | |
387 | ||
388 | use proc_macro2::TokenStream; | |
389 | use quote::{ToTokens, TokenStreamExt}; | |
390 | ||
391 | use crate::print::TokensOrDefault; | |
392 | ||
393 | impl ToTokens for Variant { | |
394 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
395 | tokens.append_all(&self.attrs); | |
396 | self.ident.to_tokens(tokens); | |
397 | self.fields.to_tokens(tokens); | |
398 | if let Some((eq_token, disc)) = &self.discriminant { | |
399 | eq_token.to_tokens(tokens); | |
400 | disc.to_tokens(tokens); | |
401 | } | |
402 | } | |
403 | } | |
404 | ||
405 | impl ToTokens for FieldsNamed { | |
406 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
407 | self.brace_token.surround(tokens, |tokens| { | |
408 | self.named.to_tokens(tokens); | |
409 | }); | |
410 | } | |
411 | } | |
412 | ||
413 | impl ToTokens for FieldsUnnamed { | |
414 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
415 | self.paren_token.surround(tokens, |tokens| { | |
416 | self.unnamed.to_tokens(tokens); | |
417 | }); | |
418 | } | |
419 | } | |
420 | ||
421 | impl ToTokens for Field { | |
422 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
423 | tokens.append_all(&self.attrs); | |
424 | self.vis.to_tokens(tokens); | |
425 | if let Some(ident) = &self.ident { | |
426 | ident.to_tokens(tokens); | |
427 | TokensOrDefault(&self.colon_token).to_tokens(tokens); | |
428 | } | |
429 | self.ty.to_tokens(tokens); | |
430 | } | |
431 | } | |
432 | ||
433 | impl ToTokens for VisPublic { | |
434 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
435 | self.pub_token.to_tokens(tokens) | |
436 | } | |
437 | } | |
438 | ||
439 | impl ToTokens for VisCrate { | |
440 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
441 | self.crate_token.to_tokens(tokens); | |
442 | } | |
443 | } | |
444 | ||
445 | impl ToTokens for VisRestricted { | |
446 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
447 | self.pub_token.to_tokens(tokens); | |
448 | self.paren_token.surround(tokens, |tokens| { | |
449 | // TODO: If we have a path which is not "self" or "super" or | |
450 | // "crate", automatically add the "in" token. | |
451 | self.in_token.to_tokens(tokens); | |
452 | self.path.to_tokens(tokens); | |
453 | }); | |
454 | } | |
455 | } | |
456 | } |