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