upgrade to syn 2.x

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-07-26 20:40:07 +00:00
parent 3b5607ecdc
commit ca82b59c6f
11 changed files with 138 additions and 99 deletions

3
Cargo.lock generated
View file

@ -731,9 +731,10 @@ dependencies = [
name = "conduit_macros"
version = "0.4.5"
dependencies = [
"itertools 0.13.0",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.71",
]
[[package]]

View file

@ -426,7 +426,7 @@ default-features = false
version = "0.1"
[workspace.dependencies.syn]
version = "1.0"
version = "2.0"
features = ["full", "extra-traits"]
[workspace.dependencies.quote]

View file

@ -16,19 +16,19 @@ use crate::Result;
#[cargo_manifest]
const WORKSPACE_MANIFEST: &'static str = ();
#[cargo_manifest("macros")]
#[cargo_manifest(crate = "macros")]
const MACROS_MANIFEST: &'static str = ();
#[cargo_manifest("core")]
#[cargo_manifest(crate = "core")]
const CORE_MANIFEST: &'static str = ();
#[cargo_manifest("database")]
#[cargo_manifest(crate = "database")]
const DATABASE_MANIFEST: &'static str = ();
#[cargo_manifest("service")]
#[cargo_manifest(crate = "service")]
const SERVICE_MANIFEST: &'static str = ();
#[cargo_manifest("admin")]
#[cargo_manifest(crate = "admin")]
const ADMIN_MANIFEST: &'static str = ();
#[cargo_manifest("router")]
#[cargo_manifest(crate = "router")]
const ROUTER_MANIFEST: &'static str = ();
#[cargo_manifest("main")]
#[cargo_manifest(crate = "main")]
const MAIN_MANIFEST: &'static str = ();
/// Processed list of features access all project crates. This is generated from

View file

@ -18,6 +18,7 @@ proc-macro = true
syn.workspace = true
quote.workspace = true
proc-macro2.workspace = true
itertools.workspace = true
[lints]
workspace = true

View file

@ -1,17 +1,15 @@
use itertools::Itertools;
use proc_macro::{Span, TokenStream};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, Fields, Ident, ItemEnum, Variant};
use quote::{quote, ToTokens};
use syn::{Error, Fields, Ident, ItemEnum, Meta, Variant};
use crate::utils::camel_to_snake_string;
use crate::{utils::camel_to_snake_string, Result};
pub(super) fn command_dispatch(args: TokenStream, input_: TokenStream) -> TokenStream {
let input = input_.clone();
let item = parse_macro_input!(input as ItemEnum);
let _args = parse_macro_input!(args as AttributeArgs);
let arm = item.variants.iter().map(dispatch_arm);
let name = item.ident;
let q = quote! {
pub(super) fn command_dispatch(item: ItemEnum, _args: &[Meta]) -> Result<TokenStream> {
let name = &item.ident;
let arm: Vec<TokenStream2> = item.variants.iter().map(dispatch_arm).try_collect()?;
let switch = quote! {
pub(super) async fn process(command: #name, body: Vec<&str>) -> Result<RoomMessageEventContent> {
use #name::*;
#[allow(non_snake_case)]
@ -21,14 +19,17 @@ pub(super) fn command_dispatch(args: TokenStream, input_: TokenStream) -> TokenS
}
};
[input_, q.into()].into_iter().collect::<TokenStream>()
Ok([item.into_token_stream(), switch]
.into_iter()
.collect::<TokenStream2>()
.into())
}
fn dispatch_arm(v: &Variant) -> TokenStream2 {
fn dispatch_arm(v: &Variant) -> Result<TokenStream2> {
let name = &v.ident;
let target = camel_to_snake_string(&format!("{name}"));
let handler = Ident::new(&target, Span::call_site().into());
match &v.fields {
let res = match &v.fields {
Fields::Named(fields) => {
let field = fields.named.iter().filter_map(|f| f.ident.as_ref());
let arg = field.clone();
@ -37,7 +38,9 @@ fn dispatch_arm(v: &Variant) -> TokenStream2 {
}
},
Fields::Unnamed(fields) => {
let field = &fields.unnamed.first().expect("one field");
let Some(ref field) = fields.unnamed.first() else {
return Err(Error::new(Span::call_site().into(), "One unnamed field required"));
};
quote! {
#name ( #field ) => Box::pin(#handler::process(#field, body)).await?,
}
@ -47,5 +50,7 @@ fn dispatch_arm(v: &Variant) -> TokenStream2 {
#name => Box::pin(#handler(&body)).await?,
}
},
}
};
Ok(res)
}

View file

@ -1,39 +1,34 @@
use std::{fs::read_to_string, path::PathBuf};
use proc_macro::TokenStream;
use proc_macro::{Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, ItemConst, Lit, NestedMeta};
use syn::{Error, ItemConst, Meta};
pub(super) fn manifest(args: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as ItemConst);
let args = parse_macro_input!(args as AttributeArgs);
let member = args.into_iter().find_map(|arg| {
let NestedMeta::Lit(arg) = arg else {
return None;
};
let Lit::Str(arg) = arg else {
return None;
};
Some(arg.value())
});
use crate::{utils, Result};
let path = manifest_path(member.as_deref());
pub(super) fn manifest(item: ItemConst, args: &[Meta]) -> Result<TokenStream> {
let member = utils::get_named_string(args, "crate");
let path = manifest_path(member.as_deref())?;
let manifest = read_to_string(&path).unwrap_or_default();
let name = item.ident;
let val = manifest.as_str();
let name = item.ident;
let ret = quote! {
const #name: &'static str = #val;
};
ret.into()
Ok(ret.into())
}
#[allow(clippy::option_env_unwrap)]
fn manifest_path(member: Option<&str>) -> PathBuf {
let mut path: PathBuf = option_env!("CARGO_MANIFEST_DIR")
.expect("missing CARGO_MANIFEST_DIR in environment")
.into();
fn manifest_path(member: Option<&str>) -> Result<PathBuf> {
let Some(path) = option_env!("CARGO_MANIFEST_DIR") else {
return Err(Error::new(
Span::call_site().into(),
"missing CARGO_MANIFEST_DIR in environment",
));
};
let mut path: PathBuf = path.into();
// conduwuit/src/macros/ -> conduwuit/src/
path.pop();
@ -47,5 +42,6 @@ fn manifest_path(member: Option<&str>) -> PathBuf {
}
path.push("Cargo.toml");
path
Ok(path)
}

View file

@ -1,13 +1,12 @@
use std::cmp;
use proc_macro::TokenStream;
use syn::{parse_macro_input, AttributeArgs, Item};
use quote::ToTokens;
use syn::{Item, Meta};
pub(super) fn recursion_depth(args: TokenStream, item_: TokenStream) -> TokenStream {
let item = item_.clone();
let item = parse_macro_input!(item as Item);
let _args = parse_macro_input!(args as AttributeArgs);
use crate::Result;
pub(super) fn recursion_depth(item: Item, _args: &[Meta]) -> Result<TokenStream> {
let mut best: usize = 0;
let mut count: usize = 0;
// think you'd find a fancy recursive ast visitor? think again
@ -24,5 +23,5 @@ pub(super) fn recursion_depth(args: TokenStream, item_: TokenStream) -> TokenStr
println!("DEPTH: {best}");
println!("LENGTH: {count}");
item_
Ok(item.into_token_stream().into())
}

View file

@ -1,23 +1,26 @@
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, AttributeArgs, ItemFn, Meta, NestedMeta};
use quote::quote;
use syn::{ItemFn, Meta, MetaList};
pub(super) fn implement(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let item = parse_macro_input!(input as ItemFn);
use crate::Result;
let NestedMeta::Meta(Meta::Path(receiver)) = args
pub(super) fn implement(item: ItemFn, args: &[Meta]) -> Result<TokenStream> {
let Meta::List(MetaList {
path,
..
}) = &args
.first()
.expect("missing path to trait or item to implement")
else {
panic!("invalid path to item for implement");
};
let input = item;
let out = quote! {
impl #receiver {
#item
impl #path {
#input
}
};
out.into_token_stream().into()
Ok(out.into())
}

View file

@ -7,23 +7,50 @@ mod rustc;
mod utils;
use proc_macro::TokenStream;
use syn::{
parse::{Parse, Parser},
parse_macro_input, Error, Item, ItemConst, ItemEnum, ItemFn, Meta,
};
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[proc_macro_attribute]
pub fn admin_command_dispatch(args: TokenStream, input: TokenStream) -> TokenStream {
admin::command_dispatch(args, input)
attribute_macro::<ItemEnum, _>(args, input, admin::command_dispatch)
}
#[proc_macro_attribute]
pub fn cargo_manifest(args: TokenStream, input: TokenStream) -> TokenStream { cargo::manifest(args, input) }
pub fn cargo_manifest(args: TokenStream, input: TokenStream) -> TokenStream {
attribute_macro::<ItemConst, _>(args, input, cargo::manifest)
}
#[proc_macro_attribute]
pub fn recursion_depth(args: TokenStream, input: TokenStream) -> TokenStream { debug::recursion_depth(args, input) }
pub fn recursion_depth(args: TokenStream, input: TokenStream) -> TokenStream {
attribute_macro::<Item, _>(args, input, debug::recursion_depth)
}
#[proc_macro]
pub fn rustc_flags_capture(args: TokenStream) -> TokenStream { rustc::flags_capture(args) }
#[proc_macro_attribute]
pub fn refutable(args: TokenStream, input: TokenStream) -> TokenStream { refutable::refutable(args, input) }
pub fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
attribute_macro::<ItemFn, _>(args, input, refutable::refutable)
}
#[proc_macro_attribute]
pub fn implement(args: TokenStream, input: TokenStream) -> TokenStream { implement::implement(args, input) }
pub fn implement(args: TokenStream, input: TokenStream) -> TokenStream {
attribute_macro::<ItemFn, _>(args, input, implement::implement)
}
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
where
F: Fn(I, &[Meta]) -> Result<TokenStream>,
I: Parse,
{
let item = parse_macro_input!(input as I);
syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated
.parse(args)
.map(|args| args.iter().cloned().collect::<Vec<_>>())
.and_then(|ref args| func(item, args))
.unwrap_or_else(|e| e.to_compile_error().into())
}

View file

@ -1,11 +1,10 @@
use proc_macro::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, AttributeArgs, FnArg::Typed, Ident, ItemFn, Pat, PatIdent, PatType, Stmt};
use syn::{FnArg::Typed, Ident, ItemFn, Meta, Pat, PatIdent, PatType, Stmt};
pub(super) fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
let _args = parse_macro_input!(args as AttributeArgs);
let mut item = parse_macro_input!(input as ItemFn);
use crate::Result;
pub(super) fn refutable(mut item: ItemFn, _args: &[Meta]) -> Result<TokenStream> {
let inputs = item.sig.inputs.clone();
let stmt = &mut item.block.stmts;
let sig = &mut item.sig;
@ -25,37 +24,30 @@ pub(super) fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
let variant = &pat.path;
let fields = &pat.fields;
// new versions of syn can replace this kronecker kludge with get_mut()
for (j, input) in sig.inputs.iter_mut().enumerate() {
if i != j {
continue;
}
let Some(Typed(PatType {
ref mut pat,
..
})) = sig.inputs.get_mut(i)
else {
continue;
};
let Typed(PatType {
ref mut pat,
..
}) = input
else {
continue;
};
let name = format!("_args_{i}");
*pat = Box::new(Pat::Ident(PatIdent {
ident: Ident::new(&name, Span::call_site().into()),
attrs: Vec::new(),
by_ref: None,
mutability: None,
subpat: None,
}));
let name = format!("_args_{i}");
*pat = Box::new(Pat::Ident(PatIdent {
ident: Ident::new(&name, Span::call_site().into()),
attrs: Vec::new(),
by_ref: None,
mutability: None,
subpat: None,
}));
let field = fields.iter();
let refute = quote! {
let #variant { #( #field ),*, .. } = #name else { panic!("incorrect variant passed to function argument {i}"); };
};
let field = fields.iter();
let refute = quote! {
let #variant { #( #field ),*, .. } = #name else { panic!("incorrect variant passed to function argument {i}"); };
};
stmt.insert(0, syn::parse2::<Stmt>(refute).expect("syntax error"));
}
stmt.insert(0, syn::parse2::<Stmt>(refute)?);
}
item.into_token_stream().into()
Ok(item.into_token_stream().into())
}

View file

@ -1,3 +1,18 @@
use syn::{Expr, Lit, Meta};
pub(crate) fn get_named_string(args: &[Meta], name: &str) -> Option<String> {
args.iter().find_map(|arg| {
let value = arg.require_name_value().ok()?;
let Expr::Lit(ref lit) = value.value else {
return None;
};
let Lit::Str(ref str) = lit.lit else {
return None;
};
value.path.is_ident(name).then_some(str.value())
})
}
#[must_use]
pub(crate) fn camel_to_snake_string(s: &str) -> String {
let mut output = String::with_capacity(