1#[cfg(feature = "parsing")]
2use crate::error::Error;
3#[cfg(feature = "parsing")]
4use crate::error::Result;
5use crate::expr::Expr;
6use crate::mac::MacroDelimiter;
7#[cfg(feature = "parsing")]
8use crate::meta::{self, ParseNestedMeta};
9#[cfg(feature = "parsing")]
10use crate::parse::{Parse, ParseStream, Parser};
11use crate::path::Path;
12use crate::token;
13use proc_macro2::TokenStream;
14#[cfg(feature = "printing")]
15use std::iter;
16#[cfg(feature = "printing")]
17use std::slice;
18
19ast_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
181impl 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"]`.
186 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")))]
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")))]
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")))]
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")))]
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")))]
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
429ast_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
451ast_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
484ast_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
494ast_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
504impl 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"]`.
509 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")))]
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")))]
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")))]
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
566impl MetaList {
567 /// See [`Attribute::parse_args`].
568 #[cfg(feature = "parsing")]
569 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
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")))]
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")))]
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")]
594pub(crate) trait FilterAttrs<'a> {
595 type Ret: Iterator<Item = &'a Attribute>;
596
597 fn outer(self) -> Self::Ret;
598 #[cfg(feature = "full")]
599 fn inner(self) -> Self::Ret;
600}
601
602#[cfg(feature = "printing")]
603impl<'a> FilterAttrs<'a> for &'a [Attribute] {
604 type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
605
606 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")]
617 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")]
629pub(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
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
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
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 {
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 {
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 {
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
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
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
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> {
735 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> {
746 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")]
759mod 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 {
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 {
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 {
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