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"))))] pubstruct 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"]`. pubfn 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")))] pubfn 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")))] pubfn 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")))] pubfn 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")))] pubfn parse_outer(input: ParseStream) -> Result<Vec<Self>> { letmut 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")))] pubfn parse_inner(input: ParseStream) -> Result<Vec<Self>> { letmut 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"))))] pubenum 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"))))] pubenum 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"))))] pubstruct 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"))))] pubstruct 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"]`. pubfn path(&self) -> &Path { matchself {
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")))] pubfn require_path_only(&self) -> Result<&Path> { let error_span = matchself {
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")))] pubfn require_list(&self) -> Result<&MetaList> { matchself {
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")))] pubfn require_name_value(&self) -> Result<&MetaNameValue> { matchself {
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 `=`")),
}
}
}
// 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)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.