mirror of https://github.com/torvalds/linux.git
839 lines
28 KiB
Rust
839 lines
28 KiB
Rust
// 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);
|
|
}
|
|
}
|
|
}
|