xref: /third_party/rust/crates/syn/src/attr.rs (revision fad3a1d3)
1use super::*;
2use proc_macro2::TokenStream;
3use std::iter;
4use std::slice;
5
6#[cfg(feature = "parsing")]
7use crate::meta::{self, ParseNestedMeta};
8#[cfg(feature = "parsing")]
9use crate::parse::{Parse, ParseStream, Parser, Result};
10
11ast_struct! {
12    /// An attribute, like `#[repr(transparent)]`.
13    ///
14    /// <br>
15    ///
16    /// # Syntax
17    ///
18    /// Rust has six types of attributes.
19    ///
20    /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
21    ///   in front of the item they describe.
22    ///
23    /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
24    ///   of the item they describe, usually a module.
25    ///
26    /// - Outer one-line doc comments like `/// Example`.
27    ///
28    /// - Inner one-line doc comments like `//! Please file an issue`.
29    ///
30    /// - Outer documentation blocks `/** Example */`.
31    ///
32    /// - Inner documentation blocks `/*! Please file an issue */`.
33    ///
34    /// The `style` field of type `AttrStyle` distinguishes whether an attribute
35    /// is outer or inner.
36    ///
37    /// Every attribute has a `path` that indicates the intended interpretation
38    /// of the rest of the attribute's contents. The path and the optional
39    /// additional contents are represented together in the `meta` field of the
40    /// attribute in three possible varieties:
41    ///
42    /// - Meta::Path &mdash; attributes whose information content conveys just a
43    ///   path, for example the `#[test]` attribute.
44    ///
45    /// - Meta::List &mdash; attributes that carry arbitrary tokens after the
46    ///   path, surrounded by a delimiter (parenthesis, bracket, or brace). For
47    ///   example `#[derive(Copy)]` or `#[precondition(x < 5)]`.
48    ///
49    /// - Meta::NameValue &mdash; attributes with an `=` sign after the path,
50    ///   followed by a Rust expression. For example `#[path =
51    ///   "sys/windows.rs"]`.
52    ///
53    /// All doc comments are represented in the NameValue style with a path of
54    /// "doc", as this is how they are processed by the compiler and by
55    /// `macro_rules!` macros.
56    ///
57    /// ```text
58    /// #[derive(Copy, Clone)]
59    ///   ~~~~~~Path
60    ///   ^^^^^^^^^^^^^^^^^^^Meta::List
61    ///
62    /// #[path = "sys/windows.rs"]
63    ///   ~~~~Path
64    ///   ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue
65    ///
66    /// #[test]
67    ///   ^^^^Meta::Path
68    /// ```
69    ///
70    /// <br>
71    ///
72    /// # Parsing from tokens to Attribute
73    ///
74    /// This type does not implement the [`Parse`] trait and thus cannot be
75    /// parsed directly by [`ParseStream::parse`]. Instead use
76    /// [`ParseStream::call`] with one of the two parser functions
77    /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
78    /// which you intend to parse.
79    ///
80    /// [`Parse`]: parse::Parse
81    /// [`ParseStream::parse`]: parse::ParseBuffer::parse
82    /// [`ParseStream::call`]: parse::ParseBuffer::call
83    ///
84    /// ```
85    /// use syn::{Attribute, Ident, Result, Token};
86    /// use syn::parse::{Parse, ParseStream};
87    ///
88    /// // Parses a unit struct with attributes.
89    /// //
90    /// //     #[path = "s.tmpl"]
91    /// //     struct S;
92    /// struct UnitStruct {
93    ///     attrs: Vec<Attribute>,
94    ///     struct_token: Token![struct],
95    ///     name: Ident,
96    ///     semi_token: Token![;],
97    /// }
98    ///
99    /// impl Parse for UnitStruct {
100    ///     fn parse(input: ParseStream) -> Result<Self> {
101    ///         Ok(UnitStruct {
102    ///             attrs: input.call(Attribute::parse_outer)?,
103    ///             struct_token: input.parse()?,
104    ///             name: input.parse()?,
105    ///             semi_token: input.parse()?,
106    ///         })
107    ///     }
108    /// }
109    /// ```
110    ///
111    /// <p><br></p>
112    ///
113    /// # Parsing from Attribute to structured arguments
114    ///
115    /// The grammar of attributes in Rust is very flexible, which makes the
116    /// syntax tree not that useful on its own. In particular, arguments of the
117    /// `Meta::List` variety of attribute are held in an arbitrary `tokens:
118    /// TokenStream`. Macros are expected to check the `path` of the attribute,
119    /// decide whether they recognize it, and then parse the remaining tokens
120    /// according to whatever grammar they wish to require for that kind of
121    /// attribute. Use [`parse_args()`] to parse those tokens into the expected
122    /// data structure.
123    ///
124    /// [`parse_args()`]: Attribute::parse_args
125    ///
126    /// <p><br></p>
127    ///
128    /// # Doc comments
129    ///
130    /// The compiler transforms doc comments, such as `/// comment` and `/*!
131    /// comment */`, into attributes before macros are expanded. Each comment is
132    /// expanded into an attribute of the form `#[doc = r"comment"]`.
133    ///
134    /// As an example, the following `mod` items are expanded identically:
135    ///
136    /// ```
137    /// # use syn::{ItemMod, parse_quote};
138    /// let doc: ItemMod = parse_quote! {
139    ///     /// Single line doc comments
140    ///     /// We write so many!
141    ///     /**
142    ///      * Multi-line comments...
143    ///      * May span many lines
144    ///      */
145    ///     mod example {
146    ///         //! Of course, they can be inner too
147    ///         /*! And fit in a single line */
148    ///     }
149    /// };
150    /// let attr: ItemMod = parse_quote! {
151    ///     #[doc = r" Single line doc comments"]
152    ///     #[doc = r" We write so many!"]
153    ///     #[doc = r"
154    ///      * Multi-line comments...
155    ///      * May span many lines
156    ///      "]
157    ///     mod example {
158    ///         #![doc = r" Of course, they can be inner too"]
159    ///         #![doc = r" And fit in a single line "]
160    ///     }
161    /// };
162    /// assert_eq!(doc, attr);
163    /// ```
164    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
165    pub struct Attribute {
166        pub pound_token: Token![#],
167        pub style: AttrStyle,
168        pub bracket_token: token::Bracket,
169        pub meta: Meta,
170    }
171}
172
173impl Attribute {
174    /// Returns the path that identifies the interpretation of this attribute.
175    ///
176    /// For example this would return the `test` in `#[test]`, the `derive` in
177    /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
178    pub fn path(&self) -> &Path {
179        self.meta.path()
180    }
181
182    /// Parse the arguments to the attribute as a syntax tree.
183    ///
184    /// This is similar to pulling out the `TokenStream` from `Meta::List` and
185    /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using
186    /// `parse_args` the error message has a more useful span when `tokens` is
187    /// empty.
188    ///
189    /// The surrounding delimiters are *not* included in the input to the
190    /// parser.
191    ///
192    /// ```text
193    /// #[my_attr(value < 5)]
194    ///           ^^^^^^^^^ what gets parsed
195    /// ```
196    ///
197    /// # Example
198    ///
199    /// ```
200    /// use syn::{parse_quote, Attribute, Expr};
201    ///
202    /// let attr: Attribute = parse_quote! {
203    ///     #[precondition(value < 5)]
204    /// };
205    ///
206    /// if attr.path().is_ident("precondition") {
207    ///     let precondition: Expr = attr.parse_args()?;
208    ///     // ...
209    /// }
210    /// # anyhow::Ok(())
211    /// ```
212    #[cfg(feature = "parsing")]
213    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
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    /// # Example
221    ///
222    /// ```
223    /// use syn::{parse_quote, Attribute};
224    ///
225    /// let attr: Attribute = parse_quote! {
226    ///     #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }]
227    /// };
228    ///
229    /// let bwom = attr.parse_args_with(Attribute::parse_outer)?;
230    ///
231    /// // Attribute does not have a Parse impl, so we couldn't directly do:
232    /// // let bwom: Attribute = attr.parse_args()?;
233    /// # anyhow::Ok(())
234    /// ```
235    #[cfg(feature = "parsing")]
236    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
237    pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
238        match &self.meta {
239            Meta::Path(path) => Err(crate::error::new2(
240                path.segments.first().unwrap().ident.span(),
241                path.segments.last().unwrap().ident.span(),
242                format!(
243                    "expected attribute arguments in parentheses: {}[{}(...)]",
244                    parsing::DisplayAttrStyle(&self.style),
245                    parsing::DisplayPath(path),
246                ),
247            )),
248            Meta::NameValue(meta) => Err(Error::new(
249                meta.eq_token.span,
250                format_args!(
251                    "expected parentheses: {}[{}(...)]",
252                    parsing::DisplayAttrStyle(&self.style),
253                    parsing::DisplayPath(&meta.path),
254                ),
255            )),
256            Meta::List(meta) => meta.parse_args_with(parser),
257        }
258    }
259
260    /// Parse the arguments to the attribute, expecting it to follow the
261    /// conventional structure used by most of Rust's built-in attributes.
262    ///
263    /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference
264    /// explains the convention in more detail. Not all attributes follow this
265    /// convention, so [`parse_args()`][Self::parse_args] is available if you
266    /// need to parse arbitrarily goofy attribute syntax.
267    ///
268    /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax
269    ///
270    /// # Example
271    ///
272    /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute
273    /// syntax.
274    ///
275    /// ```
276    /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt};
277    ///
278    /// let input: ItemStruct = parse_quote! {
279    ///     #[repr(C, align(4))]
280    ///     pub struct MyStruct(u16, u32);
281    /// };
282    ///
283    /// let mut repr_c = false;
284    /// let mut repr_transparent = false;
285    /// let mut repr_align = None::<usize>;
286    /// let mut repr_packed = None::<usize>;
287    /// for attr in &input.attrs {
288    ///     if attr.path().is_ident("repr") {
289    ///         attr.parse_nested_meta(|meta| {
290    ///             // #[repr(C)]
291    ///             if meta.path.is_ident("C") {
292    ///                 repr_c = true;
293    ///                 return Ok(());
294    ///             }
295    ///
296    ///             // #[repr(transparent)]
297    ///             if meta.path.is_ident("transparent") {
298    ///                 repr_transparent = true;
299    ///                 return Ok(());
300    ///             }
301    ///
302    ///             // #[repr(align(N))]
303    ///             if meta.path.is_ident("align") {
304    ///                 let content;
305    ///                 parenthesized!(content in meta.input);
306    ///                 let lit: LitInt = content.parse()?;
307    ///                 let n: usize = lit.base10_parse()?;
308    ///                 repr_align = Some(n);
309    ///                 return Ok(());
310    ///             }
311    ///
312    ///             // #[repr(packed)] or #[repr(packed(N))], omitted N means 1
313    ///             if meta.path.is_ident("packed") {
314    ///                 if meta.input.peek(token::Paren) {
315    ///                     let content;
316    ///                     parenthesized!(content in meta.input);
317    ///                     let lit: LitInt = content.parse()?;
318    ///                     let n: usize = lit.base10_parse()?;
319    ///                     repr_packed = Some(n);
320    ///                 } else {
321    ///                     repr_packed = Some(1);
322    ///                 }
323    ///                 return Ok(());
324    ///             }
325    ///
326    ///             Err(meta.error("unrecognized repr"))
327    ///         })?;
328    ///     }
329    /// }
330    /// # anyhow::Ok(())
331    /// ```
332    ///
333    /// # Alternatives
334    ///
335    /// In some cases, for attributes which have nested layers of structured
336    /// content, the following less flexible approach might be more convenient:
337    ///
338    /// ```
339    /// # use syn::{parse_quote, ItemStruct};
340    /// #
341    /// # let input: ItemStruct = parse_quote! {
342    /// #     #[repr(C, align(4))]
343    /// #     pub struct MyStruct(u16, u32);
344    /// # };
345    /// #
346    /// use syn::punctuated::Punctuated;
347    /// use syn::{parenthesized, token, Error, LitInt, Meta, Token};
348    ///
349    /// let mut repr_c = false;
350    /// let mut repr_transparent = false;
351    /// let mut repr_align = None::<usize>;
352    /// let mut repr_packed = None::<usize>;
353    /// for attr in &input.attrs {
354    ///     if attr.path().is_ident("repr") {
355    ///         let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
356    ///         for meta in nested {
357    ///             match meta {
358    ///                 // #[repr(C)]
359    ///                 Meta::Path(path) if path.is_ident("C") => {
360    ///                     repr_c = true;
361    ///                 }
362    ///
363    ///                 // #[repr(align(N))]
364    ///                 Meta::List(meta) if meta.path.is_ident("align") => {
365    ///                     let lit: LitInt = meta.parse_args()?;
366    ///                     let n: usize = lit.base10_parse()?;
367    ///                     repr_align = Some(n);
368    ///                 }
369    ///
370    ///                 /* ... */
371    ///
372    ///                 _ => {
373    ///                     return Err(Error::new_spanned(meta, "unrecognized repr"));
374    ///                 }
375    ///             }
376    ///         }
377    ///     }
378    /// }
379    /// # Ok(())
380    /// ```
381    #[cfg(feature = "parsing")]
382    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
383    pub fn parse_nested_meta(
384        &self,
385        logic: impl FnMut(ParseNestedMeta) -> Result<()>,
386    ) -> Result<()> {
387        self.parse_args_with(meta::parser(logic))
388    }
389
390    /// Parses zero or more outer attributes from the stream.
391    ///
392    /// # Example
393    ///
394    /// See
395    /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
396    #[cfg(feature = "parsing")]
397    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
398    pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
399        let mut attrs = Vec::new();
400        while input.peek(Token![#]) {
401            attrs.push(input.call(parsing::single_parse_outer)?);
402        }
403        Ok(attrs)
404    }
405
406    /// Parses zero or more inner attributes from the stream.
407    ///
408    /// # Example
409    ///
410    /// See
411    /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
412    #[cfg(feature = "parsing")]
413    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
414    pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
415        let mut attrs = Vec::new();
416        parsing::parse_inner(input, &mut attrs)?;
417        Ok(attrs)
418    }
419}
420
421ast_enum! {
422    /// Distinguishes between attributes that decorate an item and attributes
423    /// that are contained within an item.
424    ///
425    /// # Outer attributes
426    ///
427    /// - `#[repr(transparent)]`
428    /// - `/// # Example`
429    /// - `/** Please file an issue */`
430    ///
431    /// # Inner attributes
432    ///
433    /// - `#![feature(proc_macro)]`
434    /// - `//! # Example`
435    /// - `/*! Please file an issue */`
436    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
437    pub enum AttrStyle {
438        Outer,
439        Inner(Token![!]),
440    }
441}
442
443ast_enum_of_structs! {
444    /// Content of a compile-time structured attribute.
445    ///
446    /// ## Path
447    ///
448    /// A meta path is like the `test` in `#[test]`.
449    ///
450    /// ## List
451    ///
452    /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
453    ///
454    /// ## NameValue
455    ///
456    /// A name-value meta is like the `path = "..."` in `#[path =
457    /// "sys/windows.rs"]`.
458    ///
459    /// # Syntax tree enum
460    ///
461    /// This type is a [syntax tree enum].
462    ///
463    /// [syntax tree enum]: Expr#syntax-tree-enums
464    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
465    pub enum Meta {
466        Path(Path),
467
468        /// A structured list within an attribute, like `derive(Copy, Clone)`.
469        List(MetaList),
470
471        /// A name-value pair within an attribute, like `feature = "nightly"`.
472        NameValue(MetaNameValue),
473    }
474}
475
476ast_struct! {
477    /// A structured list within an attribute, like `derive(Copy, Clone)`.
478    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
479    pub struct MetaList {
480        pub path: Path,
481        pub delimiter: MacroDelimiter,
482        pub tokens: TokenStream,
483    }
484}
485
486ast_struct! {
487    /// A name-value pair within an attribute, like `feature = "nightly"`.
488    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
489    pub struct MetaNameValue {
490        pub path: Path,
491        pub eq_token: Token![=],
492        pub value: Expr,
493    }
494}
495
496impl Meta {
497    /// Returns the path that begins this structured meta item.
498    ///
499    /// For example this would return the `test` in `#[test]`, the `derive` in
500    /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
501    pub fn path(&self) -> &Path {
502        match self {
503            Meta::Path(path) => path,
504            Meta::List(meta) => &meta.path,
505            Meta::NameValue(meta) => &meta.path,
506        }
507    }
508
509    /// Error if this is a `Meta::List` or `Meta::NameValue`.
510    #[cfg(feature = "parsing")]
511    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
512    pub fn require_path_only(&self) -> Result<&Path> {
513        let error_span = match self {
514            Meta::Path(path) => return Ok(path),
515            Meta::List(meta) => meta.delimiter.span().open(),
516            Meta::NameValue(meta) => meta.eq_token.span,
517        };
518        Err(Error::new(error_span, "unexpected token in attribute"))
519    }
520
521    /// Error if this is a `Meta::Path` or `Meta::NameValue`.
522    #[cfg(feature = "parsing")]
523    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
524    pub fn require_list(&self) -> Result<&MetaList> {
525        match self {
526            Meta::List(meta) => Ok(meta),
527            Meta::Path(path) => Err(crate::error::new2(
528                path.segments.first().unwrap().ident.span(),
529                path.segments.last().unwrap().ident.span(),
530                format!(
531                    "expected attribute arguments in parentheses: `{}(...)`",
532                    parsing::DisplayPath(path),
533                ),
534            )),
535            Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")),
536        }
537    }
538
539    /// Error if this is a `Meta::Path` or `Meta::List`.
540    #[cfg(feature = "parsing")]
541    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
542    pub fn require_name_value(&self) -> Result<&MetaNameValue> {
543        match self {
544            Meta::NameValue(meta) => Ok(meta),
545            Meta::Path(path) => Err(crate::error::new2(
546                path.segments.first().unwrap().ident.span(),
547                path.segments.last().unwrap().ident.span(),
548                format!(
549                    "expected a value for this attribute: `{} = ...`",
550                    parsing::DisplayPath(path),
551                ),
552            )),
553            Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")),
554        }
555    }
556}
557
558impl MetaList {
559    /// See [`Attribute::parse_args`].
560    #[cfg(feature = "parsing")]
561    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
562    pub fn parse_args<T: Parse>(&self) -> Result<T> {
563        self.parse_args_with(T::parse)
564    }
565
566    /// See [`Attribute::parse_args_with`].
567    #[cfg(feature = "parsing")]
568    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
569    pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
570        let scope = self.delimiter.span().close();
571        crate::parse::parse_scoped(parser, scope, self.tokens.clone())
572    }
573
574    /// See [`Attribute::parse_nested_meta`].
575    #[cfg(feature = "parsing")]
576    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
577    pub fn parse_nested_meta(
578        &self,
579        logic: impl FnMut(ParseNestedMeta) -> Result<()>,
580    ) -> Result<()> {
581        self.parse_args_with(meta::parser(logic))
582    }
583}
584
585pub(crate) trait FilterAttrs<'a> {
586    type Ret: Iterator<Item = &'a Attribute>;
587
588    fn outer(self) -> Self::Ret;
589    fn inner(self) -> Self::Ret;
590}
591
592impl<'a> FilterAttrs<'a> for &'a [Attribute] {
593    type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
594
595    fn outer(self) -> Self::Ret {
596        fn is_outer(attr: &&Attribute) -> bool {
597            match attr.style {
598                AttrStyle::Outer => true,
599                AttrStyle::Inner(_) => false,
600            }
601        }
602        self.iter().filter(is_outer)
603    }
604
605    fn inner(self) -> Self::Ret {
606        fn is_inner(attr: &&Attribute) -> bool {
607            match attr.style {
608                AttrStyle::Inner(_) => true,
609                AttrStyle::Outer => false,
610            }
611        }
612        self.iter().filter(is_inner)
613    }
614}
615
616#[cfg(feature = "parsing")]
617pub(crate) mod parsing {
618    use super::*;
619    use crate::parse::discouraged::Speculative as _;
620    use crate::parse::{Parse, ParseStream, Result};
621    use std::fmt::{self, Display};
622
623    pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
624        while input.peek(Token![#]) && input.peek2(Token![!]) {
625            attrs.push(input.call(parsing::single_parse_inner)?);
626        }
627        Ok(())
628    }
629
630    pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
631        let content;
632        Ok(Attribute {
633            pound_token: input.parse()?,
634            style: AttrStyle::Inner(input.parse()?),
635            bracket_token: bracketed!(content in input),
636            meta: content.parse()?,
637        })
638    }
639
640    pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
641        let content;
642        Ok(Attribute {
643            pound_token: input.parse()?,
644            style: AttrStyle::Outer,
645            bracket_token: bracketed!(content in input),
646            meta: content.parse()?,
647        })
648    }
649
650    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
651    impl Parse for Meta {
652        fn parse(input: ParseStream) -> Result<Self> {
653            let path = input.call(Path::parse_mod_style)?;
654            parse_meta_after_path(path, input)
655        }
656    }
657
658    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
659    impl Parse for MetaList {
660        fn parse(input: ParseStream) -> Result<Self> {
661            let path = input.call(Path::parse_mod_style)?;
662            parse_meta_list_after_path(path, input)
663        }
664    }
665
666    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
667    impl Parse for MetaNameValue {
668        fn parse(input: ParseStream) -> Result<Self> {
669            let path = input.call(Path::parse_mod_style)?;
670            parse_meta_name_value_after_path(path, input)
671        }
672    }
673
674    pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
675        if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
676            parse_meta_list_after_path(path, input).map(Meta::List)
677        } else if input.peek(Token![=]) {
678            parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
679        } else {
680            Ok(Meta::Path(path))
681        }
682    }
683
684    fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
685        let (delimiter, tokens) = mac::parse_delimiter(input)?;
686        Ok(MetaList {
687            path,
688            delimiter,
689            tokens,
690        })
691    }
692
693    fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
694        let eq_token: Token![=] = input.parse()?;
695        let ahead = input.fork();
696        let lit: Option<Lit> = ahead.parse()?;
697        let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
698            input.advance_to(&ahead);
699            Expr::Lit(ExprLit {
700                attrs: Vec::new(),
701                lit,
702            })
703        } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
704            return Err(input.error("unexpected attribute inside of attribute"));
705        } else {
706            input.parse()?
707        };
708        Ok(MetaNameValue {
709            path,
710            eq_token,
711            value,
712        })
713    }
714
715    pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle);
716
717    impl<'a> Display for DisplayAttrStyle<'a> {
718        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
719            formatter.write_str(match self.0 {
720                AttrStyle::Outer => "#",
721                AttrStyle::Inner(_) => "#!",
722            })
723        }
724    }
725
726    pub(super) struct DisplayPath<'a>(pub &'a Path);
727
728    impl<'a> Display for DisplayPath<'a> {
729        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
730            for (i, segment) in self.0.segments.iter().enumerate() {
731                if i > 0 || self.0.leading_colon.is_some() {
732                    formatter.write_str("::")?;
733                }
734                write!(formatter, "{}", segment.ident)?;
735            }
736            Ok(())
737        }
738    }
739}
740
741#[cfg(feature = "printing")]
742mod printing {
743    use super::*;
744    use proc_macro2::TokenStream;
745    use quote::ToTokens;
746
747    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
748    impl ToTokens for Attribute {
749        fn to_tokens(&self, tokens: &mut TokenStream) {
750            self.pound_token.to_tokens(tokens);
751            if let AttrStyle::Inner(b) = &self.style {
752                b.to_tokens(tokens);
753            }
754            self.bracket_token.surround(tokens, |tokens| {
755                self.meta.to_tokens(tokens);
756            });
757        }
758    }
759
760    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
761    impl ToTokens for MetaList {
762        fn to_tokens(&self, tokens: &mut TokenStream) {
763            self.path.to_tokens(tokens);
764            self.delimiter.surround(tokens, self.tokens.clone());
765        }
766    }
767
768    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
769    impl ToTokens for MetaNameValue {
770        fn to_tokens(&self, tokens: &mut TokenStream) {
771            self.path.to_tokens(tokens);
772            self.eq_token.to_tokens(tokens);
773            self.value.to_tokens(tokens);
774        }
775    }
776}
777