mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-23 05:07:25 +00:00
These are never available in musl, so introduce easier ways to skip them rather than needing to exclude f16/f128 functions in three different places.
297 lines
8.7 KiB
Rust
297 lines
8.7 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use proc_macro2::Span;
|
|
use quote::ToTokens;
|
|
use syn::parse::{Parse, ParseStream, Parser};
|
|
use syn::punctuated::Punctuated;
|
|
use syn::spanned::Spanned;
|
|
use syn::token::{self, Comma};
|
|
use syn::{Arm, Attribute, Expr, ExprMatch, Ident, LitBool, Meta, Token, bracketed};
|
|
|
|
/// The input to our macro; just a list of `field: value` items.
|
|
#[derive(Debug)]
|
|
pub struct Invocation {
|
|
fields: Punctuated<Mapping, Comma>,
|
|
}
|
|
|
|
impl Parse for Invocation {
|
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
Ok(Self {
|
|
fields: input.parse_terminated(Mapping::parse, Token![,])?,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// A `key: expression` mapping with nothing else. Basically a simplified `syn::Field`.
|
|
#[derive(Debug)]
|
|
struct Mapping {
|
|
name: Ident,
|
|
_sep: Token![:],
|
|
expr: Expr,
|
|
}
|
|
|
|
impl Parse for Mapping {
|
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
Ok(Self {
|
|
name: input.parse()?,
|
|
_sep: input.parse()?,
|
|
expr: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// The input provided to our proc macro, after parsing into the form we expect.
|
|
#[derive(Debug)]
|
|
pub struct StructuredInput {
|
|
/// Macro to invoke once per function
|
|
pub callback: Ident,
|
|
/// Whether or not to provide `CFn` `CArgs` `RustFn` etc. This is really only needed
|
|
/// once for crate to set up the main trait.
|
|
pub emit_types: Vec<Ident>,
|
|
/// Skip these functions
|
|
pub skip: Vec<Ident>,
|
|
/// If true, omit f16 and f128 functions that aren't present in other libraries.
|
|
pub skip_f16_f128: bool,
|
|
/// Invoke only for these functions
|
|
pub only: Option<Vec<Ident>>,
|
|
/// Attributes that get applied to specific functions
|
|
pub attributes: Option<Vec<AttributeMap>>,
|
|
/// Extra expressions to pass to all invocations of the macro
|
|
pub extra: Option<Expr>,
|
|
/// Per-function extra expressions to pass to the macro
|
|
pub fn_extra: Option<BTreeMap<Ident, Expr>>,
|
|
// For diagnostics
|
|
pub emit_types_span: Option<Span>,
|
|
pub only_span: Option<Span>,
|
|
pub fn_extra_span: Option<Span>,
|
|
}
|
|
|
|
impl StructuredInput {
|
|
pub fn from_fields(input: Invocation) -> syn::Result<Self> {
|
|
let mut map: Vec<_> = input.fields.into_iter().collect();
|
|
let cb_expr = expect_field(&mut map, "callback")?;
|
|
let emit_types_expr = expect_field(&mut map, "emit_types").ok();
|
|
let skip_expr = expect_field(&mut map, "skip").ok();
|
|
let skip_f16_f128 = expect_field(&mut map, "skip_f16_f128").ok();
|
|
let only_expr = expect_field(&mut map, "only").ok();
|
|
let attr_expr = expect_field(&mut map, "attributes").ok();
|
|
let extra = expect_field(&mut map, "extra").ok();
|
|
let fn_extra = expect_field(&mut map, "fn_extra").ok();
|
|
|
|
if !map.is_empty() {
|
|
Err(syn::Error::new(
|
|
map.first().unwrap().name.span(),
|
|
format!("unexpected fields {map:?}"),
|
|
))?;
|
|
}
|
|
|
|
let emit_types_span = emit_types_expr.as_ref().map(|expr| expr.span());
|
|
let emit_types = match emit_types_expr {
|
|
Some(expr) => Parser::parse2(parse_ident_or_array, expr.into_token_stream())?,
|
|
None => Vec::new(),
|
|
};
|
|
|
|
let skip = match skip_expr {
|
|
Some(expr) => Parser::parse2(parse_ident_array, expr.into_token_stream())?,
|
|
None => Vec::new(),
|
|
};
|
|
|
|
let skip_f16_f128 = match skip_f16_f128 {
|
|
Some(expr) => expect_litbool(expr)?.value,
|
|
None => false,
|
|
};
|
|
|
|
let only_span = only_expr.as_ref().map(|expr| expr.span());
|
|
let only = match only_expr {
|
|
Some(expr) => Some(Parser::parse2(parse_ident_array, expr.into_token_stream())?),
|
|
None => None,
|
|
};
|
|
|
|
let attributes = match attr_expr {
|
|
Some(expr) => {
|
|
let mut attributes = Vec::new();
|
|
let attr_exprs = Parser::parse2(parse_expr_array, expr.into_token_stream())?;
|
|
|
|
for attr in attr_exprs {
|
|
attributes.push(syn::parse2(attr.into_token_stream())?);
|
|
}
|
|
Some(attributes)
|
|
}
|
|
None => None,
|
|
};
|
|
|
|
let fn_extra_span = fn_extra.as_ref().map(|expr| expr.span());
|
|
let fn_extra = match fn_extra {
|
|
Some(expr) => Some(extract_fn_extra_field(expr)?),
|
|
None => None,
|
|
};
|
|
|
|
Ok(Self {
|
|
callback: expect_ident(cb_expr)?,
|
|
emit_types,
|
|
skip,
|
|
skip_f16_f128,
|
|
only,
|
|
only_span,
|
|
attributes,
|
|
extra,
|
|
fn_extra,
|
|
fn_extra_span,
|
|
emit_types_span,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn extract_fn_extra_field(expr: Expr) -> syn::Result<BTreeMap<Ident, Expr>> {
|
|
let Expr::Match(mexpr) = expr else {
|
|
let e = syn::Error::new(expr.span(), "`fn_extra` expects a match expression");
|
|
return Err(e);
|
|
};
|
|
|
|
let ExprMatch {
|
|
attrs,
|
|
match_token: _,
|
|
expr,
|
|
brace_token: _,
|
|
arms,
|
|
} = mexpr;
|
|
|
|
expect_empty_attrs(&attrs)?;
|
|
|
|
let match_on = expect_ident(*expr)?;
|
|
if match_on != "MACRO_FN_NAME" {
|
|
let e = syn::Error::new(match_on.span(), "only allowed to match on `MACRO_FN_NAME`");
|
|
return Err(e);
|
|
}
|
|
|
|
let mut res = BTreeMap::new();
|
|
|
|
for arm in arms {
|
|
let Arm {
|
|
attrs,
|
|
pat,
|
|
guard,
|
|
fat_arrow_token: _,
|
|
body,
|
|
comma: _,
|
|
} = arm;
|
|
|
|
expect_empty_attrs(&attrs)?;
|
|
|
|
let keys = match pat {
|
|
syn::Pat::Wild(w) => vec![Ident::new("_", w.span())],
|
|
_ => Parser::parse2(parse_ident_pat, pat.into_token_stream())?,
|
|
};
|
|
|
|
if let Some(guard) = guard {
|
|
let e = syn::Error::new(guard.0.span(), "no guards allowed in this position");
|
|
return Err(e);
|
|
}
|
|
|
|
for key in keys {
|
|
let inserted = res.insert(key.clone(), *body.clone());
|
|
if inserted.is_some() {
|
|
let e = syn::Error::new(key.span(), format!("key `{key}` specified twice"));
|
|
return Err(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(res)
|
|
}
|
|
|
|
fn expect_empty_attrs(attrs: &[Attribute]) -> syn::Result<()> {
|
|
if attrs.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let e = syn::Error::new(
|
|
attrs.first().unwrap().span(),
|
|
"no attributes allowed in this position",
|
|
);
|
|
Err(e)
|
|
}
|
|
|
|
/// Extract a named field from a map, raising an error if it doesn't exist.
|
|
fn expect_field(v: &mut Vec<Mapping>, name: &str) -> syn::Result<Expr> {
|
|
let pos = v.iter().position(|v| v.name == name).ok_or_else(|| {
|
|
syn::Error::new(
|
|
Span::call_site(),
|
|
format!("missing expected field `{name}`"),
|
|
)
|
|
})?;
|
|
|
|
Ok(v.remove(pos).expr)
|
|
}
|
|
|
|
/// Coerce an expression into a simple identifier.
|
|
fn expect_ident(expr: Expr) -> syn::Result<Ident> {
|
|
syn::parse2(expr.into_token_stream())
|
|
}
|
|
|
|
/// Coerce an expression into a simple keyword.
|
|
fn expect_litbool(expr: Expr) -> syn::Result<LitBool> {
|
|
syn::parse2(expr.into_token_stream())
|
|
}
|
|
|
|
/// Parse either a single identifier (`foo`) or an array of identifiers (`[foo, bar, baz]`).
|
|
fn parse_ident_or_array(input: ParseStream) -> syn::Result<Vec<Ident>> {
|
|
if !input.peek(token::Bracket) {
|
|
return Ok(vec![input.parse()?]);
|
|
}
|
|
|
|
parse_ident_array(input)
|
|
}
|
|
|
|
/// Parse an array of expressions.
|
|
fn parse_expr_array(input: ParseStream) -> syn::Result<Vec<Expr>> {
|
|
let content;
|
|
let _ = bracketed!(content in input);
|
|
let fields = content.parse_terminated(Expr::parse, Token![,])?;
|
|
Ok(fields.into_iter().collect())
|
|
}
|
|
|
|
/// Parse an array of idents, e.g. `[foo, bar, baz]`.
|
|
fn parse_ident_array(input: ParseStream) -> syn::Result<Vec<Ident>> {
|
|
let content;
|
|
let _ = bracketed!(content in input);
|
|
let fields = content.parse_terminated(Ident::parse, Token![,])?;
|
|
Ok(fields.into_iter().collect())
|
|
}
|
|
|
|
/// Parse an pattern of idents, specifically `(foo | bar | baz)`.
|
|
fn parse_ident_pat(input: ParseStream) -> syn::Result<Vec<Ident>> {
|
|
if !input.peek2(Token![|]) {
|
|
return Ok(vec![input.parse()?]);
|
|
}
|
|
|
|
let fields = Punctuated::<Ident, Token![|]>::parse_separated_nonempty(input)?;
|
|
Ok(fields.into_iter().collect())
|
|
}
|
|
|
|
/// A mapping of attributes to identifiers (just a simplified `Expr`).
|
|
///
|
|
/// Expressed as:
|
|
///
|
|
/// ```ignore
|
|
/// #[meta1]
|
|
/// #[meta2]
|
|
/// [foo, bar, baz]
|
|
/// ```
|
|
#[derive(Debug)]
|
|
pub struct AttributeMap {
|
|
pub meta: Vec<Meta>,
|
|
pub names: Vec<Ident>,
|
|
}
|
|
|
|
impl Parse for AttributeMap {
|
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
|
|
Ok(Self {
|
|
meta: attrs.into_iter().map(|a| a.meta).collect(),
|
|
names: parse_ident_array(input)?,
|
|
})
|
|
}
|
|
}
|