| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| #[cfg(feature = "parsing")] | |
| use crate::error::Error; | |
| #[cfg(feature = "parsing")] | |
| use crate::error::Result; | |
| use crate::expr::Expr; | |
| use crate::mac::MacroDelimiter; | |
| #[cfg(feature = "parsing")] | |
| use crate::meta::{self, ParseNestedMeta}; | |
| #[cfg(feature = "parsing")] | |
| use crate::parse::{Parse, ParseStream, Parser}; | |
| use crate::path::Path; | |
| use crate::token; | |
| use proc_macro2::TokenStream; | |
| #[cfg(feature = "printing")] | |
| use std::iter; | |
| #[cfg(feature = "printing")] | |
| use std::slice; | |
| ast_struct! { | |
| /// An attribute, like `#[repr(transparent)]`. | |
| /// | |
| /// <br> | |
| /// | |
| /// # Syntax | |
| /// | |
| /// Rust has six types of attributes. | |
| /// | |
| /// - Outer attributes like `#[repr(transparent)]`. These appear outside or | |
| /// in front of the item they describe. | |
| /// | |
| /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside | |
| /// of the item they describe, usually a module. | |
| /// | |
| /// - Outer one-line doc comments like `/// Example`. | |
| /// | |
| /// - Inner one-line doc comments like `//! Please file an issue`. | |
| /// | |
| /// - Outer documentation blocks `/** Example */`. | |
| /// | |
| /// - Inner documentation blocks `/*! Please file an issue */`. | |
| /// | |
| /// The `style` field of type `AttrStyle` distinguishes whether an attribute | |
| /// is outer or inner. | |
| /// | |
| /// Every attribute has a `path` that indicates the intended interpretation | |
| /// of the rest of the attribute's contents. The path and the optional | |
| /// additional contents are represented together in the `meta` field of the | |
| /// attribute in three possible varieties: | |
| /// | |
| /// - Meta::Path — attributes whose information content conveys just a | |
| /// path, for example the `#[test]` attribute. | |
| /// | |
| /// - Meta::List — attributes that carry arbitrary tokens after the | |
| /// path, surrounded by a delimiter (parenthesis, bracket, or brace). For | |
| /// example `#[derive(Copy)]` or `#[precondition(x < 5)]`. | |
| /// | |
| /// - Meta::NameValue — attributes with an `=` sign after the path, | |
| /// followed by a Rust expression. For example `#[path = | |
| /// "sys/windows.rs"]`. | |
| /// | |
| /// All doc comments are represented in the NameValue style with a path of | |
| /// "doc", as this is how they are processed by the compiler and by | |
| /// `macro_rules!` macros. | |
| /// | |
| /// ```text | |
| /// #[derive(Copy, Clone)] | |
| /// ~~~~~~Path | |
| /// ^^^^^^^^^^^^^^^^^^^Meta::List | |
| /// | |
| /// #[path = "sys/windows.rs"] | |
| /// ~~~~Path | |
| /// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue | |
| /// | |
| /// #[test] | |
| /// ^^^^Meta::Path | |
| /// ``` | |
| /// | |
| /// <br> | |
| /// | |
| /// # Parsing from tokens to Attribute | |
| /// | |
| /// This type does not implement the [`Parse`] trait and thus cannot be | |
| /// parsed directly by [`ParseStream::parse`]. Instead use | |
| /// [`ParseStream::call`] with one of the two parser functions | |
| /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on | |
| /// which you intend to parse. | |
| /// | |
| /// [`Parse`]: crate::parse::Parse | |
| /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse | |
| /// [`ParseStream::call`]: crate::parse::ParseBuffer::call | |
| /// | |
| /// ``` | |
| /// use syn::{Attribute, Ident, Result, Token}; | |
| /// use syn::parse::{Parse, ParseStream}; | |
| /// | |
| /// // Parses a unit struct with attributes. | |
| /// // | |
| /// // #[path = "s.tmpl"] | |
| /// // struct S; | |
| /// struct UnitStruct { | |
| /// attrs: Vec<Attribute>, | |
| /// struct_token: Token![struct], | |
| /// name: Ident, | |
| /// semi_token: Token![;], | |
| /// } | |
| /// | |
| /// impl Parse for UnitStruct { | |
| /// fn parse(input: ParseStream) -> Result<Self> { | |
| /// Ok(UnitStruct { | |
| /// attrs: input.call(Attribute::parse_outer)?, | |
| /// struct_token: input.parse()?, | |
| /// name: input.parse()?, | |
| /// semi_token: input.parse()?, | |
| /// }) | |
| /// } | |
| /// } | |
| /// ``` | |
| /// | |
| /// <p><br></p> | |
| /// | |
| /// # Parsing from Attribute to structured arguments | |
| /// | |
| /// The grammar of attributes in Rust is very flexible, which makes the | |
| /// syntax tree not that useful on its own. In particular, arguments of the | |
| /// `Meta::List` variety of attribute are held in an arbitrary `tokens: | |
| /// TokenStream`. Macros are expected to check the `path` of the attribute, | |
| /// decide whether they recognize it, and then parse the remaining tokens | |
| /// according to whatever grammar they wish to require for that kind of | |
| /// attribute. Use [`parse_args()`] to parse those tokens into the expected | |
| /// data structure. | |
| /// | |
| /// [`parse_args()`]: Attribute::parse_args | |
| /// | |
| /// <p><br></p> | |
| /// | |
| /// # Doc comments | |
| /// | |
| /// The compiler transforms doc comments, such as `/// comment` and `/*! | |
| /// comment */`, into attributes before macros are expanded. Each comment is | |
| /// expanded into an attribute of the form `#[doc = r"comment"]`. | |
| /// | |
| /// As an example, the following `mod` items are expanded identically: | |
| /// | |
| /// ``` | |
| /// # use syn::{ItemMod, parse_quote}; | |
| /// let doc: ItemMod = parse_quote! { | |
| /// /// Single line doc comments | |
| /// /// We write so many! | |
| /// /** | |
| /// * Multi-line comments... | |
| /// * May span many lines | |
| /// */ | |
| /// mod example { | |
| /// //! Of course, they can be inner too | |
| /// /*! And fit in a single line */ | |
| /// } | |
| /// }; | |
| /// let attr: ItemMod = parse_quote! { | |
| /// #[doc = r" Single line doc comments"] | |
| /// #[doc = r" We write so many!"] | |
| /// #[doc = r" | |
| /// * Multi-line comments... | |
| /// * May span many lines | |
| /// "] | |
| /// mod example { | |
| /// #![doc = r" Of course, they can be inner too"] | |
| /// #![doc = r" And fit in a single line "] | |
| /// } | |
| /// }; | |
| /// assert_eq!(doc, attr); | |
| /// ``` | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct Attribute { | |
| pub pound_token: Token![#], | |
| pub style: AttrStyle, | |
| pub bracket_token: token::Bracket, | |
| pub meta: Meta, | |
| } | |
| } | |
| impl Attribute { | |
| /// Returns the path that identifies the interpretation of this attribute. | |
| /// | |
| /// For example this would return the `test` in `#[test]`, the `derive` in | |
| /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. | |
| pub fn path(&self) -> &Path { | |
| self.meta.path() | |
| } | |
| /// Parse the arguments to the attribute as a syntax tree. | |
| /// | |
| /// This is similar to pulling out the `TokenStream` from `Meta::List` and | |
| /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using | |
| /// `parse_args` the error message has a more useful span when `tokens` is | |
| /// empty. | |
| /// | |
| /// The surrounding delimiters are *not* included in the input to the | |
| /// parser. | |
| /// | |
| /// ```text | |
| /// #[my_attr(value < 5)] | |
| /// ^^^^^^^^^ what gets parsed | |
| /// ``` | |
| /// | |
| /// # Example | |
| /// | |
| /// ``` | |
| /// use syn::{parse_quote, Attribute, Expr}; | |
| /// | |
| /// let attr: Attribute = parse_quote! { | |
| /// #[precondition(value < 5)] | |
| /// }; | |
| /// | |
| /// if attr.path().is_ident("precondition") { | |
| /// let precondition: Expr = attr.parse_args()?; | |
| /// // ... | |
| /// } | |
| /// # anyhow::Ok(()) | |
| /// ``` | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_args<T: Parse>(&self) -> Result<T> { | |
| self.parse_args_with(T::parse) | |
| } | |
| /// Parse the arguments to the attribute using the given parser. | |
| /// | |
| /// # Example | |
| /// | |
| /// ``` | |
| /// use syn::{parse_quote, Attribute}; | |
| /// | |
| /// let attr: Attribute = parse_quote! { | |
| /// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }] | |
| /// }; | |
| /// | |
| /// let bwom = attr.parse_args_with(Attribute::parse_outer)?; | |
| /// | |
| /// // Attribute does not have a Parse impl, so we couldn't directly do: | |
| /// // let bwom: Attribute = attr.parse_args()?; | |
| /// # anyhow::Ok(()) | |
| /// ``` | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { | |
| match &self.meta { | |
| Meta::Path(path) => Err(crate::error::new2( | |
| path.segments.first().unwrap().ident.span(), | |
| path.segments.last().unwrap().ident.span(), | |
| format!( | |
| "expected attribute arguments in parentheses: {}[{}(...)]", | |
| parsing::DisplayAttrStyle(&self.style), | |
| parsing::DisplayPath(path), | |
| ), | |
| )), | |
| Meta::NameValue(meta) => Err(Error::new( | |
| meta.eq_token.span, | |
| format_args!( | |
| "expected parentheses: {}[{}(...)]", | |
| parsing::DisplayAttrStyle(&self.style), | |
| parsing::DisplayPath(&meta.path), | |
| ), | |
| )), | |
| Meta::List(meta) => meta.parse_args_with(parser), | |
| } | |
| } | |
| /// Parse the arguments to the attribute, expecting it to follow the | |
| /// conventional structure used by most of Rust's built-in attributes. | |
| /// | |
| /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference | |
| /// explains the convention in more detail. Not all attributes follow this | |
| /// convention, so [`parse_args()`][Self::parse_args] is available if you | |
| /// need to parse arbitrarily goofy attribute syntax. | |
| /// | |
| /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax | |
| /// | |
| /// # Example | |
| /// | |
| /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute | |
| /// syntax. | |
| /// | |
| /// ``` | |
| /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt}; | |
| /// | |
| /// let input: ItemStruct = parse_quote! { | |
| /// #[repr(C, align(4))] | |
| /// pub struct MyStruct(u16, u32); | |
| /// }; | |
| /// | |
| /// let mut repr_c = false; | |
| /// let mut repr_transparent = false; | |
| /// let mut repr_align = None::<usize>; | |
| /// let mut repr_packed = None::<usize>; | |
| /// for attr in &input.attrs { | |
| /// if attr.path().is_ident("repr") { | |
| /// attr.parse_nested_meta(|meta| { | |
| /// // #[repr(C)] | |
| /// if meta.path.is_ident("C") { | |
| /// repr_c = true; | |
| /// return Ok(()); | |
| /// } | |
| /// | |
| /// // #[repr(transparent)] | |
| /// if meta.path.is_ident("transparent") { | |
| /// repr_transparent = true; | |
| /// return Ok(()); | |
| /// } | |
| /// | |
| /// // #[repr(align(N))] | |
| /// if meta.path.is_ident("align") { | |
| /// let content; | |
| /// parenthesized!(content in meta.input); | |
| /// let lit: LitInt = content.parse()?; | |
| /// let n: usize = lit.base10_parse()?; | |
| /// repr_align = Some(n); | |
| /// return Ok(()); | |
| /// } | |
| /// | |
| /// // #[repr(packed)] or #[repr(packed(N))], omitted N means 1 | |
| /// if meta.path.is_ident("packed") { | |
| /// if meta.input.peek(token::Paren) { | |
| /// let content; | |
| /// parenthesized!(content in meta.input); | |
| /// let lit: LitInt = content.parse()?; | |
| /// let n: usize = lit.base10_parse()?; | |
| /// repr_packed = Some(n); | |
| /// } else { | |
| /// repr_packed = Some(1); | |
| /// } | |
| /// return Ok(()); | |
| /// } | |
| /// | |
| /// Err(meta.error("unrecognized repr")) | |
| /// })?; | |
| /// } | |
| /// } | |
| /// # anyhow::Ok(()) | |
| /// ``` | |
| /// | |
| /// # Alternatives | |
| /// | |
| /// In some cases, for attributes which have nested layers of structured | |
| /// content, the following less flexible approach might be more convenient: | |
| /// | |
| /// ``` | |
| /// # use syn::{parse_quote, ItemStruct}; | |
| /// # | |
| /// # let input: ItemStruct = parse_quote! { | |
| /// # #[repr(C, align(4))] | |
| /// # pub struct MyStruct(u16, u32); | |
| /// # }; | |
| /// # | |
| /// use syn::punctuated::Punctuated; | |
| /// use syn::{parenthesized, token, Error, LitInt, Meta, Token}; | |
| /// | |
| /// let mut repr_c = false; | |
| /// let mut repr_transparent = false; | |
| /// let mut repr_align = None::<usize>; | |
| /// let mut repr_packed = None::<usize>; | |
| /// for attr in &input.attrs { | |
| /// if attr.path().is_ident("repr") { | |
| /// let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; | |
| /// for meta in nested { | |
| /// match meta { | |
| /// // #[repr(C)] | |
| /// Meta::Path(path) if path.is_ident("C") => { | |
| /// repr_c = true; | |
| /// } | |
| /// | |
| /// // #[repr(align(N))] | |
| /// Meta::List(meta) if meta.path.is_ident("align") => { | |
| /// let lit: LitInt = meta.parse_args()?; | |
| /// let n: usize = lit.base10_parse()?; | |
| /// repr_align = Some(n); | |
| /// } | |
| /// | |
| /// /* ... */ | |
| /// | |
| /// _ => { | |
| /// return Err(Error::new_spanned(meta, "unrecognized repr")); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// # Ok(()) | |
| /// ``` | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_nested_meta( | |
| &self, | |
| logic: impl FnMut(ParseNestedMeta) -> Result<()>, | |
| ) -> Result<()> { | |
| self.parse_args_with(meta::parser(logic)) | |
| } | |
| /// Parses zero or more outer attributes from the stream. | |
| /// | |
| /// # Example | |
| /// | |
| /// See | |
| /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> { | |
| let mut attrs = Vec::new(); | |
| while input.peek(Token![#]) { | |
| attrs.push(input.call(parsing::single_parse_outer)?); | |
| } | |
| Ok(attrs) | |
| } | |
| /// Parses zero or more inner attributes from the stream. | |
| /// | |
| /// # Example | |
| /// | |
| /// See | |
| /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> { | |
| let mut attrs = Vec::new(); | |
| parsing::parse_inner(input, &mut attrs)?; | |
| Ok(attrs) | |
| } | |
| } | |
| ast_enum! { | |
| /// Distinguishes between attributes that decorate an item and attributes | |
| /// that are contained within an item. | |
| /// | |
| /// # Outer attributes | |
| /// | |
| /// - `#[repr(transparent)]` | |
| /// - `/// # Example` | |
| /// - `/** Please file an issue */` | |
| /// | |
| /// # Inner attributes | |
| /// | |
| /// - `#![feature(proc_macro)]` | |
| /// - `//! # Example` | |
| /// - `/*! Please file an issue */` | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub enum AttrStyle { | |
| Outer, | |
| Inner(Token![!]), | |
| } | |
| } | |
| ast_enum! { | |
| /// Content of a compile-time structured attribute. | |
| /// | |
| /// ## Path | |
| /// | |
| /// A meta path is like the `test` in `#[test]`. | |
| /// | |
| /// ## List | |
| /// | |
| /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`. | |
| /// | |
| /// ## NameValue | |
| /// | |
| /// A name-value meta is like the `path = "..."` in `#[path = | |
| /// "sys/windows.rs"]`. | |
| /// | |
| /// # Syntax tree enum | |
| /// | |
| /// This type is a [syntax tree enum]. | |
| /// | |
| /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub enum Meta { | |
| Path(Path), | |
| /// A structured list within an attribute, like `derive(Copy, Clone)`. | |
| List(MetaList), | |
| /// A name-value pair within an attribute, like `feature = "nightly"`. | |
| NameValue(MetaNameValue), | |
| } | |
| } | |
| ast_struct! { | |
| /// A structured list within an attribute, like `derive(Copy, Clone)`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct MetaList { | |
| pub path: Path, | |
| pub delimiter: MacroDelimiter, | |
| pub tokens: TokenStream, | |
| } | |
| } | |
| ast_struct! { | |
| /// A name-value pair within an attribute, like `feature = "nightly"`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct MetaNameValue { | |
| pub path: Path, | |
| pub eq_token: Token![=], | |
| pub value: Expr, | |
| } | |
| } | |
| impl Meta { | |
| /// Returns the path that begins this structured meta item. | |
| /// | |
| /// For example this would return the `test` in `#[test]`, the `derive` in | |
| /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. | |
| pub fn path(&self) -> &Path { | |
| match self { | |
| Meta::Path(path) => path, | |
| Meta::List(meta) => &meta.path, | |
| Meta::NameValue(meta) => &meta.path, | |
| } | |
| } | |
| /// Error if this is a `Meta::List` or `Meta::NameValue`. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn require_path_only(&self) -> Result<&Path> { | |
| let error_span = match self { | |
| Meta::Path(path) => return Ok(path), | |
| Meta::List(meta) => meta.delimiter.span().open(), | |
| Meta::NameValue(meta) => meta.eq_token.span, | |
| }; | |
| Err(Error::new(error_span, "unexpected token in attribute")) | |
| } | |
| /// Error if this is a `Meta::Path` or `Meta::NameValue`. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn require_list(&self) -> Result<&MetaList> { | |
| match self { | |
| Meta::List(meta) => Ok(meta), | |
| Meta::Path(path) => Err(crate::error::new2( | |
| path.segments.first().unwrap().ident.span(), | |
| path.segments.last().unwrap().ident.span(), | |
| format!( | |
| "expected attribute arguments in parentheses: `{}(...)`", | |
| parsing::DisplayPath(path), | |
| ), | |
| )), | |
| Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")), | |
| } | |
| } | |
| /// Error if this is a `Meta::Path` or `Meta::List`. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn require_name_value(&self) -> Result<&MetaNameValue> { | |
| match self { | |
| Meta::NameValue(meta) => Ok(meta), | |
| Meta::Path(path) => Err(crate::error::new2( | |
| path.segments.first().unwrap().ident.span(), | |
| path.segments.last().unwrap().ident.span(), | |
| format!( | |
| "expected a value for this attribute: `{} = ...`", | |
| parsing::DisplayPath(path), | |
| ), | |
| )), | |
| Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")), | |
| } | |
| } | |
| } | |
| impl MetaList { | |
| /// See [`Attribute::parse_args`]. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_args<T: Parse>(&self) -> Result<T> { | |
| self.parse_args_with(T::parse) | |
| } | |
| /// See [`Attribute::parse_args_with`]. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { | |
| let scope = self.delimiter.span().close(); | |
| crate::parse::parse_scoped(parser, scope, self.tokens.clone()) | |
| } | |
| /// See [`Attribute::parse_nested_meta`]. | |
| #[cfg(feature = "parsing")] | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn parse_nested_meta( | |
| &self, | |
| logic: impl FnMut(ParseNestedMeta) -> Result<()>, | |
| ) -> Result<()> { | |
| self.parse_args_with(meta::parser(logic)) | |
| } | |
| } | |
| #[cfg(feature = "printing")] | |
| pub(crate) trait FilterAttrs<'a> { | |
| type Ret: Iterator<Item = &'a Attribute>; | |
| fn outer(self) -> Self::Ret; | |
| #[cfg(feature = "full")] | |
| fn inner(self) -> Self::Ret; | |
| } | |
| #[cfg(feature = "printing")] | |
| impl<'a> FilterAttrs<'a> for &'a [Attribute] { | |
| type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>; | |
| fn outer(self) -> Self::Ret { | |
| fn is_outer(attr: &&Attribute) -> bool { | |
| match attr.style { | |
| AttrStyle::Outer => true, | |
| AttrStyle::Inner(_) => false, | |
| } | |
| } | |
| self.iter().filter(is_outer) | |
| } | |
| #[cfg(feature = "full")] | |
| fn inner(self) -> Self::Ret { | |
| fn is_inner(attr: &&Attribute) -> bool { | |
| match attr.style { | |
| AttrStyle::Inner(_) => true, | |
| AttrStyle::Outer => false, | |
| } | |
| } | |
| self.iter().filter(is_inner) | |
| } | |
| } | |
| impl From<Path> for Meta { | |
| fn from(meta: Path) -> Meta { | |
| Meta::Path(meta) | |
| } | |
| } | |
| impl From<MetaList> for Meta { | |
| fn from(meta: MetaList) -> Meta { | |
| Meta::List(meta) | |
| } | |
| } | |
| impl From<MetaNameValue> for Meta { | |
| fn from(meta: MetaNameValue) -> Meta { | |
| Meta::NameValue(meta) | |
| } | |
| } | |
| #[cfg(feature = "parsing")] | |
| pub(crate) mod parsing { | |
| use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; | |
| use crate::error::Result; | |
| use crate::expr::{Expr, ExprLit}; | |
| use crate::lit::Lit; | |
| use crate::parse::discouraged::Speculative as _; | |
| use crate::parse::{Parse, ParseStream}; | |
| use crate::path::Path; | |
| use crate::{mac, token}; | |
| use proc_macro2::Ident; | |
| use std::fmt::{self, Display}; | |
| pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> { | |
| while input.peek(Token![#]) && input.peek2(Token![!]) { | |
| attrs.push(input.call(single_parse_inner)?); | |
| } | |
| Ok(()) | |
| } | |
| pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> { | |
| let content; | |
| Ok(Attribute { | |
| pound_token: input.parse()?, | |
| style: AttrStyle::Inner(input.parse()?), | |
| bracket_token: bracketed!(content in input), | |
| meta: content.parse()?, | |
| }) | |
| } | |
| pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> { | |
| let content; | |
| Ok(Attribute { | |
| pound_token: input.parse()?, | |
| style: AttrStyle::Outer, | |
| bracket_token: bracketed!(content in input), | |
| meta: content.parse()?, | |
| }) | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for Meta { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let path = parse_outermost_meta_path(input)?; | |
| parse_meta_after_path(path, input) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for MetaList { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let path = parse_outermost_meta_path(input)?; | |
| parse_meta_list_after_path(path, input) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for MetaNameValue { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let path = parse_outermost_meta_path(input)?; | |
| parse_meta_name_value_after_path(path, input) | |
| } | |
| } | |
| // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path, | |
| // only the `unsafe` keyword is accepted as an attribute's outermost path. | |
| fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> { | |
| if input.peek(Token![unsafe]) { | |
| let unsafe_token: Token![unsafe] = input.parse()?; | |
| Ok(Path::from(Ident::new("unsafe", unsafe_token.span))) | |
| } else { | |
| Path::parse_mod_style(input) | |
| } | |
| } | |
| pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> { | |
| if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) { | |
| parse_meta_list_after_path(path, input).map(Meta::List) | |
| } else if input.peek(Token![=]) { | |
| parse_meta_name_value_after_path(path, input).map(Meta::NameValue) | |
| } else { | |
| Ok(Meta::Path(path)) | |
| } | |
| } | |
| fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> { | |
| let (delimiter, tokens) = mac::parse_delimiter(input)?; | |
| Ok(MetaList { | |
| path, | |
| delimiter, | |
| tokens, | |
| }) | |
| } | |
| fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> { | |
| let eq_token: Token![=] = input.parse()?; | |
| let ahead = input.fork(); | |
| let lit: Option<Lit> = ahead.parse()?; | |
| let value = if let (Some(lit), true) = (lit, ahead.is_empty()) { | |
| input.advance_to(&ahead); | |
| Expr::Lit(ExprLit { | |
| attrs: Vec::new(), | |
| lit, | |
| }) | |
| } else if input.peek(Token![#]) && input.peek2(token::Bracket) { | |
| return Err(input.error("unexpected attribute inside of attribute")); | |
| } else { | |
| input.parse()? | |
| }; | |
| Ok(MetaNameValue { | |
| path, | |
| eq_token, | |
| value, | |
| }) | |
| } | |
| pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle); | |
| impl<'a> Display for DisplayAttrStyle<'a> { | |
| fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
| formatter.write_str(match self.0 { | |
| AttrStyle::Outer => "#", | |
| AttrStyle::Inner(_) => "#!", | |
| }) | |
| } | |
| } | |
| pub(super) struct DisplayPath<'a>(pub &'a Path); | |
| impl<'a> Display for DisplayPath<'a> { | |
| fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
| for (i, segment) in self.0.segments.iter().enumerate() { | |
| if i > 0 || self.0.leading_colon.is_some() { | |
| formatter.write_str("::")?; | |
| } | |
| write!(formatter, "{}", segment.ident)?; | |
| } | |
| Ok(()) | |
| } | |
| } | |
| } | |
| #[cfg(feature = "printing")] | |
| mod printing { | |
| use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; | |
| use crate::path; | |
| use crate::path::printing::PathStyle; | |
| use proc_macro2::TokenStream; | |
| use quote::ToTokens; | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for Attribute { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.pound_token.to_tokens(tokens); | |
| if let AttrStyle::Inner(b) = &self.style { | |
| b.to_tokens(tokens); | |
| } | |
| self.bracket_token.surround(tokens, |tokens| { | |
| self.meta.to_tokens(tokens); | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for Meta { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| match self { | |
| Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod), | |
| Meta::List(meta_list) => meta_list.to_tokens(tokens), | |
| Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens), | |
| } | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for MetaList { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| path::printing::print_path(tokens, &self.path, PathStyle::Mod); | |
| self.delimiter.surround(tokens, self.tokens.clone()); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for MetaNameValue { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| path::printing::print_path(tokens, &self.path, PathStyle::Mod); | |
| self.eq_token.to_tokens(tokens); | |
| self.value.to_tokens(tokens); | |
| } | |
| } | |
| } |