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