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