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