xref: /linux/rust/syn/attr.rs (revision 784faa8eca8270671e0ed6d9d21f04bbb80fc5f7)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 #[cfg(feature = "parsing")]
4 use crate::error::Error;
5 #[cfg(feature = "parsing")]
6 use crate::error::Result;
7 use crate::expr::Expr;
8 use crate::mac::MacroDelimiter;
9 #[cfg(feature = "parsing")]
10 use crate::meta::{self, ParseNestedMeta};
11 #[cfg(feature = "parsing")]
12 use crate::parse::{Parse, ParseStream, Parser};
13 use crate::path::Path;
14 use crate::token;
15 use proc_macro2::TokenStream;
16 #[cfg(feature = "printing")]
17 use std::iter;
18 #[cfg(feature = "printing")]
19 use std::slice;
20 
21 ast_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 
183 impl 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"]`.
path(&self) -> &Path188     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")))]
parse_args<T: Parse>(&self) -> Result<T>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")))]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>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")))]
parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>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")))]
parse_outer(input: ParseStream) -> Result<Vec<Self>>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")))]
parse_inner(input: ParseStream) -> Result<Vec<Self>>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 
431 ast_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 
453 ast_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 
486 ast_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 
496 ast_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 
506 impl 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"]`.
path(&self) -> &Path511     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")))]
require_path_only(&self) -> Result<&Path>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")))]
require_list(&self) -> Result<&MetaList>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")))]
require_name_value(&self) -> Result<&MetaNameValue>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 
568 impl MetaList {
569     /// See [`Attribute::parse_args`].
570     #[cfg(feature = "parsing")]
571     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
parse_args<T: Parse>(&self) -> Result<T>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")))]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>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")))]
parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>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")]
596 pub(crate) trait FilterAttrs<'a> {
597     type Ret: Iterator<Item = &'a Attribute>;
598 
outer(self) -> Self::Ret599     fn outer(self) -> Self::Ret;
600     #[cfg(feature = "full")]
inner(self) -> Self::Ret601     fn inner(self) -> Self::Ret;
602 }
603 
604 #[cfg(feature = "printing")]
605 impl<'a> FilterAttrs<'a> for &'a [Attribute] {
606     type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
607 
outer(self) -> Self::Ret608     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")]
inner(self) -> Self::Ret619     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 
630 impl From<Path> for Meta {
from(meta: Path) -> Meta631     fn from(meta: Path) -> Meta {
632         Meta::Path(meta)
633     }
634 }
635 
636 impl From<MetaList> for Meta {
from(meta: MetaList) -> Meta637     fn from(meta: MetaList) -> Meta {
638         Meta::List(meta)
639     }
640 }
641 
642 impl From<MetaNameValue> for Meta {
from(meta: MetaNameValue) -> Meta643     fn from(meta: MetaNameValue) -> Meta {
644         Meta::NameValue(meta)
645     }
646 }
647 
648 #[cfg(feature = "parsing")]
649 pub(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 
parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()>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 
single_parse_inner(input: ParseStream) -> Result<Attribute>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 
single_parse_outer(input: ParseStream) -> Result<Attribute>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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>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.
parse_outermost_meta_path(input: ParseStream) -> Result<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 
parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta>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 
parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList>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 
parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue>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> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result767         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> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result778         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")]
791 mod 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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 }
839