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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
445    pub enum AttrStyle {
446        Outer,
447        Inner(Token![!]),
448    }
449}
450
451ast_enum! {
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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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(docsrs, 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
628impl From<Path> for Meta {
629    fn from(meta: Path) -> Meta {
630        Meta::Path(meta)
631    }
632}
633
634impl From<MetaList> for Meta {
635    fn from(meta: MetaList) -> Meta {
636        Meta::List(meta)
637    }
638}
639
640impl From<MetaNameValue> for Meta {
641    fn from(meta: MetaNameValue) -> Meta {
642        Meta::NameValue(meta)
643    }
644}
645
646#[cfg(feature = "parsing")]
647pub(crate) mod parsing {
648    use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
649    use crate::error::Result;
650    use crate::expr::{Expr, ExprLit};
651    use crate::lit::Lit;
652    use crate::parse::discouraged::Speculative as _;
653    use crate::parse::{Parse, ParseStream};
654    use crate::path::Path;
655    use crate::{mac, token};
656    use proc_macro2::Ident;
657    use std::fmt::{self, Display};
658
659    pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
660        while input.peek(Token![#]) && input.peek2(Token![!]) {
661            attrs.push(input.call(single_parse_inner)?);
662        }
663        Ok(())
664    }
665
666    pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
667        let content;
668        Ok(Attribute {
669            pound_token: input.parse()?,
670            style: AttrStyle::Inner(input.parse()?),
671            bracket_token: bracketed!(content in input),
672            meta: content.parse()?,
673        })
674    }
675
676    pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
677        let content;
678        Ok(Attribute {
679            pound_token: input.parse()?,
680            style: AttrStyle::Outer,
681            bracket_token: bracketed!(content in input),
682            meta: content.parse()?,
683        })
684    }
685
686    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
687    impl Parse for Meta {
688        fn parse(input: ParseStream) -> Result<Self> {
689            let path = parse_outermost_meta_path(input)?;
690            parse_meta_after_path(path, input)
691        }
692    }
693
694    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
695    impl Parse for MetaList {
696        fn parse(input: ParseStream) -> Result<Self> {
697            let path = parse_outermost_meta_path(input)?;
698            parse_meta_list_after_path(path, input)
699        }
700    }
701
702    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
703    impl Parse for MetaNameValue {
704        fn parse(input: ParseStream) -> Result<Self> {
705            let path = parse_outermost_meta_path(input)?;
706            parse_meta_name_value_after_path(path, input)
707        }
708    }
709
710    // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path,
711    // only the `unsafe` keyword is accepted as an attribute's outermost path.
712    fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> {
713        if input.peek(Token![unsafe]) {
714            let unsafe_token: Token![unsafe] = input.parse()?;
715            Ok(Path::from(Ident::new("unsafe", unsafe_token.span)))
716        } else {
717            Path::parse_mod_style(input)
718        }
719    }
720
721    pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
722        if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
723            parse_meta_list_after_path(path, input).map(Meta::List)
724        } else if input.peek(Token![=]) {
725            parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
726        } else {
727            Ok(Meta::Path(path))
728        }
729    }
730
731    fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
732        let (delimiter, tokens) = mac::parse_delimiter(input)?;
733        Ok(MetaList {
734            path,
735            delimiter,
736            tokens,
737        })
738    }
739
740    fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
741        let eq_token: Token![=] = input.parse()?;
742        let ahead = input.fork();
743        let lit: Option<Lit> = ahead.parse()?;
744        let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
745            input.advance_to(&ahead);
746            Expr::Lit(ExprLit {
747                attrs: Vec::new(),
748                lit,
749            })
750        } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
751            return Err(input.error("unexpected attribute inside of attribute"));
752        } else {
753            input.parse()?
754        };
755        Ok(MetaNameValue {
756            path,
757            eq_token,
758            value,
759        })
760    }
761
762    pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle);
763
764    impl<'a> Display for DisplayAttrStyle<'a> {
765        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
766            formatter.write_str(match self.0 {
767                AttrStyle::Outer => "#",
768                AttrStyle::Inner(_) => "#!",
769            })
770        }
771    }
772
773    pub(super) struct DisplayPath<'a>(pub &'a Path);
774
775    impl<'a> Display for DisplayPath<'a> {
776        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
777            for (i, segment) in self.0.segments.iter().enumerate() {
778                if i > 0 || self.0.leading_colon.is_some() {
779                    formatter.write_str("::")?;
780                }
781                write!(formatter, "{}", segment.ident)?;
782            }
783            Ok(())
784        }
785    }
786}
787
788#[cfg(feature = "printing")]
789mod printing {
790    use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
791    use crate::path;
792    use crate::path::printing::PathStyle;
793    use proc_macro2::TokenStream;
794    use quote::ToTokens;
795
796    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
797    impl ToTokens for Attribute {
798        fn to_tokens(&self, tokens: &mut TokenStream) {
799            self.pound_token.to_tokens(tokens);
800            if let AttrStyle::Inner(b) = &self.style {
801                b.to_tokens(tokens);
802            }
803            self.bracket_token.surround(tokens, |tokens| {
804                self.meta.to_tokens(tokens);
805            });
806        }
807    }
808
809    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
810    impl ToTokens for Meta {
811        fn to_tokens(&self, tokens: &mut TokenStream) {
812            match self {
813                Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod),
814                Meta::List(meta_list) => meta_list.to_tokens(tokens),
815                Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens),
816            }
817        }
818    }
819
820    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
821    impl ToTokens for MetaList {
822        fn to_tokens(&self, tokens: &mut TokenStream) {
823            path::printing::print_path(tokens, &self.path, PathStyle::Mod);
824            self.delimiter.surround(tokens, self.tokens.clone());
825        }
826    }
827
828    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
829    impl ToTokens for MetaNameValue {
830        fn to_tokens(&self, tokens: &mut TokenStream) {
831            path::printing::print_path(tokens, &self.path, PathStyle::Mod);
832            self.eq_token.to_tokens(tokens);
833            self.value.to_tokens(tokens);
834        }
835    }
836}