Skip to main content

syn/
attr.rs

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