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