chore(vendor): add anyhow crate
This commit was merged in pull request #68.
This commit is contained in:
427
third_party/crates/syn-3.0.6/src/meta.rs
vendored
Normal file
427
third_party/crates/syn-3.0.6/src/meta.rs
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
//! Facility for interpreting structured content inside of an `Attribute`.
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ext::IdentExt as _;
|
||||
use crate::lit::Lit;
|
||||
use crate::parse::{ParseStream, Parser};
|
||||
use crate::path::{Path, PathSegment};
|
||||
use crate::punctuated::Punctuated;
|
||||
use core::fmt::Display;
|
||||
use proc_macro2::Ident;
|
||||
|
||||
/// Make a parser that is usable with `parse_macro_input!` in a
|
||||
/// `#[proc_macro_attribute]` macro.
|
||||
///
|
||||
/// *Warning:* When parsing attribute args **other than** the
|
||||
/// `proc_macro::TokenStream` input of a `proc_macro_attribute`, you do **not**
|
||||
/// need this function. In several cases your callers will get worse error
|
||||
/// messages if you use this function, because the surrounding delimiter's span
|
||||
/// is concealed from attribute macros by rustc. Use
|
||||
/// [`Attribute::parse_nested_meta`] instead.
|
||||
///
|
||||
/// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// This example implements an attribute macro whose invocations look like this:
|
||||
///
|
||||
/// ```
|
||||
/// # const IGNORE: &str = stringify! {
|
||||
/// #[tea(kind = "EarlGrey", hot)]
|
||||
/// struct Picard {...}
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// The "parameters" supported by the attribute are:
|
||||
///
|
||||
/// - `kind = "..."`
|
||||
/// - `hot`
|
||||
/// - `with(sugar, milk, ...)`, a comma-separated list of ingredients
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate proc_macro;
|
||||
/// #
|
||||
/// use proc_macro::TokenStream;
|
||||
/// use syn::{parse_macro_input, LitStr, Path};
|
||||
///
|
||||
/// # const IGNORE: &str = stringify! {
|
||||
/// #[proc_macro_attribute]
|
||||
/// # };
|
||||
/// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
/// let mut kind: Option<LitStr> = None;
|
||||
/// let mut hot: bool = false;
|
||||
/// let mut with: Vec<Path> = Vec::new();
|
||||
/// let tea_parser = syn::meta::parser(|meta| {
|
||||
/// if meta.path.is_ident("kind") {
|
||||
/// kind = Some(meta.value()?.parse()?);
|
||||
/// Ok(())
|
||||
/// } else if meta.path.is_ident("hot") {
|
||||
/// hot = true;
|
||||
/// Ok(())
|
||||
/// } else if meta.path.is_ident("with") {
|
||||
/// meta.parse_nested_meta(|meta| {
|
||||
/// with.push(meta.path);
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported tea property"))
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// parse_macro_input!(args with tea_parser);
|
||||
/// eprintln!("kind={kind:?} hot={hot} with={with:?}");
|
||||
///
|
||||
/// /* ... */
|
||||
/// # TokenStream::new()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `syn::meta` library will take care of dealing with the commas including
|
||||
/// trailing commas, and producing sensible error messages on unexpected input.
|
||||
///
|
||||
/// ```console
|
||||
/// error: expected `,`
|
||||
/// --> src/main.rs:3:37
|
||||
/// |
|
||||
/// 3 | #[tea(kind = "EarlGrey", with(sugar = "lol", milk))]
|
||||
/// | ^
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Same as above but we factor out most of the logic into a separate function.
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate proc_macro;
|
||||
/// #
|
||||
/// use proc_macro::TokenStream;
|
||||
/// use syn::meta::ParseNestedMeta;
|
||||
/// use syn::parse::{Parser, Result};
|
||||
/// use syn::{parse_macro_input, LitStr, Path};
|
||||
///
|
||||
/// # const IGNORE: &str = stringify! {
|
||||
/// #[proc_macro_attribute]
|
||||
/// # };
|
||||
/// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
/// let mut attrs = TeaAttributes::default();
|
||||
/// let tea_parser = syn::meta::parser(|meta| attrs.parse(meta));
|
||||
/// parse_macro_input!(args with tea_parser);
|
||||
///
|
||||
/// /* ... */
|
||||
/// # TokenStream::new()
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct TeaAttributes {
|
||||
/// kind: Option<LitStr>,
|
||||
/// hot: bool,
|
||||
/// with: Vec<Path>,
|
||||
/// }
|
||||
///
|
||||
/// impl TeaAttributes {
|
||||
/// fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
/// if meta.path.is_ident("kind") {
|
||||
/// self.kind = Some(meta.value()?.parse()?);
|
||||
/// Ok(())
|
||||
/// } else /* just like in last example */
|
||||
/// # { unimplemented!() }
|
||||
///
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn parser(logic: impl FnMut(ParseNestedMeta) -> Result<()>) -> impl Parser<Output = ()> {
|
||||
|input: ParseStream| {
|
||||
if input.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
parse_nested_meta(input, logic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for parsing a single property in the conventional syntax for
|
||||
/// structured attributes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Refer to usage examples on the following two entry-points:
|
||||
///
|
||||
/// - [`Attribute::parse_nested_meta`] if you have an entire `Attribute` to
|
||||
/// parse. Always use this if possible. Generally this is able to produce
|
||||
/// better error messages because `Attribute` holds span information for all
|
||||
/// of the delimiters therein.
|
||||
///
|
||||
/// - [`syn::meta::parser`] if you are implementing a `proc_macro_attribute`
|
||||
/// macro and parsing the arguments to the attribute macro, i.e. the ones
|
||||
/// written in the same attribute that dispatched the macro invocation. Rustc
|
||||
/// does not pass span information for the surrounding delimiters into the
|
||||
/// attribute macro invocation in this situation, so error messages might be
|
||||
/// less precise.
|
||||
///
|
||||
/// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
|
||||
/// [`syn::meta::parser`]: crate::meta::parser
|
||||
#[non_exhaustive]
|
||||
pub struct ParseNestedMeta<'a> {
|
||||
pub path: Path,
|
||||
pub input: ParseStream<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ParseNestedMeta<'a> {
|
||||
/// Used when parsing `key = "value"` syntax.
|
||||
///
|
||||
/// All it does is advance `meta.input` past the `=` sign in the input. You
|
||||
/// could accomplish the same effect by writing
|
||||
/// `meta.parse::<Token![=]>()?`, so at most it is a minor convenience to
|
||||
/// use `meta.value()?`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use syn::{parse_quote, Attribute, LitStr};
|
||||
///
|
||||
/// let attr: Attribute = parse_quote! {
|
||||
/// #[tea(kind = "EarlGrey")]
|
||||
/// };
|
||||
/// // conceptually:
|
||||
/// if attr.path().is_ident("tea") { // this parses the `tea`
|
||||
/// attr.parse_nested_meta(|meta| { // this parses the `(`
|
||||
/// if meta.path.is_ident("kind") { // this parses the `kind`
|
||||
/// let value = meta.value()?; // this parses the `=`
|
||||
/// let s: LitStr = value.parse()?; // this parses `"EarlGrey"`
|
||||
/// if s.value() == "EarlGrey" {
|
||||
/// // ...
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported attribute"))
|
||||
/// }
|
||||
/// })?;
|
||||
/// }
|
||||
/// # anyhow::Ok(())
|
||||
/// ```
|
||||
pub fn value(&self) -> Result<ParseStream<'a>> {
|
||||
self.input.parse::<Token![=]>()?;
|
||||
Ok(self.input)
|
||||
}
|
||||
|
||||
/// Used when parsing `list(...)` syntax **if** the content inside the
|
||||
/// nested parentheses is also expected to conform to Rust's structured
|
||||
/// attribute convention.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use syn::{parse_quote, Attribute};
|
||||
///
|
||||
/// let attr: Attribute = parse_quote! {
|
||||
/// #[tea(with(sugar, milk))]
|
||||
/// };
|
||||
///
|
||||
/// if attr.path().is_ident("tea") {
|
||||
/// attr.parse_nested_meta(|meta| {
|
||||
/// if meta.path.is_ident("with") {
|
||||
/// meta.parse_nested_meta(|meta| { // <---
|
||||
/// if meta.path.is_ident("sugar") {
|
||||
/// // Here we can go even deeper if needed.
|
||||
/// Ok(())
|
||||
/// } else if meta.path.is_ident("milk") {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported ingredient"))
|
||||
/// }
|
||||
/// })
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported tea property"))
|
||||
/// }
|
||||
/// })?;
|
||||
/// }
|
||||
/// # anyhow::Ok(())
|
||||
/// ```
|
||||
///
|
||||
/// # Counterexample
|
||||
///
|
||||
/// If you don't need `parse_nested_meta`'s help in parsing the content
|
||||
/// written within the nested parentheses, keep in mind that you can always
|
||||
/// just parse it yourself from the exposed ParseStream. Rust syntax permits
|
||||
/// arbitrary tokens within those parentheses so for the crazier stuff,
|
||||
/// `parse_nested_meta` is not what you want.
|
||||
///
|
||||
/// ```
|
||||
/// use syn::{parenthesized, parse_quote, Attribute, LitInt};
|
||||
///
|
||||
/// let attr: Attribute = parse_quote! {
|
||||
/// #[repr(align(32))]
|
||||
/// };
|
||||
///
|
||||
/// let mut align: Option<LitInt> = None;
|
||||
/// if attr.path().is_ident("repr") {
|
||||
/// attr.parse_nested_meta(|meta| {
|
||||
/// if meta.path.is_ident("align") {
|
||||
/// let content;
|
||||
/// parenthesized!(content in meta.input);
|
||||
/// align = Some(content.parse()?);
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported repr"))
|
||||
/// }
|
||||
/// })?;
|
||||
/// }
|
||||
/// # anyhow::Ok(())
|
||||
/// ```
|
||||
pub fn parse_nested_meta(
|
||||
&self,
|
||||
logic: impl FnMut(ParseNestedMeta) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let content;
|
||||
parenthesized!(content in self.input);
|
||||
parse_nested_meta(&content, logic)
|
||||
}
|
||||
|
||||
/// Report that the attribute's content did not conform to expectations.
|
||||
///
|
||||
/// The span of the resulting error will cover `meta.path` *and* everything
|
||||
/// that has been parsed so far since it.
|
||||
///
|
||||
/// There are 2 ways you might call this. First, if `meta.path` is not
|
||||
/// something you recognize:
|
||||
///
|
||||
/// ```
|
||||
/// # use syn::Attribute;
|
||||
/// #
|
||||
/// # fn example(attr: &Attribute) -> syn::Result<()> {
|
||||
/// attr.parse_nested_meta(|meta| {
|
||||
/// if meta.path.is_ident("kind") {
|
||||
/// // ...
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(meta.error("unsupported tea property"))
|
||||
/// }
|
||||
/// })?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// In this case, it behaves exactly like
|
||||
/// `syn::Error::new_spanned(&meta.path, "message...")`.
|
||||
///
|
||||
/// ```console
|
||||
/// error: unsupported tea property
|
||||
/// --> src/main.rs:3:26
|
||||
/// |
|
||||
/// 3 | #[tea(kind = "EarlGrey", wat = "foo")]
|
||||
/// | ^^^
|
||||
/// ```
|
||||
///
|
||||
/// More usefully, the second place is if you've already parsed a value but
|
||||
/// have decided not to accept the value:
|
||||
///
|
||||
/// ```
|
||||
/// # use syn::Attribute;
|
||||
/// #
|
||||
/// # fn example(attr: &Attribute) -> syn::Result<()> {
|
||||
/// use syn::Expr;
|
||||
///
|
||||
/// attr.parse_nested_meta(|meta| {
|
||||
/// if meta.path.is_ident("kind") {
|
||||
/// let expr: Expr = meta.value()?.parse()?;
|
||||
/// match expr {
|
||||
/// Expr::Lit(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// Expr::Path(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// Expr::Macro(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// _ => Err(meta.error("tea kind must be a string literal, path, or macro")),
|
||||
/// }
|
||||
/// } else /* as above */
|
||||
/// # { unimplemented!() }
|
||||
///
|
||||
/// })?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```console
|
||||
/// error: tea kind must be a string literal, path, or macro
|
||||
/// --> src/main.rs:3:7
|
||||
/// |
|
||||
/// 3 | #[tea(kind = async { replicator.await })]
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
///
|
||||
/// Often you may want to use `syn::Error::new_spanned` even in this
|
||||
/// situation. In the above code, that would be:
|
||||
///
|
||||
/// ```
|
||||
/// # use syn::{Error, Expr};
|
||||
/// #
|
||||
/// # fn example(expr: Expr) -> syn::Result<()> {
|
||||
/// match expr {
|
||||
/// Expr::Lit(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// Expr::Path(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// Expr::Macro(expr) => /* ... */
|
||||
/// # unimplemented!(),
|
||||
/// _ => Err(Error::new_spanned(expr, "unsupported expression type for `kind`")),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```console
|
||||
/// error: unsupported expression type for `kind`
|
||||
/// --> src/main.rs:3:14
|
||||
/// |
|
||||
/// 3 | #[tea(kind = async { replicator.await })]
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
pub fn error(&self, msg: impl Display) -> Error {
|
||||
let start_span = self.path.segments[0].ident.span();
|
||||
let end_span = self.input.cursor().prev_span();
|
||||
crate::error::new2(start_span, end_span, msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_nested_meta(
|
||||
input: ParseStream,
|
||||
mut logic: impl FnMut(ParseNestedMeta) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
let path = input.call(parse_meta_path)?;
|
||||
logic(ParseNestedMeta { path, input })?;
|
||||
if input.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
input.parse::<Token![,]>()?;
|
||||
if input.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
if input.peek(Ident::peek_any) {
|
||||
let ident = Ident::parse_any(input)?;
|
||||
segments.push_value(PathSegment::from(ident));
|
||||
} else if input.is_empty() {
|
||||
return Err(input.error("expected nested attribute"));
|
||||
} else if input.peek(Lit) {
|
||||
return Err(input.error("unexpected literal in nested attribute, expected ident"));
|
||||
} else {
|
||||
return Err(input.error("unexpected token in nested attribute, expected ident"));
|
||||
}
|
||||
while input.peek(Token![::]) {
|
||||
let punct = input.parse()?;
|
||||
segments.push_punct(punct);
|
||||
let ident = Ident::parse_any(input)?;
|
||||
segments.push_value(PathSegment::from(ident));
|
||||
}
|
||||
segments
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user