Skip to main content

syn/
attr.rs

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