• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::*;
2 use crate::punctuated::Punctuated;
3 use proc_macro2::TokenStream;
4 use std::iter;
5 
6 #[cfg(feature = "parsing")]
7 use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
8 #[cfg(feature = "parsing")]
9 use crate::punctuated::Pair;
10 
11 ast_struct! {
12     /// An attribute like `#[repr(transparent)]`.
13     ///
14     /// *This type is available only if Syn is built with the `"derive"` or `"full"`
15     /// feature.*
16     ///
17     /// <br>
18     ///
19     /// # Syntax
20     ///
21     /// Rust has six types of attributes.
22     ///
23     /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
24     ///   in front of the item they describe.
25     /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
26     ///   of the item they describe, usually a module.
27     /// - Outer doc comments like `/// # Example`.
28     /// - Inner doc comments like `//! Please file an issue`.
29     /// - Outer block comments `/** # Example */`.
30     /// - Inner block comments `/*! Please file an issue */`.
31     ///
32     /// The `style` field of type `AttrStyle` distinguishes whether an attribute
33     /// is outer or inner. Doc comments and block comments are promoted to
34     /// attributes, as this is how they are processed by the compiler and by
35     /// `macro_rules!` macros.
36     ///
37     /// The `path` field gives the possibly colon-delimited path against which
38     /// the attribute is resolved. It is equal to `"doc"` for desugared doc
39     /// comments. The `tokens` field contains the rest of the attribute body as
40     /// tokens.
41     ///
42     /// ```text
43     /// #[derive(Copy)]      #[crate::precondition x < 5]
44     ///   ^^^^^^~~~~~~         ^^^^^^^^^^^^^^^^^^^ ~~~~~
45     ///   path  tokens                 path        tokens
46     /// ```
47     ///
48     /// <br>
49     ///
50     /// # Parsing from tokens to Attribute
51     ///
52     /// This type does not implement the [`Parse`] trait and thus cannot be
53     /// parsed directly by [`ParseStream::parse`]. Instead use
54     /// [`ParseStream::call`] with one of the two parser functions
55     /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
56     /// which you intend to parse.
57     ///
58     /// [`Parse`]: parse::Parse
59     /// [`ParseStream::parse`]: parse::ParseBuffer::parse
60     /// [`ParseStream::call`]: parse::ParseBuffer::call
61     ///
62     /// ```
63     /// use syn::{Attribute, Ident, Result, Token};
64     /// use syn::parse::{Parse, ParseStream};
65     ///
66     /// // Parses a unit struct with attributes.
67     /// //
68     /// //     #[path = "s.tmpl"]
69     /// //     struct S;
70     /// struct UnitStruct {
71     ///     attrs: Vec<Attribute>,
72     ///     struct_token: Token![struct],
73     ///     name: Ident,
74     ///     semi_token: Token![;],
75     /// }
76     ///
77     /// impl Parse for UnitStruct {
78     ///     fn parse(input: ParseStream) -> Result<Self> {
79     ///         Ok(UnitStruct {
80     ///             attrs: input.call(Attribute::parse_outer)?,
81     ///             struct_token: input.parse()?,
82     ///             name: input.parse()?,
83     ///             semi_token: input.parse()?,
84     ///         })
85     ///     }
86     /// }
87     /// ```
88     ///
89     /// <p><br></p>
90     ///
91     /// # Parsing from Attribute to structured arguments
92     ///
93     /// The grammar of attributes in Rust is very flexible, which makes the
94     /// syntax tree not that useful on its own. In particular, arguments of the
95     /// attribute are held in an arbitrary `tokens: TokenStream`. Macros are
96     /// expected to check the `path` of the attribute, decide whether they
97     /// recognize it, and then parse the remaining tokens according to whatever
98     /// grammar they wish to require for that kind of attribute.
99     ///
100     /// If the attribute you are parsing is expected to conform to the
101     /// conventional structured form of attribute, use [`parse_meta()`] to
102     /// obtain that structured representation. If the attribute follows some
103     /// other grammar of its own, use [`parse_args()`] to parse that into the
104     /// expected data structure.
105     ///
106     /// [`parse_meta()`]: Attribute::parse_meta
107     /// [`parse_args()`]: Attribute::parse_args
108     ///
109     /// <p><br></p>
110     ///
111     /// # Doc comments
112     ///
113     /// The compiler transforms doc comments, such as `/// comment` and `/*!
114     /// comment */`, into attributes before macros are expanded. Each comment is
115     /// expanded into an attribute of the form `#[doc = r"comment"]`.
116     ///
117     /// As an example, the following `mod` items are expanded identically:
118     ///
119     /// ```
120     /// # use syn::{ItemMod, parse_quote};
121     /// let doc: ItemMod = parse_quote! {
122     ///     /// Single line doc comments
123     ///     /// We write so many!
124     ///     /**
125     ///      * Multi-line comments...
126     ///      * May span many lines
127     ///      */
128     ///     mod example {
129     ///         //! Of course, they can be inner too
130     ///         /*! And fit in a single line */
131     ///     }
132     /// };
133     /// let attr: ItemMod = parse_quote! {
134     ///     #[doc = r" Single line doc comments"]
135     ///     #[doc = r" We write so many!"]
136     ///     #[doc = r"
137     ///      * Multi-line comments...
138     ///      * May span many lines
139     ///      "]
140     ///     mod example {
141     ///         #![doc = r" Of course, they can be inner too"]
142     ///         #![doc = r" And fit in a single line "]
143     ///     }
144     /// };
145     /// assert_eq!(doc, attr);
146     /// ```
147     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
148     pub struct Attribute {
149         pub pound_token: Token![#],
150         pub style: AttrStyle,
151         pub bracket_token: token::Bracket,
152         pub path: Path,
153         pub tokens: TokenStream,
154     }
155 }
156 
157 impl Attribute {
158     /// Parses the content of the attribute, consisting of the path and tokens,
159     /// as a [`Meta`] if possible.
160     ///
161     /// *This function is available only if Syn is built with the `"parsing"`
162     /// feature.*
163     #[cfg(feature = "parsing")]
164     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
parse_meta(&self) -> Result<Meta>165     pub fn parse_meta(&self) -> Result<Meta> {
166         fn clone_ident_segment(segment: &PathSegment) -> PathSegment {
167             PathSegment {
168                 ident: segment.ident.clone(),
169                 arguments: PathArguments::None,
170             }
171         }
172 
173         let path = Path {
174             leading_colon: self
175                 .path
176                 .leading_colon
177                 .as_ref()
178                 .map(|colon| Token![::](colon.spans)),
179             segments: self
180                 .path
181                 .segments
182                 .pairs()
183                 .map(|pair| match pair {
184                     Pair::Punctuated(seg, punct) => {
185                         Pair::Punctuated(clone_ident_segment(seg), Token![::](punct.spans))
186                     }
187                     Pair::End(seg) => Pair::End(clone_ident_segment(seg)),
188                 })
189                 .collect(),
190         };
191 
192         let parser = |input: ParseStream| parsing::parse_meta_after_path(path, input);
193         parse::Parser::parse2(parser, self.tokens.clone())
194     }
195 
196     /// Parse the arguments to the attribute as a syntax tree.
197     ///
198     /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
199     ///
200     /// - the surrounding delimiters are *not* included in the input to the
201     ///   parser; and
202     /// - the error message has a more useful span when `tokens` is empty.
203     ///
204     /// ```text
205     /// #[my_attr(value < 5)]
206     ///           ^^^^^^^^^ what gets parsed
207     /// ```
208     ///
209     /// *This function is available only if Syn is built with the `"parsing"`
210     /// feature.*
211     #[cfg(feature = "parsing")]
212     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
parse_args<T: Parse>(&self) -> Result<T>213     pub fn parse_args<T: Parse>(&self) -> Result<T> {
214         self.parse_args_with(T::parse)
215     }
216 
217     /// Parse the arguments to the attribute using the given parser.
218     ///
219     /// *This function is available only if Syn is built with the `"parsing"`
220     /// feature.*
221     #[cfg(feature = "parsing")]
222     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>223     pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
224         let parser = |input: ParseStream| {
225             let args = enter_args(self, input)?;
226             parse::parse_stream(parser, &args)
227         };
228         parser.parse2(self.tokens.clone())
229     }
230 
231     /// Parses zero or more outer attributes from the stream.
232     ///
233     /// *This function is available only if Syn is built with the `"parsing"`
234     /// feature.*
235     #[cfg(feature = "parsing")]
236     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
parse_outer(input: ParseStream) -> Result<Vec<Self>>237     pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
238         let mut attrs = Vec::new();
239         while input.peek(Token![#]) {
240             attrs.push(input.call(parsing::single_parse_outer)?);
241         }
242         Ok(attrs)
243     }
244 
245     /// Parses zero or more inner attributes from the stream.
246     ///
247     /// *This function is available only if Syn is built with the `"parsing"`
248     /// feature.*
249     #[cfg(feature = "parsing")]
250     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
parse_inner(input: ParseStream) -> Result<Vec<Self>>251     pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
252         let mut attrs = Vec::new();
253         while input.peek(Token![#]) && input.peek2(Token![!]) {
254             attrs.push(input.call(parsing::single_parse_inner)?);
255         }
256         Ok(attrs)
257     }
258 }
259 
260 #[cfg(feature = "parsing")]
expected_parentheses(attr: &Attribute) -> String261 fn expected_parentheses(attr: &Attribute) -> String {
262     let style = match attr.style {
263         AttrStyle::Outer => "#",
264         AttrStyle::Inner(_) => "#!",
265     };
266 
267     let mut path = String::new();
268     for segment in &attr.path.segments {
269         if !path.is_empty() || attr.path.leading_colon.is_some() {
270             path += "::";
271         }
272         path += &segment.ident.to_string();
273     }
274 
275     format!("{}[{}(...)]", style, path)
276 }
277 
278 #[cfg(feature = "parsing")]
enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>>279 fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
280     if input.is_empty() {
281         let expected = expected_parentheses(attr);
282         let msg = format!("expected attribute arguments in parentheses: {}", expected);
283         return Err(crate::error::new2(
284             attr.pound_token.span,
285             attr.bracket_token.span,
286             msg,
287         ));
288     } else if input.peek(Token![=]) {
289         let expected = expected_parentheses(attr);
290         let msg = format!("expected parentheses: {}", expected);
291         return Err(input.error(msg));
292     };
293 
294     let content;
295     if input.peek(token::Paren) {
296         parenthesized!(content in input);
297     } else if input.peek(token::Bracket) {
298         bracketed!(content in input);
299     } else if input.peek(token::Brace) {
300         braced!(content in input);
301     } else {
302         return Err(input.error("unexpected token in attribute arguments"));
303     }
304 
305     if input.is_empty() {
306         Ok(content)
307     } else {
308         Err(input.error("unexpected token in attribute arguments"))
309     }
310 }
311 
312 ast_enum! {
313     /// Distinguishes between attributes that decorate an item and attributes
314     /// that are contained within an item.
315     ///
316     /// *This type is available only if Syn is built with the `"derive"` or `"full"`
317     /// feature.*
318     ///
319     /// # Outer attributes
320     ///
321     /// - `#[repr(transparent)]`
322     /// - `/// # Example`
323     /// - `/** Please file an issue */`
324     ///
325     /// # Inner attributes
326     ///
327     /// - `#![feature(proc_macro)]`
328     /// - `//! # Example`
329     /// - `/*! Please file an issue */`
330     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
331     pub enum AttrStyle {
332         Outer,
333         Inner(Token![!]),
334     }
335 }
336 
337 ast_enum_of_structs! {
338     /// Content of a compile-time structured attribute.
339     ///
340     /// *This type is available only if Syn is built with the `"derive"` or `"full"`
341     /// feature.*
342     ///
343     /// ## Path
344     ///
345     /// A meta path is like the `test` in `#[test]`.
346     ///
347     /// ## List
348     ///
349     /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
350     ///
351     /// ## NameValue
352     ///
353     /// A name-value meta is like the `path = "..."` in `#[path =
354     /// "sys/windows.rs"]`.
355     ///
356     /// # Syntax tree enum
357     ///
358     /// This type is a [syntax tree enum].
359     ///
360     /// [syntax tree enum]: Expr#syntax-tree-enums
361     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
362     pub enum Meta {
363         Path(Path),
364 
365         /// A structured list within an attribute, like `derive(Copy, Clone)`.
366         List(MetaList),
367 
368         /// A name-value pair within an attribute, like `feature = "nightly"`.
369         NameValue(MetaNameValue),
370     }
371 }
372 
373 ast_struct! {
374     /// A structured list within an attribute, like `derive(Copy, Clone)`.
375     ///
376     /// *This type is available only if Syn is built with the `"derive"` or
377     /// `"full"` feature.*
378     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
379     pub struct MetaList {
380         pub path: Path,
381         pub paren_token: token::Paren,
382         pub nested: Punctuated<NestedMeta, Token![,]>,
383     }
384 }
385 
386 ast_struct! {
387     /// A name-value pair within an attribute, like `feature = "nightly"`.
388     ///
389     /// *This type is available only if Syn is built with the `"derive"` or
390     /// `"full"` feature.*
391     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
392     pub struct MetaNameValue {
393         pub path: Path,
394         pub eq_token: Token![=],
395         pub lit: Lit,
396     }
397 }
398 
399 impl Meta {
400     /// Returns the identifier that begins this structured meta item.
401     ///
402     /// For example this would return the `test` in `#[test]`, the `derive` in
403     /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
path(&self) -> &Path404     pub fn path(&self) -> &Path {
405         match self {
406             Meta::Path(path) => path,
407             Meta::List(meta) => &meta.path,
408             Meta::NameValue(meta) => &meta.path,
409         }
410     }
411 }
412 
413 ast_enum_of_structs! {
414     /// Element of a compile-time attribute list.
415     ///
416     /// *This type is available only if Syn is built with the `"derive"` or `"full"`
417     /// feature.*
418     #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
419     pub enum NestedMeta {
420         /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
421         /// would be a nested `Meta::Path`.
422         Meta(Meta),
423 
424         /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
425         Lit(Lit),
426     }
427 }
428 
429 /// Conventional argument type associated with an invocation of an attribute
430 /// macro.
431 ///
432 /// For example if we are developing an attribute macro that is intended to be
433 /// invoked on function items as follows:
434 ///
435 /// ```
436 /// # const IGNORE: &str = stringify! {
437 /// #[my_attribute(path = "/v1/refresh")]
438 /// # };
439 /// pub fn refresh() {
440 ///     /* ... */
441 /// }
442 /// ```
443 ///
444 /// The implementation of this macro would want to parse its attribute arguments
445 /// as type `AttributeArgs`.
446 ///
447 /// ```
448 /// # extern crate proc_macro;
449 /// #
450 /// use proc_macro::TokenStream;
451 /// use syn::{parse_macro_input, AttributeArgs, ItemFn};
452 ///
453 /// # const IGNORE: &str = stringify! {
454 /// #[proc_macro_attribute]
455 /// # };
456 /// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
457 ///     let args = parse_macro_input!(args as AttributeArgs);
458 ///     let input = parse_macro_input!(input as ItemFn);
459 ///
460 ///     /* ... */
461 /// #   "".parse().unwrap()
462 /// }
463 /// ```
464 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
465 pub type AttributeArgs = Vec<NestedMeta>;
466 
467 pub trait FilterAttrs<'a> {
468     type Ret: Iterator<Item = &'a Attribute>;
469 
outer(self) -> Self::Ret470     fn outer(self) -> Self::Ret;
inner(self) -> Self::Ret471     fn inner(self) -> Self::Ret;
472 }
473 
474 impl<'a, T> FilterAttrs<'a> for T
475 where
476     T: IntoIterator<Item = &'a Attribute>,
477 {
478     type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
479 
outer(self) -> Self::Ret480     fn outer(self) -> Self::Ret {
481         fn is_outer(attr: &&Attribute) -> bool {
482             match attr.style {
483                 AttrStyle::Outer => true,
484                 AttrStyle::Inner(_) => false,
485             }
486         }
487         self.into_iter().filter(is_outer)
488     }
489 
inner(self) -> Self::Ret490     fn inner(self) -> Self::Ret {
491         fn is_inner(attr: &&Attribute) -> bool {
492             match attr.style {
493                 AttrStyle::Inner(_) => true,
494                 AttrStyle::Outer => false,
495             }
496         }
497         self.into_iter().filter(is_inner)
498     }
499 }
500 
501 #[cfg(feature = "parsing")]
502 pub mod parsing {
503     use super::*;
504     use crate::ext::IdentExt;
505     use crate::parse::{Parse, ParseStream, Result};
506     #[cfg(feature = "full")]
507     use crate::private;
508 
single_parse_inner(input: ParseStream) -> Result<Attribute>509     pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
510         let content;
511         Ok(Attribute {
512             pound_token: input.parse()?,
513             style: AttrStyle::Inner(input.parse()?),
514             bracket_token: bracketed!(content in input),
515             path: content.call(Path::parse_mod_style)?,
516             tokens: content.parse()?,
517         })
518     }
519 
single_parse_outer(input: ParseStream) -> Result<Attribute>520     pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
521         let content;
522         Ok(Attribute {
523             pound_token: input.parse()?,
524             style: AttrStyle::Outer,
525             bracket_token: bracketed!(content in input),
526             path: content.call(Path::parse_mod_style)?,
527             tokens: content.parse()?,
528         })
529     }
530 
531     #[cfg(feature = "full")]
532     impl private {
attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute>533         pub(crate) fn attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute> {
534             let mut attrs = outer;
535             attrs.extend(inner);
536             attrs
537         }
538     }
539 
540     // Like Path::parse_mod_style but accepts keywords in the path.
parse_meta_path(input: ParseStream) -> Result<Path>541     fn parse_meta_path(input: ParseStream) -> Result<Path> {
542         Ok(Path {
543             leading_colon: input.parse()?,
544             segments: {
545                 let mut segments = Punctuated::new();
546                 while input.peek(Ident::peek_any) {
547                     let ident = Ident::parse_any(input)?;
548                     segments.push_value(PathSegment::from(ident));
549                     if !input.peek(Token![::]) {
550                         break;
551                     }
552                     let punct = input.parse()?;
553                     segments.push_punct(punct);
554                 }
555                 if segments.is_empty() {
556                     return Err(input.error("expected path"));
557                 } else if segments.trailing_punct() {
558                     return Err(input.error("expected path segment"));
559                 }
560                 segments
561             },
562         })
563     }
564 
565     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
566     impl Parse for Meta {
parse(input: ParseStream) -> Result<Self>567         fn parse(input: ParseStream) -> Result<Self> {
568             let path = input.call(parse_meta_path)?;
569             parse_meta_after_path(path, input)
570         }
571     }
572 
573     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
574     impl Parse for MetaList {
parse(input: ParseStream) -> Result<Self>575         fn parse(input: ParseStream) -> Result<Self> {
576             let path = input.call(parse_meta_path)?;
577             parse_meta_list_after_path(path, input)
578         }
579     }
580 
581     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
582     impl Parse for MetaNameValue {
parse(input: ParseStream) -> Result<Self>583         fn parse(input: ParseStream) -> Result<Self> {
584             let path = input.call(parse_meta_path)?;
585             parse_meta_name_value_after_path(path, input)
586         }
587     }
588 
589     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
590     impl Parse for NestedMeta {
parse(input: ParseStream) -> Result<Self>591         fn parse(input: ParseStream) -> Result<Self> {
592             if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
593                 input.parse().map(NestedMeta::Lit)
594             } else if input.peek(Ident::peek_any)
595                 || input.peek(Token![::]) && input.peek3(Ident::peek_any)
596             {
597                 input.parse().map(NestedMeta::Meta)
598             } else {
599                 Err(input.error("expected identifier or literal"))
600             }
601         }
602     }
603 
parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta>604     pub fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
605         if input.peek(token::Paren) {
606             parse_meta_list_after_path(path, input).map(Meta::List)
607         } else if input.peek(Token![=]) {
608             parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
609         } else {
610             Ok(Meta::Path(path))
611         }
612     }
613 
parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList>614     fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
615         let content;
616         Ok(MetaList {
617             path,
618             paren_token: parenthesized!(content in input),
619             nested: content.parse_terminated(NestedMeta::parse)?,
620         })
621     }
622 
parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue>623     fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
624         Ok(MetaNameValue {
625             path,
626             eq_token: input.parse()?,
627             lit: input.parse()?,
628         })
629     }
630 }
631 
632 #[cfg(feature = "printing")]
633 mod printing {
634     use super::*;
635     use proc_macro2::TokenStream;
636     use quote::ToTokens;
637 
638     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
639     impl ToTokens for Attribute {
to_tokens(&self, tokens: &mut TokenStream)640         fn to_tokens(&self, tokens: &mut TokenStream) {
641             self.pound_token.to_tokens(tokens);
642             if let AttrStyle::Inner(b) = &self.style {
643                 b.to_tokens(tokens);
644             }
645             self.bracket_token.surround(tokens, |tokens| {
646                 self.path.to_tokens(tokens);
647                 self.tokens.to_tokens(tokens);
648             });
649         }
650     }
651 
652     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
653     impl ToTokens for MetaList {
to_tokens(&self, tokens: &mut TokenStream)654         fn to_tokens(&self, tokens: &mut TokenStream) {
655             self.path.to_tokens(tokens);
656             self.paren_token.surround(tokens, |tokens| {
657                 self.nested.to_tokens(tokens);
658             })
659         }
660     }
661 
662     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
663     impl ToTokens for MetaNameValue {
to_tokens(&self, tokens: &mut TokenStream)664         fn to_tokens(&self, tokens: &mut TokenStream) {
665             self.path.to_tokens(tokens);
666             self.eq_token.to_tokens(tokens);
667             self.lit.to_tokens(tokens);
668         }
669     }
670 }
671