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