663 lines
21 KiB
Rust
663 lines
21 KiB
Rust
use super::*;
|
|
use crate::punctuated::Punctuated;
|
|
use proc_macro2::TokenStream;
|
|
use std::iter;
|
|
use std::slice;
|
|
|
|
#[cfg(feature = "parsing")]
|
|
use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
|
|
#[cfg(feature = "parsing")]
|
|
use crate::punctuated::Pair;
|
|
|
|
ast_struct! {
|
|
/// An attribute like `#[repr(transparent)]`.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or `"full"`
|
|
/// feature.*
|
|
///
|
|
/// <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 doc comments like `/// # Example`.
|
|
/// - Inner doc comments like `//! Please file an issue`.
|
|
/// - Outer block comments `/** # Example */`.
|
|
/// - Inner block comments `/*! Please file an issue */`.
|
|
///
|
|
/// The `style` field of type `AttrStyle` distinguishes whether an attribute
|
|
/// is outer or inner. Doc comments and block comments are promoted to
|
|
/// attributes, as this is how they are processed by the compiler and by
|
|
/// `macro_rules!` macros.
|
|
///
|
|
/// The `path` field gives the possibly colon-delimited path against which
|
|
/// the attribute is resolved. It is equal to `"doc"` for desugared doc
|
|
/// comments. The `tokens` field contains the rest of the attribute body as
|
|
/// tokens.
|
|
///
|
|
/// ```text
|
|
/// #[derive(Copy)] #[crate::precondition x < 5]
|
|
/// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
|
|
/// path tokens path tokens
|
|
/// ```
|
|
///
|
|
/// <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`]: parse::Parse
|
|
/// [`ParseStream::parse`]: parse::ParseBuffer::parse
|
|
/// [`ParseStream::call`]: 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
|
|
/// 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.
|
|
///
|
|
/// If the attribute you are parsing is expected to conform to the
|
|
/// conventional structured form of attribute, use [`parse_meta()`] to
|
|
/// obtain that structured representation. If the attribute follows some
|
|
/// other grammar of its own, use [`parse_args()`] to parse that into the
|
|
/// expected data structure.
|
|
///
|
|
/// [`parse_meta()`]: Attribute::parse_meta
|
|
/// [`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(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct Attribute {
|
|
pub pound_token: Token![#],
|
|
pub style: AttrStyle,
|
|
pub bracket_token: token::Bracket,
|
|
pub path: Path,
|
|
pub tokens: TokenStream,
|
|
}
|
|
}
|
|
|
|
impl Attribute {
|
|
/// Parses the content of the attribute, consisting of the path and tokens,
|
|
/// as a [`Meta`] if possible.
|
|
///
|
|
/// *This function is available only if Syn is built with the `"parsing"`
|
|
/// feature.*
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
pub fn parse_meta(&self) -> Result<Meta> {
|
|
fn clone_ident_segment(segment: &PathSegment) -> PathSegment {
|
|
PathSegment {
|
|
ident: segment.ident.clone(),
|
|
arguments: PathArguments::None,
|
|
}
|
|
}
|
|
|
|
let path = Path {
|
|
leading_colon: self
|
|
.path
|
|
.leading_colon
|
|
.as_ref()
|
|
.map(|colon| Token),
|
|
segments: self
|
|
.path
|
|
.segments
|
|
.pairs()
|
|
.map(|pair| match pair {
|
|
Pair::Punctuated(seg, punct) => {
|
|
Pair::Punctuated(clone_ident_segment(seg), Token)
|
|
}
|
|
Pair::End(seg) => Pair::End(clone_ident_segment(seg)),
|
|
})
|
|
.collect(),
|
|
};
|
|
|
|
let parser = |input: ParseStream| parsing::parse_meta_after_path(path, input);
|
|
parse::Parser::parse2(parser, self.tokens.clone())
|
|
}
|
|
|
|
/// Parse the arguments to the attribute as a syntax tree.
|
|
///
|
|
/// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
|
|
///
|
|
/// - the surrounding delimiters are *not* included in the input to the
|
|
/// parser; and
|
|
/// - the error message has a more useful span when `tokens` is empty.
|
|
///
|
|
/// ```text
|
|
/// #[my_attr(value < 5)]
|
|
/// ^^^^^^^^^ what gets parsed
|
|
/// ```
|
|
///
|
|
/// *This function is available only if Syn is built with the `"parsing"`
|
|
/// feature.*
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(doc_cfg, 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.
|
|
///
|
|
/// *This function is available only if Syn is built with the `"parsing"`
|
|
/// feature.*
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
|
|
let parser = |input: ParseStream| {
|
|
let args = enter_args(self, input)?;
|
|
parse::parse_stream(parser, &args)
|
|
};
|
|
parser.parse2(self.tokens.clone())
|
|
}
|
|
|
|
/// Parses zero or more outer attributes from the stream.
|
|
///
|
|
/// *This function is available only if Syn is built with the `"parsing"`
|
|
/// feature.*
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(doc_cfg, 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.
|
|
///
|
|
/// *This function is available only if Syn is built with the `"parsing"`
|
|
/// feature.*
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(doc_cfg, 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)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
fn expected_parentheses(attr: &Attribute) -> String {
|
|
let style = match attr.style {
|
|
AttrStyle::Outer => "#",
|
|
AttrStyle::Inner(_) => "#!",
|
|
};
|
|
|
|
let mut path = String::new();
|
|
for segment in &attr.path.segments {
|
|
if !path.is_empty() || attr.path.leading_colon.is_some() {
|
|
path += "::";
|
|
}
|
|
path += &segment.ident.to_string();
|
|
}
|
|
|
|
format!("{}[{}(...)]", style, path)
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
|
|
if input.is_empty() {
|
|
let expected = expected_parentheses(attr);
|
|
let msg = format!("expected attribute arguments in parentheses: {}", expected);
|
|
return Err(crate::error::new2(
|
|
attr.pound_token.span,
|
|
attr.bracket_token.span,
|
|
msg,
|
|
));
|
|
} else if input.peek(Token![=]) {
|
|
let expected = expected_parentheses(attr);
|
|
let msg = format!("expected parentheses: {}", expected);
|
|
return Err(input.error(msg));
|
|
};
|
|
|
|
let content;
|
|
if input.peek(token::Paren) {
|
|
parenthesized!(content in input);
|
|
} else if input.peek(token::Bracket) {
|
|
bracketed!(content in input);
|
|
} else if input.peek(token::Brace) {
|
|
braced!(content in input);
|
|
} else {
|
|
return Err(input.error("unexpected token in attribute arguments"));
|
|
}
|
|
|
|
if input.is_empty() {
|
|
Ok(content)
|
|
} else {
|
|
Err(input.error("unexpected token in attribute arguments"))
|
|
}
|
|
}
|
|
|
|
ast_enum! {
|
|
/// Distinguishes between attributes that decorate an item and attributes
|
|
/// that are contained within an item.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or `"full"`
|
|
/// feature.*
|
|
///
|
|
/// # Outer attributes
|
|
///
|
|
/// - `#[repr(transparent)]`
|
|
/// - `/// # Example`
|
|
/// - `/** Please file an issue */`
|
|
///
|
|
/// # Inner attributes
|
|
///
|
|
/// - `#![feature(proc_macro)]`
|
|
/// - `//! # Example`
|
|
/// - `/*! Please file an issue */`
|
|
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub enum AttrStyle {
|
|
Outer,
|
|
Inner(Token![!]),
|
|
}
|
|
}
|
|
|
|
ast_enum_of_structs! {
|
|
/// Content of a compile-time structured attribute.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or `"full"`
|
|
/// feature.*
|
|
///
|
|
/// ## 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]: Expr#syntax-tree-enums
|
|
#[cfg_attr(doc_cfg, 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)`.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or
|
|
/// `"full"` feature.*
|
|
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct MetaList {
|
|
pub path: Path,
|
|
pub paren_token: token::Paren,
|
|
pub nested: Punctuated<NestedMeta, Token![,]>,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// A name-value pair within an attribute, like `feature = "nightly"`.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or
|
|
/// `"full"` feature.*
|
|
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct MetaNameValue {
|
|
pub path: Path,
|
|
pub eq_token: Token![=],
|
|
pub lit: Lit,
|
|
}
|
|
}
|
|
|
|
impl Meta {
|
|
/// Returns the identifier 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,
|
|
}
|
|
}
|
|
}
|
|
|
|
ast_enum_of_structs! {
|
|
/// Element of a compile-time attribute list.
|
|
///
|
|
/// *This type is available only if Syn is built with the `"derive"` or `"full"`
|
|
/// feature.*
|
|
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub enum NestedMeta {
|
|
/// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
|
|
/// would be a nested `Meta::Path`.
|
|
Meta(Meta),
|
|
|
|
/// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
|
|
Lit(Lit),
|
|
}
|
|
}
|
|
|
|
/// Conventional argument type associated with an invocation of an attribute
|
|
/// macro.
|
|
///
|
|
/// For example if we are developing an attribute macro that is intended to be
|
|
/// invoked on function items as follows:
|
|
///
|
|
/// ```
|
|
/// # const IGNORE: &str = stringify! {
|
|
/// #[my_attribute(path = "/v1/refresh")]
|
|
/// # };
|
|
/// pub fn refresh() {
|
|
/// /* ... */
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// The implementation of this macro would want to parse its attribute arguments
|
|
/// as type `AttributeArgs`.
|
|
///
|
|
/// ```
|
|
/// # extern crate proc_macro;
|
|
/// #
|
|
/// use proc_macro::TokenStream;
|
|
/// use syn::{parse_macro_input, AttributeArgs, ItemFn};
|
|
///
|
|
/// # const IGNORE: &str = stringify! {
|
|
/// #[proc_macro_attribute]
|
|
/// # };
|
|
/// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
/// let args = parse_macro_input!(args as AttributeArgs);
|
|
/// let input = parse_macro_input!(input as ItemFn);
|
|
///
|
|
/// /* ... */
|
|
/// # "".parse().unwrap()
|
|
/// }
|
|
/// ```
|
|
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub type AttributeArgs = Vec<NestedMeta>;
|
|
|
|
pub trait FilterAttrs<'a> {
|
|
type Ret: Iterator<Item = &'a Attribute>;
|
|
|
|
fn outer(self) -> Self::Ret;
|
|
fn inner(self) -> Self::Ret;
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
pub mod parsing {
|
|
use super::*;
|
|
use crate::ext::IdentExt;
|
|
use crate::parse::{Parse, ParseStream, Result};
|
|
|
|
pub fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
|
|
while input.peek(Token![#]) && input.peek2(Token![!]) {
|
|
attrs.push(input.call(parsing::single_parse_inner)?);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub 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),
|
|
path: content.call(Path::parse_mod_style)?,
|
|
tokens: content.parse()?,
|
|
})
|
|
}
|
|
|
|
pub 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),
|
|
path: content.call(Path::parse_mod_style)?,
|
|
tokens: content.parse()?,
|
|
})
|
|
}
|
|
|
|
// Like Path::parse_mod_style but accepts keywords in the path.
|
|
fn parse_meta_path(input: ParseStream) -> Result<Path> {
|
|
Ok(Path {
|
|
leading_colon: input.parse()?,
|
|
segments: {
|
|
let mut segments = Punctuated::new();
|
|
while input.peek(Ident::peek_any) {
|
|
let ident = Ident::parse_any(input)?;
|
|
segments.push_value(PathSegment::from(ident));
|
|
if !input.peek(Token![::]) {
|
|
break;
|
|
}
|
|
let punct = input.parse()?;
|
|
segments.push_punct(punct);
|
|
}
|
|
if segments.is_empty() {
|
|
return Err(input.error("expected path"));
|
|
} else if segments.trailing_punct() {
|
|
return Err(input.error("expected path segment"));
|
|
}
|
|
segments
|
|
},
|
|
})
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Meta {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let path = input.call(parse_meta_path)?;
|
|
parse_meta_after_path(path, input)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
impl Parse for MetaList {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let path = input.call(parse_meta_path)?;
|
|
parse_meta_list_after_path(path, input)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
impl Parse for MetaNameValue {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let path = input.call(parse_meta_path)?;
|
|
parse_meta_name_value_after_path(path, input)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
|
|
impl Parse for NestedMeta {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
|
|
input.parse().map(NestedMeta::Lit)
|
|
} else if input.peek(Ident::peek_any)
|
|
|| input.peek(Token![::]) && input.peek3(Ident::peek_any)
|
|
{
|
|
input.parse().map(NestedMeta::Meta)
|
|
} else {
|
|
Err(input.error("expected identifier or literal"))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
|
|
if input.peek(token::Paren) {
|
|
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 content;
|
|
Ok(MetaList {
|
|
path,
|
|
paren_token: parenthesized!(content in input),
|
|
nested: content.parse_terminated(NestedMeta::parse)?,
|
|
})
|
|
}
|
|
|
|
fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
|
|
Ok(MetaNameValue {
|
|
path,
|
|
eq_token: input.parse()?,
|
|
lit: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "printing")]
|
|
mod printing {
|
|
use super::*;
|
|
use proc_macro2::TokenStream;
|
|
use quote::ToTokens;
|
|
|
|
#[cfg_attr(doc_cfg, 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.path.to_tokens(tokens);
|
|
self.tokens.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for MetaList {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.path.to_tokens(tokens);
|
|
self.paren_token.surround(tokens, |tokens| {
|
|
self.nested.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for MetaNameValue {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.path.to_tokens(tokens);
|
|
self.eq_token.to_tokens(tokens);
|
|
self.lit.to_tokens(tokens);
|
|
}
|
|
}
|
|
}
|