xref: /linux/rust/syn/attr.rs (revision 784faa8eca8270671e0ed6d9d21f04bbb80fc5f7)
1*69942c0aSMiguel Ojeda // SPDX-License-Identifier: Apache-2.0 OR MIT
2*69942c0aSMiguel Ojeda 
3808c999fSMiguel Ojeda #[cfg(feature = "parsing")]
4808c999fSMiguel Ojeda use crate::error::Error;
5808c999fSMiguel Ojeda #[cfg(feature = "parsing")]
6808c999fSMiguel Ojeda use crate::error::Result;
7808c999fSMiguel Ojeda use crate::expr::Expr;
8808c999fSMiguel Ojeda use crate::mac::MacroDelimiter;
9808c999fSMiguel Ojeda #[cfg(feature = "parsing")]
10808c999fSMiguel Ojeda use crate::meta::{self, ParseNestedMeta};
11808c999fSMiguel Ojeda #[cfg(feature = "parsing")]
12808c999fSMiguel Ojeda use crate::parse::{Parse, ParseStream, Parser};
13808c999fSMiguel Ojeda use crate::path::Path;
14808c999fSMiguel Ojeda use crate::token;
15808c999fSMiguel Ojeda use proc_macro2::TokenStream;
16808c999fSMiguel Ojeda #[cfg(feature = "printing")]
17808c999fSMiguel Ojeda use std::iter;
18808c999fSMiguel Ojeda #[cfg(feature = "printing")]
19808c999fSMiguel Ojeda use std::slice;
20808c999fSMiguel Ojeda 
21808c999fSMiguel Ojeda ast_struct! {
22808c999fSMiguel Ojeda     /// An attribute, like `#[repr(transparent)]`.
23808c999fSMiguel Ojeda     ///
24808c999fSMiguel Ojeda     /// <br>
25808c999fSMiguel Ojeda     ///
26808c999fSMiguel Ojeda     /// # Syntax
27808c999fSMiguel Ojeda     ///
28808c999fSMiguel Ojeda     /// Rust has six types of attributes.
29808c999fSMiguel Ojeda     ///
30808c999fSMiguel Ojeda     /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
31808c999fSMiguel Ojeda     ///   in front of the item they describe.
32808c999fSMiguel Ojeda     ///
33808c999fSMiguel Ojeda     /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
34808c999fSMiguel Ojeda     ///   of the item they describe, usually a module.
35808c999fSMiguel Ojeda     ///
36808c999fSMiguel Ojeda     /// - Outer one-line doc comments like `/// Example`.
37808c999fSMiguel Ojeda     ///
38808c999fSMiguel Ojeda     /// - Inner one-line doc comments like `//! Please file an issue`.
39808c999fSMiguel Ojeda     ///
40808c999fSMiguel Ojeda     /// - Outer documentation blocks `/** Example */`.
41808c999fSMiguel Ojeda     ///
42808c999fSMiguel Ojeda     /// - Inner documentation blocks `/*! Please file an issue */`.
43808c999fSMiguel Ojeda     ///
44808c999fSMiguel Ojeda     /// The `style` field of type `AttrStyle` distinguishes whether an attribute
45808c999fSMiguel Ojeda     /// is outer or inner.
46808c999fSMiguel Ojeda     ///
47808c999fSMiguel Ojeda     /// Every attribute has a `path` that indicates the intended interpretation
48808c999fSMiguel Ojeda     /// of the rest of the attribute's contents. The path and the optional
49808c999fSMiguel Ojeda     /// additional contents are represented together in the `meta` field of the
50808c999fSMiguel Ojeda     /// attribute in three possible varieties:
51808c999fSMiguel Ojeda     ///
52808c999fSMiguel Ojeda     /// - Meta::Path &mdash; attributes whose information content conveys just a
53808c999fSMiguel Ojeda     ///   path, for example the `#[test]` attribute.
54808c999fSMiguel Ojeda     ///
55808c999fSMiguel Ojeda     /// - Meta::List &mdash; attributes that carry arbitrary tokens after the
56808c999fSMiguel Ojeda     ///   path, surrounded by a delimiter (parenthesis, bracket, or brace). For
57808c999fSMiguel Ojeda     ///   example `#[derive(Copy)]` or `#[precondition(x < 5)]`.
58808c999fSMiguel Ojeda     ///
59808c999fSMiguel Ojeda     /// - Meta::NameValue &mdash; attributes with an `=` sign after the path,
60808c999fSMiguel Ojeda     ///   followed by a Rust expression. For example `#[path =
61808c999fSMiguel Ojeda     ///   "sys/windows.rs"]`.
62808c999fSMiguel Ojeda     ///
63808c999fSMiguel Ojeda     /// All doc comments are represented in the NameValue style with a path of
64808c999fSMiguel Ojeda     /// "doc", as this is how they are processed by the compiler and by
65808c999fSMiguel Ojeda     /// `macro_rules!` macros.
66808c999fSMiguel Ojeda     ///
67808c999fSMiguel Ojeda     /// ```text
68808c999fSMiguel Ojeda     /// #[derive(Copy, Clone)]
69808c999fSMiguel Ojeda     ///   ~~~~~~Path
70808c999fSMiguel Ojeda     ///   ^^^^^^^^^^^^^^^^^^^Meta::List
71808c999fSMiguel Ojeda     ///
72808c999fSMiguel Ojeda     /// #[path = "sys/windows.rs"]
73808c999fSMiguel Ojeda     ///   ~~~~Path
74808c999fSMiguel Ojeda     ///   ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue
75808c999fSMiguel Ojeda     ///
76808c999fSMiguel Ojeda     /// #[test]
77808c999fSMiguel Ojeda     ///   ^^^^Meta::Path
78808c999fSMiguel Ojeda     /// ```
79808c999fSMiguel Ojeda     ///
80808c999fSMiguel Ojeda     /// <br>
81808c999fSMiguel Ojeda     ///
82808c999fSMiguel Ojeda     /// # Parsing from tokens to Attribute
83808c999fSMiguel Ojeda     ///
84808c999fSMiguel Ojeda     /// This type does not implement the [`Parse`] trait and thus cannot be
85808c999fSMiguel Ojeda     /// parsed directly by [`ParseStream::parse`]. Instead use
86808c999fSMiguel Ojeda     /// [`ParseStream::call`] with one of the two parser functions
87808c999fSMiguel Ojeda     /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
88808c999fSMiguel Ojeda     /// which you intend to parse.
89808c999fSMiguel Ojeda     ///
90808c999fSMiguel Ojeda     /// [`Parse`]: crate::parse::Parse
91808c999fSMiguel Ojeda     /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse
92808c999fSMiguel Ojeda     /// [`ParseStream::call`]: crate::parse::ParseBuffer::call
93808c999fSMiguel Ojeda     ///
94808c999fSMiguel Ojeda     /// ```
95808c999fSMiguel Ojeda     /// use syn::{Attribute, Ident, Result, Token};
96808c999fSMiguel Ojeda     /// use syn::parse::{Parse, ParseStream};
97808c999fSMiguel Ojeda     ///
98808c999fSMiguel Ojeda     /// // Parses a unit struct with attributes.
99808c999fSMiguel Ojeda     /// //
100808c999fSMiguel Ojeda     /// //     #[path = "s.tmpl"]
101808c999fSMiguel Ojeda     /// //     struct S;
102808c999fSMiguel Ojeda     /// struct UnitStruct {
103808c999fSMiguel Ojeda     ///     attrs: Vec<Attribute>,
104808c999fSMiguel Ojeda     ///     struct_token: Token![struct],
105808c999fSMiguel Ojeda     ///     name: Ident,
106808c999fSMiguel Ojeda     ///     semi_token: Token![;],
107808c999fSMiguel Ojeda     /// }
108808c999fSMiguel Ojeda     ///
109808c999fSMiguel Ojeda     /// impl Parse for UnitStruct {
110808c999fSMiguel Ojeda     ///     fn parse(input: ParseStream) -> Result<Self> {
111808c999fSMiguel Ojeda     ///         Ok(UnitStruct {
112808c999fSMiguel Ojeda     ///             attrs: input.call(Attribute::parse_outer)?,
113808c999fSMiguel Ojeda     ///             struct_token: input.parse()?,
114808c999fSMiguel Ojeda     ///             name: input.parse()?,
115808c999fSMiguel Ojeda     ///             semi_token: input.parse()?,
116808c999fSMiguel Ojeda     ///         })
117808c999fSMiguel Ojeda     ///     }
118808c999fSMiguel Ojeda     /// }
119808c999fSMiguel Ojeda     /// ```
120808c999fSMiguel Ojeda     ///
121808c999fSMiguel Ojeda     /// <p><br></p>
122808c999fSMiguel Ojeda     ///
123808c999fSMiguel Ojeda     /// # Parsing from Attribute to structured arguments
124808c999fSMiguel Ojeda     ///
125808c999fSMiguel Ojeda     /// The grammar of attributes in Rust is very flexible, which makes the
126808c999fSMiguel Ojeda     /// syntax tree not that useful on its own. In particular, arguments of the
127808c999fSMiguel Ojeda     /// `Meta::List` variety of attribute are held in an arbitrary `tokens:
128808c999fSMiguel Ojeda     /// TokenStream`. Macros are expected to check the `path` of the attribute,
129808c999fSMiguel Ojeda     /// decide whether they recognize it, and then parse the remaining tokens
130808c999fSMiguel Ojeda     /// according to whatever grammar they wish to require for that kind of
131808c999fSMiguel Ojeda     /// attribute. Use [`parse_args()`] to parse those tokens into the expected
132808c999fSMiguel Ojeda     /// data structure.
133808c999fSMiguel Ojeda     ///
134808c999fSMiguel Ojeda     /// [`parse_args()`]: Attribute::parse_args
135808c999fSMiguel Ojeda     ///
136808c999fSMiguel Ojeda     /// <p><br></p>
137808c999fSMiguel Ojeda     ///
138808c999fSMiguel Ojeda     /// # Doc comments
139808c999fSMiguel Ojeda     ///
140808c999fSMiguel Ojeda     /// The compiler transforms doc comments, such as `/// comment` and `/*!
141808c999fSMiguel Ojeda     /// comment */`, into attributes before macros are expanded. Each comment is
142808c999fSMiguel Ojeda     /// expanded into an attribute of the form `#[doc = r"comment"]`.
143808c999fSMiguel Ojeda     ///
144808c999fSMiguel Ojeda     /// As an example, the following `mod` items are expanded identically:
145808c999fSMiguel Ojeda     ///
146808c999fSMiguel Ojeda     /// ```
147808c999fSMiguel Ojeda     /// # use syn::{ItemMod, parse_quote};
148808c999fSMiguel Ojeda     /// let doc: ItemMod = parse_quote! {
149808c999fSMiguel Ojeda     ///     /// Single line doc comments
150808c999fSMiguel Ojeda     ///     /// We write so many!
151808c999fSMiguel Ojeda     ///     /**
152808c999fSMiguel Ojeda     ///      * Multi-line comments...
153808c999fSMiguel Ojeda     ///      * May span many lines
154808c999fSMiguel Ojeda     ///      */
155808c999fSMiguel Ojeda     ///     mod example {
156808c999fSMiguel Ojeda     ///         //! Of course, they can be inner too
157808c999fSMiguel Ojeda     ///         /*! And fit in a single line */
158808c999fSMiguel Ojeda     ///     }
159808c999fSMiguel Ojeda     /// };
160808c999fSMiguel Ojeda     /// let attr: ItemMod = parse_quote! {
161808c999fSMiguel Ojeda     ///     #[doc = r" Single line doc comments"]
162808c999fSMiguel Ojeda     ///     #[doc = r" We write so many!"]
163808c999fSMiguel Ojeda     ///     #[doc = r"
164808c999fSMiguel Ojeda     ///      * Multi-line comments...
165808c999fSMiguel Ojeda     ///      * May span many lines
166808c999fSMiguel Ojeda     ///      "]
167808c999fSMiguel Ojeda     ///     mod example {
168808c999fSMiguel Ojeda     ///         #![doc = r" Of course, they can be inner too"]
169808c999fSMiguel Ojeda     ///         #![doc = r" And fit in a single line "]
170808c999fSMiguel Ojeda     ///     }
171808c999fSMiguel Ojeda     /// };
172808c999fSMiguel Ojeda     /// assert_eq!(doc, attr);
173808c999fSMiguel Ojeda     /// ```
174808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
175808c999fSMiguel Ojeda     pub struct Attribute {
176808c999fSMiguel Ojeda         pub pound_token: Token![#],
177808c999fSMiguel Ojeda         pub style: AttrStyle,
178808c999fSMiguel Ojeda         pub bracket_token: token::Bracket,
179808c999fSMiguel Ojeda         pub meta: Meta,
180808c999fSMiguel Ojeda     }
181808c999fSMiguel Ojeda }
182808c999fSMiguel Ojeda 
183808c999fSMiguel Ojeda impl Attribute {
184808c999fSMiguel Ojeda     /// Returns the path that identifies the interpretation of this attribute.
185808c999fSMiguel Ojeda     ///
186808c999fSMiguel Ojeda     /// For example this would return the `test` in `#[test]`, the `derive` in
187808c999fSMiguel Ojeda     /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
path(&self) -> &Path188808c999fSMiguel Ojeda     pub fn path(&self) -> &Path {
189808c999fSMiguel Ojeda         self.meta.path()
190808c999fSMiguel Ojeda     }
191808c999fSMiguel Ojeda 
192808c999fSMiguel Ojeda     /// Parse the arguments to the attribute as a syntax tree.
193808c999fSMiguel Ojeda     ///
194808c999fSMiguel Ojeda     /// This is similar to pulling out the `TokenStream` from `Meta::List` and
195808c999fSMiguel Ojeda     /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using
196808c999fSMiguel Ojeda     /// `parse_args` the error message has a more useful span when `tokens` is
197808c999fSMiguel Ojeda     /// empty.
198808c999fSMiguel Ojeda     ///
199808c999fSMiguel Ojeda     /// The surrounding delimiters are *not* included in the input to the
200808c999fSMiguel Ojeda     /// parser.
201808c999fSMiguel Ojeda     ///
202808c999fSMiguel Ojeda     /// ```text
203808c999fSMiguel Ojeda     /// #[my_attr(value < 5)]
204808c999fSMiguel Ojeda     ///           ^^^^^^^^^ what gets parsed
205808c999fSMiguel Ojeda     /// ```
206808c999fSMiguel Ojeda     ///
207808c999fSMiguel Ojeda     /// # Example
208808c999fSMiguel Ojeda     ///
209808c999fSMiguel Ojeda     /// ```
210808c999fSMiguel Ojeda     /// use syn::{parse_quote, Attribute, Expr};
211808c999fSMiguel Ojeda     ///
212808c999fSMiguel Ojeda     /// let attr: Attribute = parse_quote! {
213808c999fSMiguel Ojeda     ///     #[precondition(value < 5)]
214808c999fSMiguel Ojeda     /// };
215808c999fSMiguel Ojeda     ///
216808c999fSMiguel Ojeda     /// if attr.path().is_ident("precondition") {
217808c999fSMiguel Ojeda     ///     let precondition: Expr = attr.parse_args()?;
218808c999fSMiguel Ojeda     ///     // ...
219808c999fSMiguel Ojeda     /// }
220808c999fSMiguel Ojeda     /// # anyhow::Ok(())
221808c999fSMiguel Ojeda     /// ```
222808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
223808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_args<T: Parse>(&self) -> Result<T>224808c999fSMiguel Ojeda     pub fn parse_args<T: Parse>(&self) -> Result<T> {
225808c999fSMiguel Ojeda         self.parse_args_with(T::parse)
226808c999fSMiguel Ojeda     }
227808c999fSMiguel Ojeda 
228808c999fSMiguel Ojeda     /// Parse the arguments to the attribute using the given parser.
229808c999fSMiguel Ojeda     ///
230808c999fSMiguel Ojeda     /// # Example
231808c999fSMiguel Ojeda     ///
232808c999fSMiguel Ojeda     /// ```
233808c999fSMiguel Ojeda     /// use syn::{parse_quote, Attribute};
234808c999fSMiguel Ojeda     ///
235808c999fSMiguel Ojeda     /// let attr: Attribute = parse_quote! {
236808c999fSMiguel Ojeda     ///     #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }]
237808c999fSMiguel Ojeda     /// };
238808c999fSMiguel Ojeda     ///
239808c999fSMiguel Ojeda     /// let bwom = attr.parse_args_with(Attribute::parse_outer)?;
240808c999fSMiguel Ojeda     ///
241808c999fSMiguel Ojeda     /// // Attribute does not have a Parse impl, so we couldn't directly do:
242808c999fSMiguel Ojeda     /// // let bwom: Attribute = attr.parse_args()?;
243808c999fSMiguel Ojeda     /// # anyhow::Ok(())
244808c999fSMiguel Ojeda     /// ```
245808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
246808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>247808c999fSMiguel Ojeda     pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
248808c999fSMiguel Ojeda         match &self.meta {
249808c999fSMiguel Ojeda             Meta::Path(path) => Err(crate::error::new2(
250808c999fSMiguel Ojeda                 path.segments.first().unwrap().ident.span(),
251808c999fSMiguel Ojeda                 path.segments.last().unwrap().ident.span(),
252808c999fSMiguel Ojeda                 format!(
253808c999fSMiguel Ojeda                     "expected attribute arguments in parentheses: {}[{}(...)]",
254808c999fSMiguel Ojeda                     parsing::DisplayAttrStyle(&self.style),
255808c999fSMiguel Ojeda                     parsing::DisplayPath(path),
256808c999fSMiguel Ojeda                 ),
257808c999fSMiguel Ojeda             )),
258808c999fSMiguel Ojeda             Meta::NameValue(meta) => Err(Error::new(
259808c999fSMiguel Ojeda                 meta.eq_token.span,
260808c999fSMiguel Ojeda                 format_args!(
261808c999fSMiguel Ojeda                     "expected parentheses: {}[{}(...)]",
262808c999fSMiguel Ojeda                     parsing::DisplayAttrStyle(&self.style),
263808c999fSMiguel Ojeda                     parsing::DisplayPath(&meta.path),
264808c999fSMiguel Ojeda                 ),
265808c999fSMiguel Ojeda             )),
266808c999fSMiguel Ojeda             Meta::List(meta) => meta.parse_args_with(parser),
267808c999fSMiguel Ojeda         }
268808c999fSMiguel Ojeda     }
269808c999fSMiguel Ojeda 
270808c999fSMiguel Ojeda     /// Parse the arguments to the attribute, expecting it to follow the
271808c999fSMiguel Ojeda     /// conventional structure used by most of Rust's built-in attributes.
272808c999fSMiguel Ojeda     ///
273808c999fSMiguel Ojeda     /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference
274808c999fSMiguel Ojeda     /// explains the convention in more detail. Not all attributes follow this
275808c999fSMiguel Ojeda     /// convention, so [`parse_args()`][Self::parse_args] is available if you
276808c999fSMiguel Ojeda     /// need to parse arbitrarily goofy attribute syntax.
277808c999fSMiguel Ojeda     ///
278808c999fSMiguel Ojeda     /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax
279808c999fSMiguel Ojeda     ///
280808c999fSMiguel Ojeda     /// # Example
281808c999fSMiguel Ojeda     ///
282808c999fSMiguel Ojeda     /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute
283808c999fSMiguel Ojeda     /// syntax.
284808c999fSMiguel Ojeda     ///
285808c999fSMiguel Ojeda     /// ```
286808c999fSMiguel Ojeda     /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt};
287808c999fSMiguel Ojeda     ///
288808c999fSMiguel Ojeda     /// let input: ItemStruct = parse_quote! {
289808c999fSMiguel Ojeda     ///     #[repr(C, align(4))]
290808c999fSMiguel Ojeda     ///     pub struct MyStruct(u16, u32);
291808c999fSMiguel Ojeda     /// };
292808c999fSMiguel Ojeda     ///
293808c999fSMiguel Ojeda     /// let mut repr_c = false;
294808c999fSMiguel Ojeda     /// let mut repr_transparent = false;
295808c999fSMiguel Ojeda     /// let mut repr_align = None::<usize>;
296808c999fSMiguel Ojeda     /// let mut repr_packed = None::<usize>;
297808c999fSMiguel Ojeda     /// for attr in &input.attrs {
298808c999fSMiguel Ojeda     ///     if attr.path().is_ident("repr") {
299808c999fSMiguel Ojeda     ///         attr.parse_nested_meta(|meta| {
300808c999fSMiguel Ojeda     ///             // #[repr(C)]
301808c999fSMiguel Ojeda     ///             if meta.path.is_ident("C") {
302808c999fSMiguel Ojeda     ///                 repr_c = true;
303808c999fSMiguel Ojeda     ///                 return Ok(());
304808c999fSMiguel Ojeda     ///             }
305808c999fSMiguel Ojeda     ///
306808c999fSMiguel Ojeda     ///             // #[repr(transparent)]
307808c999fSMiguel Ojeda     ///             if meta.path.is_ident("transparent") {
308808c999fSMiguel Ojeda     ///                 repr_transparent = true;
309808c999fSMiguel Ojeda     ///                 return Ok(());
310808c999fSMiguel Ojeda     ///             }
311808c999fSMiguel Ojeda     ///
312808c999fSMiguel Ojeda     ///             // #[repr(align(N))]
313808c999fSMiguel Ojeda     ///             if meta.path.is_ident("align") {
314808c999fSMiguel Ojeda     ///                 let content;
315808c999fSMiguel Ojeda     ///                 parenthesized!(content in meta.input);
316808c999fSMiguel Ojeda     ///                 let lit: LitInt = content.parse()?;
317808c999fSMiguel Ojeda     ///                 let n: usize = lit.base10_parse()?;
318808c999fSMiguel Ojeda     ///                 repr_align = Some(n);
319808c999fSMiguel Ojeda     ///                 return Ok(());
320808c999fSMiguel Ojeda     ///             }
321808c999fSMiguel Ojeda     ///
322808c999fSMiguel Ojeda     ///             // #[repr(packed)] or #[repr(packed(N))], omitted N means 1
323808c999fSMiguel Ojeda     ///             if meta.path.is_ident("packed") {
324808c999fSMiguel Ojeda     ///                 if meta.input.peek(token::Paren) {
325808c999fSMiguel Ojeda     ///                     let content;
326808c999fSMiguel Ojeda     ///                     parenthesized!(content in meta.input);
327808c999fSMiguel Ojeda     ///                     let lit: LitInt = content.parse()?;
328808c999fSMiguel Ojeda     ///                     let n: usize = lit.base10_parse()?;
329808c999fSMiguel Ojeda     ///                     repr_packed = Some(n);
330808c999fSMiguel Ojeda     ///                 } else {
331808c999fSMiguel Ojeda     ///                     repr_packed = Some(1);
332808c999fSMiguel Ojeda     ///                 }
333808c999fSMiguel Ojeda     ///                 return Ok(());
334808c999fSMiguel Ojeda     ///             }
335808c999fSMiguel Ojeda     ///
336808c999fSMiguel Ojeda     ///             Err(meta.error("unrecognized repr"))
337808c999fSMiguel Ojeda     ///         })?;
338808c999fSMiguel Ojeda     ///     }
339808c999fSMiguel Ojeda     /// }
340808c999fSMiguel Ojeda     /// # anyhow::Ok(())
341808c999fSMiguel Ojeda     /// ```
342808c999fSMiguel Ojeda     ///
343808c999fSMiguel Ojeda     /// # Alternatives
344808c999fSMiguel Ojeda     ///
345808c999fSMiguel Ojeda     /// In some cases, for attributes which have nested layers of structured
346808c999fSMiguel Ojeda     /// content, the following less flexible approach might be more convenient:
347808c999fSMiguel Ojeda     ///
348808c999fSMiguel Ojeda     /// ```
349808c999fSMiguel Ojeda     /// # use syn::{parse_quote, ItemStruct};
350808c999fSMiguel Ojeda     /// #
351808c999fSMiguel Ojeda     /// # let input: ItemStruct = parse_quote! {
352808c999fSMiguel Ojeda     /// #     #[repr(C, align(4))]
353808c999fSMiguel Ojeda     /// #     pub struct MyStruct(u16, u32);
354808c999fSMiguel Ojeda     /// # };
355808c999fSMiguel Ojeda     /// #
356808c999fSMiguel Ojeda     /// use syn::punctuated::Punctuated;
357808c999fSMiguel Ojeda     /// use syn::{parenthesized, token, Error, LitInt, Meta, Token};
358808c999fSMiguel Ojeda     ///
359808c999fSMiguel Ojeda     /// let mut repr_c = false;
360808c999fSMiguel Ojeda     /// let mut repr_transparent = false;
361808c999fSMiguel Ojeda     /// let mut repr_align = None::<usize>;
362808c999fSMiguel Ojeda     /// let mut repr_packed = None::<usize>;
363808c999fSMiguel Ojeda     /// for attr in &input.attrs {
364808c999fSMiguel Ojeda     ///     if attr.path().is_ident("repr") {
365808c999fSMiguel Ojeda     ///         let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
366808c999fSMiguel Ojeda     ///         for meta in nested {
367808c999fSMiguel Ojeda     ///             match meta {
368808c999fSMiguel Ojeda     ///                 // #[repr(C)]
369808c999fSMiguel Ojeda     ///                 Meta::Path(path) if path.is_ident("C") => {
370808c999fSMiguel Ojeda     ///                     repr_c = true;
371808c999fSMiguel Ojeda     ///                 }
372808c999fSMiguel Ojeda     ///
373808c999fSMiguel Ojeda     ///                 // #[repr(align(N))]
374808c999fSMiguel Ojeda     ///                 Meta::List(meta) if meta.path.is_ident("align") => {
375808c999fSMiguel Ojeda     ///                     let lit: LitInt = meta.parse_args()?;
376808c999fSMiguel Ojeda     ///                     let n: usize = lit.base10_parse()?;
377808c999fSMiguel Ojeda     ///                     repr_align = Some(n);
378808c999fSMiguel Ojeda     ///                 }
379808c999fSMiguel Ojeda     ///
380808c999fSMiguel Ojeda     ///                 /* ... */
381808c999fSMiguel Ojeda     ///
382808c999fSMiguel Ojeda     ///                 _ => {
383808c999fSMiguel Ojeda     ///                     return Err(Error::new_spanned(meta, "unrecognized repr"));
384808c999fSMiguel Ojeda     ///                 }
385808c999fSMiguel Ojeda     ///             }
386808c999fSMiguel Ojeda     ///         }
387808c999fSMiguel Ojeda     ///     }
388808c999fSMiguel Ojeda     /// }
389808c999fSMiguel Ojeda     /// # Ok(())
390808c999fSMiguel Ojeda     /// ```
391808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
392808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>393808c999fSMiguel Ojeda     pub fn parse_nested_meta(
394808c999fSMiguel Ojeda         &self,
395808c999fSMiguel Ojeda         logic: impl FnMut(ParseNestedMeta) -> Result<()>,
396808c999fSMiguel Ojeda     ) -> Result<()> {
397808c999fSMiguel Ojeda         self.parse_args_with(meta::parser(logic))
398808c999fSMiguel Ojeda     }
399808c999fSMiguel Ojeda 
400808c999fSMiguel Ojeda     /// Parses zero or more outer attributes from the stream.
401808c999fSMiguel Ojeda     ///
402808c999fSMiguel Ojeda     /// # Example
403808c999fSMiguel Ojeda     ///
404808c999fSMiguel Ojeda     /// See
405808c999fSMiguel Ojeda     /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
406808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
407808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_outer(input: ParseStream) -> Result<Vec<Self>>408808c999fSMiguel Ojeda     pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
409808c999fSMiguel Ojeda         let mut attrs = Vec::new();
410808c999fSMiguel Ojeda         while input.peek(Token![#]) {
411808c999fSMiguel Ojeda             attrs.push(input.call(parsing::single_parse_outer)?);
412808c999fSMiguel Ojeda         }
413808c999fSMiguel Ojeda         Ok(attrs)
414808c999fSMiguel Ojeda     }
415808c999fSMiguel Ojeda 
416808c999fSMiguel Ojeda     /// Parses zero or more inner attributes from the stream.
417808c999fSMiguel Ojeda     ///
418808c999fSMiguel Ojeda     /// # Example
419808c999fSMiguel Ojeda     ///
420808c999fSMiguel Ojeda     /// See
421808c999fSMiguel Ojeda     /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
422808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
423808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_inner(input: ParseStream) -> Result<Vec<Self>>424808c999fSMiguel Ojeda     pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
425808c999fSMiguel Ojeda         let mut attrs = Vec::new();
426808c999fSMiguel Ojeda         parsing::parse_inner(input, &mut attrs)?;
427808c999fSMiguel Ojeda         Ok(attrs)
428808c999fSMiguel Ojeda     }
429808c999fSMiguel Ojeda }
430808c999fSMiguel Ojeda 
431808c999fSMiguel Ojeda ast_enum! {
432808c999fSMiguel Ojeda     /// Distinguishes between attributes that decorate an item and attributes
433808c999fSMiguel Ojeda     /// that are contained within an item.
434808c999fSMiguel Ojeda     ///
435808c999fSMiguel Ojeda     /// # Outer attributes
436808c999fSMiguel Ojeda     ///
437808c999fSMiguel Ojeda     /// - `#[repr(transparent)]`
438808c999fSMiguel Ojeda     /// - `/// # Example`
439808c999fSMiguel Ojeda     /// - `/** Please file an issue */`
440808c999fSMiguel Ojeda     ///
441808c999fSMiguel Ojeda     /// # Inner attributes
442808c999fSMiguel Ojeda     ///
443808c999fSMiguel Ojeda     /// - `#![feature(proc_macro)]`
444808c999fSMiguel Ojeda     /// - `//! # Example`
445808c999fSMiguel Ojeda     /// - `/*! Please file an issue */`
446808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
447808c999fSMiguel Ojeda     pub enum AttrStyle {
448808c999fSMiguel Ojeda         Outer,
449808c999fSMiguel Ojeda         Inner(Token![!]),
450808c999fSMiguel Ojeda     }
451808c999fSMiguel Ojeda }
452808c999fSMiguel Ojeda 
453808c999fSMiguel Ojeda ast_enum! {
454808c999fSMiguel Ojeda     /// Content of a compile-time structured attribute.
455808c999fSMiguel Ojeda     ///
456808c999fSMiguel Ojeda     /// ## Path
457808c999fSMiguel Ojeda     ///
458808c999fSMiguel Ojeda     /// A meta path is like the `test` in `#[test]`.
459808c999fSMiguel Ojeda     ///
460808c999fSMiguel Ojeda     /// ## List
461808c999fSMiguel Ojeda     ///
462808c999fSMiguel Ojeda     /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
463808c999fSMiguel Ojeda     ///
464808c999fSMiguel Ojeda     /// ## NameValue
465808c999fSMiguel Ojeda     ///
466808c999fSMiguel Ojeda     /// A name-value meta is like the `path = "..."` in `#[path =
467808c999fSMiguel Ojeda     /// "sys/windows.rs"]`.
468808c999fSMiguel Ojeda     ///
469808c999fSMiguel Ojeda     /// # Syntax tree enum
470808c999fSMiguel Ojeda     ///
471808c999fSMiguel Ojeda     /// This type is a [syntax tree enum].
472808c999fSMiguel Ojeda     ///
473808c999fSMiguel Ojeda     /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums
474808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
475808c999fSMiguel Ojeda     pub enum Meta {
476808c999fSMiguel Ojeda         Path(Path),
477808c999fSMiguel Ojeda 
478808c999fSMiguel Ojeda         /// A structured list within an attribute, like `derive(Copy, Clone)`.
479808c999fSMiguel Ojeda         List(MetaList),
480808c999fSMiguel Ojeda 
481808c999fSMiguel Ojeda         /// A name-value pair within an attribute, like `feature = "nightly"`.
482808c999fSMiguel Ojeda         NameValue(MetaNameValue),
483808c999fSMiguel Ojeda     }
484808c999fSMiguel Ojeda }
485808c999fSMiguel Ojeda 
486808c999fSMiguel Ojeda ast_struct! {
487808c999fSMiguel Ojeda     /// A structured list within an attribute, like `derive(Copy, Clone)`.
488808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
489808c999fSMiguel Ojeda     pub struct MetaList {
490808c999fSMiguel Ojeda         pub path: Path,
491808c999fSMiguel Ojeda         pub delimiter: MacroDelimiter,
492808c999fSMiguel Ojeda         pub tokens: TokenStream,
493808c999fSMiguel Ojeda     }
494808c999fSMiguel Ojeda }
495808c999fSMiguel Ojeda 
496808c999fSMiguel Ojeda ast_struct! {
497808c999fSMiguel Ojeda     /// A name-value pair within an attribute, like `feature = "nightly"`.
498808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
499808c999fSMiguel Ojeda     pub struct MetaNameValue {
500808c999fSMiguel Ojeda         pub path: Path,
501808c999fSMiguel Ojeda         pub eq_token: Token![=],
502808c999fSMiguel Ojeda         pub value: Expr,
503808c999fSMiguel Ojeda     }
504808c999fSMiguel Ojeda }
505808c999fSMiguel Ojeda 
506808c999fSMiguel Ojeda impl Meta {
507808c999fSMiguel Ojeda     /// Returns the path that begins this structured meta item.
508808c999fSMiguel Ojeda     ///
509808c999fSMiguel Ojeda     /// For example this would return the `test` in `#[test]`, the `derive` in
510808c999fSMiguel Ojeda     /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
path(&self) -> &Path511808c999fSMiguel Ojeda     pub fn path(&self) -> &Path {
512808c999fSMiguel Ojeda         match self {
513808c999fSMiguel Ojeda             Meta::Path(path) => path,
514808c999fSMiguel Ojeda             Meta::List(meta) => &meta.path,
515808c999fSMiguel Ojeda             Meta::NameValue(meta) => &meta.path,
516808c999fSMiguel Ojeda         }
517808c999fSMiguel Ojeda     }
518808c999fSMiguel Ojeda 
519808c999fSMiguel Ojeda     /// Error if this is a `Meta::List` or `Meta::NameValue`.
520808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
521808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
require_path_only(&self) -> Result<&Path>522808c999fSMiguel Ojeda     pub fn require_path_only(&self) -> Result<&Path> {
523808c999fSMiguel Ojeda         let error_span = match self {
524808c999fSMiguel Ojeda             Meta::Path(path) => return Ok(path),
525808c999fSMiguel Ojeda             Meta::List(meta) => meta.delimiter.span().open(),
526808c999fSMiguel Ojeda             Meta::NameValue(meta) => meta.eq_token.span,
527808c999fSMiguel Ojeda         };
528808c999fSMiguel Ojeda         Err(Error::new(error_span, "unexpected token in attribute"))
529808c999fSMiguel Ojeda     }
530808c999fSMiguel Ojeda 
531808c999fSMiguel Ojeda     /// Error if this is a `Meta::Path` or `Meta::NameValue`.
532808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
533808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
require_list(&self) -> Result<&MetaList>534808c999fSMiguel Ojeda     pub fn require_list(&self) -> Result<&MetaList> {
535808c999fSMiguel Ojeda         match self {
536808c999fSMiguel Ojeda             Meta::List(meta) => Ok(meta),
537808c999fSMiguel Ojeda             Meta::Path(path) => Err(crate::error::new2(
538808c999fSMiguel Ojeda                 path.segments.first().unwrap().ident.span(),
539808c999fSMiguel Ojeda                 path.segments.last().unwrap().ident.span(),
540808c999fSMiguel Ojeda                 format!(
541808c999fSMiguel Ojeda                     "expected attribute arguments in parentheses: `{}(...)`",
542808c999fSMiguel Ojeda                     parsing::DisplayPath(path),
543808c999fSMiguel Ojeda                 ),
544808c999fSMiguel Ojeda             )),
545808c999fSMiguel Ojeda             Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")),
546808c999fSMiguel Ojeda         }
547808c999fSMiguel Ojeda     }
548808c999fSMiguel Ojeda 
549808c999fSMiguel Ojeda     /// Error if this is a `Meta::Path` or `Meta::List`.
550808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
551808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
require_name_value(&self) -> Result<&MetaNameValue>552808c999fSMiguel Ojeda     pub fn require_name_value(&self) -> Result<&MetaNameValue> {
553808c999fSMiguel Ojeda         match self {
554808c999fSMiguel Ojeda             Meta::NameValue(meta) => Ok(meta),
555808c999fSMiguel Ojeda             Meta::Path(path) => Err(crate::error::new2(
556808c999fSMiguel Ojeda                 path.segments.first().unwrap().ident.span(),
557808c999fSMiguel Ojeda                 path.segments.last().unwrap().ident.span(),
558808c999fSMiguel Ojeda                 format!(
559808c999fSMiguel Ojeda                     "expected a value for this attribute: `{} = ...`",
560808c999fSMiguel Ojeda                     parsing::DisplayPath(path),
561808c999fSMiguel Ojeda                 ),
562808c999fSMiguel Ojeda             )),
563808c999fSMiguel Ojeda             Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")),
564808c999fSMiguel Ojeda         }
565808c999fSMiguel Ojeda     }
566808c999fSMiguel Ojeda }
567808c999fSMiguel Ojeda 
568808c999fSMiguel Ojeda impl MetaList {
569808c999fSMiguel Ojeda     /// See [`Attribute::parse_args`].
570808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
571808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_args<T: Parse>(&self) -> Result<T>572808c999fSMiguel Ojeda     pub fn parse_args<T: Parse>(&self) -> Result<T> {
573808c999fSMiguel Ojeda         self.parse_args_with(T::parse)
574808c999fSMiguel Ojeda     }
575808c999fSMiguel Ojeda 
576808c999fSMiguel Ojeda     /// See [`Attribute::parse_args_with`].
577808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
578808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>579808c999fSMiguel Ojeda     pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
580808c999fSMiguel Ojeda         let scope = self.delimiter.span().close();
581808c999fSMiguel Ojeda         crate::parse::parse_scoped(parser, scope, self.tokens.clone())
582808c999fSMiguel Ojeda     }
583808c999fSMiguel Ojeda 
584808c999fSMiguel Ojeda     /// See [`Attribute::parse_nested_meta`].
585808c999fSMiguel Ojeda     #[cfg(feature = "parsing")]
586808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>587808c999fSMiguel Ojeda     pub fn parse_nested_meta(
588808c999fSMiguel Ojeda         &self,
589808c999fSMiguel Ojeda         logic: impl FnMut(ParseNestedMeta) -> Result<()>,
590808c999fSMiguel Ojeda     ) -> Result<()> {
591808c999fSMiguel Ojeda         self.parse_args_with(meta::parser(logic))
592808c999fSMiguel Ojeda     }
593808c999fSMiguel Ojeda }
594808c999fSMiguel Ojeda 
595808c999fSMiguel Ojeda #[cfg(feature = "printing")]
596808c999fSMiguel Ojeda pub(crate) trait FilterAttrs<'a> {
597808c999fSMiguel Ojeda     type Ret: Iterator<Item = &'a Attribute>;
598808c999fSMiguel Ojeda 
outer(self) -> Self::Ret599808c999fSMiguel Ojeda     fn outer(self) -> Self::Ret;
600808c999fSMiguel Ojeda     #[cfg(feature = "full")]
inner(self) -> Self::Ret601808c999fSMiguel Ojeda     fn inner(self) -> Self::Ret;
602808c999fSMiguel Ojeda }
603808c999fSMiguel Ojeda 
604808c999fSMiguel Ojeda #[cfg(feature = "printing")]
605808c999fSMiguel Ojeda impl<'a> FilterAttrs<'a> for &'a [Attribute] {
606808c999fSMiguel Ojeda     type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
607808c999fSMiguel Ojeda 
outer(self) -> Self::Ret608808c999fSMiguel Ojeda     fn outer(self) -> Self::Ret {
609808c999fSMiguel Ojeda         fn is_outer(attr: &&Attribute) -> bool {
610808c999fSMiguel Ojeda             match attr.style {
611808c999fSMiguel Ojeda                 AttrStyle::Outer => true,
612808c999fSMiguel Ojeda                 AttrStyle::Inner(_) => false,
613808c999fSMiguel Ojeda             }
614808c999fSMiguel Ojeda         }
615808c999fSMiguel Ojeda         self.iter().filter(is_outer)
616808c999fSMiguel Ojeda     }
617808c999fSMiguel Ojeda 
618808c999fSMiguel Ojeda     #[cfg(feature = "full")]
inner(self) -> Self::Ret619808c999fSMiguel Ojeda     fn inner(self) -> Self::Ret {
620808c999fSMiguel Ojeda         fn is_inner(attr: &&Attribute) -> bool {
621808c999fSMiguel Ojeda             match attr.style {
622808c999fSMiguel Ojeda                 AttrStyle::Inner(_) => true,
623808c999fSMiguel Ojeda                 AttrStyle::Outer => false,
624808c999fSMiguel Ojeda             }
625808c999fSMiguel Ojeda         }
626808c999fSMiguel Ojeda         self.iter().filter(is_inner)
627808c999fSMiguel Ojeda     }
628808c999fSMiguel Ojeda }
629808c999fSMiguel Ojeda 
630808c999fSMiguel Ojeda impl From<Path> for Meta {
from(meta: Path) -> Meta631808c999fSMiguel Ojeda     fn from(meta: Path) -> Meta {
632808c999fSMiguel Ojeda         Meta::Path(meta)
633808c999fSMiguel Ojeda     }
634808c999fSMiguel Ojeda }
635808c999fSMiguel Ojeda 
636808c999fSMiguel Ojeda impl From<MetaList> for Meta {
from(meta: MetaList) -> Meta637808c999fSMiguel Ojeda     fn from(meta: MetaList) -> Meta {
638808c999fSMiguel Ojeda         Meta::List(meta)
639808c999fSMiguel Ojeda     }
640808c999fSMiguel Ojeda }
641808c999fSMiguel Ojeda 
642808c999fSMiguel Ojeda impl From<MetaNameValue> for Meta {
from(meta: MetaNameValue) -> Meta643808c999fSMiguel Ojeda     fn from(meta: MetaNameValue) -> Meta {
644808c999fSMiguel Ojeda         Meta::NameValue(meta)
645808c999fSMiguel Ojeda     }
646808c999fSMiguel Ojeda }
647808c999fSMiguel Ojeda 
648808c999fSMiguel Ojeda #[cfg(feature = "parsing")]
649808c999fSMiguel Ojeda pub(crate) mod parsing {
650808c999fSMiguel Ojeda     use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
651808c999fSMiguel Ojeda     use crate::error::Result;
652808c999fSMiguel Ojeda     use crate::expr::{Expr, ExprLit};
653808c999fSMiguel Ojeda     use crate::lit::Lit;
654808c999fSMiguel Ojeda     use crate::parse::discouraged::Speculative as _;
655808c999fSMiguel Ojeda     use crate::parse::{Parse, ParseStream};
656808c999fSMiguel Ojeda     use crate::path::Path;
657808c999fSMiguel Ojeda     use crate::{mac, token};
658808c999fSMiguel Ojeda     use proc_macro2::Ident;
659808c999fSMiguel Ojeda     use std::fmt::{self, Display};
660808c999fSMiguel Ojeda 
parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()>661808c999fSMiguel Ojeda     pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
662808c999fSMiguel Ojeda         while input.peek(Token![#]) && input.peek2(Token![!]) {
663808c999fSMiguel Ojeda             attrs.push(input.call(single_parse_inner)?);
664808c999fSMiguel Ojeda         }
665808c999fSMiguel Ojeda         Ok(())
666808c999fSMiguel Ojeda     }
667808c999fSMiguel Ojeda 
single_parse_inner(input: ParseStream) -> Result<Attribute>668808c999fSMiguel Ojeda     pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
669808c999fSMiguel Ojeda         let content;
670808c999fSMiguel Ojeda         Ok(Attribute {
671808c999fSMiguel Ojeda             pound_token: input.parse()?,
672808c999fSMiguel Ojeda             style: AttrStyle::Inner(input.parse()?),
673808c999fSMiguel Ojeda             bracket_token: bracketed!(content in input),
674808c999fSMiguel Ojeda             meta: content.parse()?,
675808c999fSMiguel Ojeda         })
676808c999fSMiguel Ojeda     }
677808c999fSMiguel Ojeda 
single_parse_outer(input: ParseStream) -> Result<Attribute>678808c999fSMiguel Ojeda     pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
679808c999fSMiguel Ojeda         let content;
680808c999fSMiguel Ojeda         Ok(Attribute {
681808c999fSMiguel Ojeda             pound_token: input.parse()?,
682808c999fSMiguel Ojeda             style: AttrStyle::Outer,
683808c999fSMiguel Ojeda             bracket_token: bracketed!(content in input),
684808c999fSMiguel Ojeda             meta: content.parse()?,
685808c999fSMiguel Ojeda         })
686808c999fSMiguel Ojeda     }
687808c999fSMiguel Ojeda 
688808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
689808c999fSMiguel Ojeda     impl Parse for Meta {
parse(input: ParseStream) -> Result<Self>690808c999fSMiguel Ojeda         fn parse(input: ParseStream) -> Result<Self> {
691808c999fSMiguel Ojeda             let path = parse_outermost_meta_path(input)?;
692808c999fSMiguel Ojeda             parse_meta_after_path(path, input)
693808c999fSMiguel Ojeda         }
694808c999fSMiguel Ojeda     }
695808c999fSMiguel Ojeda 
696808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
697808c999fSMiguel Ojeda     impl Parse for MetaList {
parse(input: ParseStream) -> Result<Self>698808c999fSMiguel Ojeda         fn parse(input: ParseStream) -> Result<Self> {
699808c999fSMiguel Ojeda             let path = parse_outermost_meta_path(input)?;
700808c999fSMiguel Ojeda             parse_meta_list_after_path(path, input)
701808c999fSMiguel Ojeda         }
702808c999fSMiguel Ojeda     }
703808c999fSMiguel Ojeda 
704808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
705808c999fSMiguel Ojeda     impl Parse for MetaNameValue {
parse(input: ParseStream) -> Result<Self>706808c999fSMiguel Ojeda         fn parse(input: ParseStream) -> Result<Self> {
707808c999fSMiguel Ojeda             let path = parse_outermost_meta_path(input)?;
708808c999fSMiguel Ojeda             parse_meta_name_value_after_path(path, input)
709808c999fSMiguel Ojeda         }
710808c999fSMiguel Ojeda     }
711808c999fSMiguel Ojeda 
712808c999fSMiguel Ojeda     // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path,
713808c999fSMiguel Ojeda     // only the `unsafe` keyword is accepted as an attribute's outermost path.
parse_outermost_meta_path(input: ParseStream) -> Result<Path>714808c999fSMiguel Ojeda     fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> {
715808c999fSMiguel Ojeda         if input.peek(Token![unsafe]) {
716808c999fSMiguel Ojeda             let unsafe_token: Token![unsafe] = input.parse()?;
717808c999fSMiguel Ojeda             Ok(Path::from(Ident::new("unsafe", unsafe_token.span)))
718808c999fSMiguel Ojeda         } else {
719808c999fSMiguel Ojeda             Path::parse_mod_style(input)
720808c999fSMiguel Ojeda         }
721808c999fSMiguel Ojeda     }
722808c999fSMiguel Ojeda 
parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta>723808c999fSMiguel Ojeda     pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
724808c999fSMiguel Ojeda         if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
725808c999fSMiguel Ojeda             parse_meta_list_after_path(path, input).map(Meta::List)
726808c999fSMiguel Ojeda         } else if input.peek(Token![=]) {
727808c999fSMiguel Ojeda             parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
728808c999fSMiguel Ojeda         } else {
729808c999fSMiguel Ojeda             Ok(Meta::Path(path))
730808c999fSMiguel Ojeda         }
731808c999fSMiguel Ojeda     }
732808c999fSMiguel Ojeda 
parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList>733808c999fSMiguel Ojeda     fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
734808c999fSMiguel Ojeda         let (delimiter, tokens) = mac::parse_delimiter(input)?;
735808c999fSMiguel Ojeda         Ok(MetaList {
736808c999fSMiguel Ojeda             path,
737808c999fSMiguel Ojeda             delimiter,
738808c999fSMiguel Ojeda             tokens,
739808c999fSMiguel Ojeda         })
740808c999fSMiguel Ojeda     }
741808c999fSMiguel Ojeda 
parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue>742808c999fSMiguel Ojeda     fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
743808c999fSMiguel Ojeda         let eq_token: Token![=] = input.parse()?;
744808c999fSMiguel Ojeda         let ahead = input.fork();
745808c999fSMiguel Ojeda         let lit: Option<Lit> = ahead.parse()?;
746808c999fSMiguel Ojeda         let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
747808c999fSMiguel Ojeda             input.advance_to(&ahead);
748808c999fSMiguel Ojeda             Expr::Lit(ExprLit {
749808c999fSMiguel Ojeda                 attrs: Vec::new(),
750808c999fSMiguel Ojeda                 lit,
751808c999fSMiguel Ojeda             })
752808c999fSMiguel Ojeda         } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
753808c999fSMiguel Ojeda             return Err(input.error("unexpected attribute inside of attribute"));
754808c999fSMiguel Ojeda         } else {
755808c999fSMiguel Ojeda             input.parse()?
756808c999fSMiguel Ojeda         };
757808c999fSMiguel Ojeda         Ok(MetaNameValue {
758808c999fSMiguel Ojeda             path,
759808c999fSMiguel Ojeda             eq_token,
760808c999fSMiguel Ojeda             value,
761808c999fSMiguel Ojeda         })
762808c999fSMiguel Ojeda     }
763808c999fSMiguel Ojeda 
764808c999fSMiguel Ojeda     pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle);
765808c999fSMiguel Ojeda 
766808c999fSMiguel Ojeda     impl<'a> Display for DisplayAttrStyle<'a> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result767808c999fSMiguel Ojeda         fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
768808c999fSMiguel Ojeda             formatter.write_str(match self.0 {
769808c999fSMiguel Ojeda                 AttrStyle::Outer => "#",
770808c999fSMiguel Ojeda                 AttrStyle::Inner(_) => "#!",
771808c999fSMiguel Ojeda             })
772808c999fSMiguel Ojeda         }
773808c999fSMiguel Ojeda     }
774808c999fSMiguel Ojeda 
775808c999fSMiguel Ojeda     pub(super) struct DisplayPath<'a>(pub &'a Path);
776808c999fSMiguel Ojeda 
777808c999fSMiguel Ojeda     impl<'a> Display for DisplayPath<'a> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result778808c999fSMiguel Ojeda         fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
779808c999fSMiguel Ojeda             for (i, segment) in self.0.segments.iter().enumerate() {
780808c999fSMiguel Ojeda                 if i > 0 || self.0.leading_colon.is_some() {
781808c999fSMiguel Ojeda                     formatter.write_str("::")?;
782808c999fSMiguel Ojeda                 }
783808c999fSMiguel Ojeda                 write!(formatter, "{}", segment.ident)?;
784808c999fSMiguel Ojeda             }
785808c999fSMiguel Ojeda             Ok(())
786808c999fSMiguel Ojeda         }
787808c999fSMiguel Ojeda     }
788808c999fSMiguel Ojeda }
789808c999fSMiguel Ojeda 
790808c999fSMiguel Ojeda #[cfg(feature = "printing")]
791808c999fSMiguel Ojeda mod printing {
792808c999fSMiguel Ojeda     use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
793808c999fSMiguel Ojeda     use crate::path;
794808c999fSMiguel Ojeda     use crate::path::printing::PathStyle;
795808c999fSMiguel Ojeda     use proc_macro2::TokenStream;
796808c999fSMiguel Ojeda     use quote::ToTokens;
797808c999fSMiguel Ojeda 
798808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
799808c999fSMiguel Ojeda     impl ToTokens for Attribute {
to_tokens(&self, tokens: &mut TokenStream)800808c999fSMiguel Ojeda         fn to_tokens(&self, tokens: &mut TokenStream) {
801808c999fSMiguel Ojeda             self.pound_token.to_tokens(tokens);
802808c999fSMiguel Ojeda             if let AttrStyle::Inner(b) = &self.style {
803808c999fSMiguel Ojeda                 b.to_tokens(tokens);
804808c999fSMiguel Ojeda             }
805808c999fSMiguel Ojeda             self.bracket_token.surround(tokens, |tokens| {
806808c999fSMiguel Ojeda                 self.meta.to_tokens(tokens);
807808c999fSMiguel Ojeda             });
808808c999fSMiguel Ojeda         }
809808c999fSMiguel Ojeda     }
810808c999fSMiguel Ojeda 
811808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
812808c999fSMiguel Ojeda     impl ToTokens for Meta {
to_tokens(&self, tokens: &mut TokenStream)813808c999fSMiguel Ojeda         fn to_tokens(&self, tokens: &mut TokenStream) {
814808c999fSMiguel Ojeda             match self {
815808c999fSMiguel Ojeda                 Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod),
816808c999fSMiguel Ojeda                 Meta::List(meta_list) => meta_list.to_tokens(tokens),
817808c999fSMiguel Ojeda                 Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens),
818808c999fSMiguel Ojeda             }
819808c999fSMiguel Ojeda         }
820808c999fSMiguel Ojeda     }
821808c999fSMiguel Ojeda 
822808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
823808c999fSMiguel Ojeda     impl ToTokens for MetaList {
to_tokens(&self, tokens: &mut TokenStream)824808c999fSMiguel Ojeda         fn to_tokens(&self, tokens: &mut TokenStream) {
825808c999fSMiguel Ojeda             path::printing::print_path(tokens, &self.path, PathStyle::Mod);
826808c999fSMiguel Ojeda             self.delimiter.surround(tokens, self.tokens.clone());
827808c999fSMiguel Ojeda         }
828808c999fSMiguel Ojeda     }
829808c999fSMiguel Ojeda 
830808c999fSMiguel Ojeda     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
831808c999fSMiguel Ojeda     impl ToTokens for MetaNameValue {
to_tokens(&self, tokens: &mut TokenStream)832808c999fSMiguel Ojeda         fn to_tokens(&self, tokens: &mut TokenStream) {
833808c999fSMiguel Ojeda             path::printing::print_path(tokens, &self.path, PathStyle::Mod);
834808c999fSMiguel Ojeda             self.eq_token.to_tokens(tokens);
835808c999fSMiguel Ojeda             self.value.to_tokens(tokens);
836808c999fSMiguel Ojeda         }
837808c999fSMiguel Ojeda     }
838808c999fSMiguel Ojeda }
839