diff --git a/Cargo.lock b/Cargo.lock index ba697e0..9254a98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,14 +3,52 @@ version = 4 [[package]] -name = "example" -version = "1.1.0" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a23e249717cd6d24a8f3fcd9639403948df1227fe303a106396597a68277e" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] [[package]] name = "splat-overload" version = "0.0.0" dependencies = [ - "example", + "proc-macro2", + "quote", + "syn", ] + +[[package]] +name = "splat-overload-test" +version = "0.1.0" +dependencies = [ + "splat-overload", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/Cargo.toml b/Cargo.toml index b9804b5..32d23ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,11 @@ resolver = "3" members = [ "splat-overload", + "splat-overload-test" ] default-members = [ "splat-overload", + "splat-overload-test" ] [workspace.package] @@ -24,7 +26,7 @@ readme = "README.md" repository = "https://github.com/rustfoundation/overloading-macros" # Requires #[splat] which was introduced just before 1.99 branched rust-version = "1.99" - [workspace.dependencies] -# Delete this package and replace it with the actual dependencies -example = "1.1.0" \ No newline at end of file +syn = { version = "2.0.119", features = ["full"] } +quote = "1.0.46" +proc-macro2 = "1.0.106" \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/splat-overload-test/Cargo.toml b/splat-overload-test/Cargo.toml new file mode 100644 index 0000000..0f37e13 --- /dev/null +++ b/splat-overload-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "splat-overload-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +splat-overload = { path = "../splat-overload" } diff --git a/splat-overload-test/rust-toolchain.toml b/splat-overload-test/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/splat-overload-test/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/splat-overload-test/src/bin/multiple-args.rs b/splat-overload-test/src/bin/multiple-args.rs new file mode 100644 index 0000000..cee9f89 --- /dev/null +++ b/splat-overload-test/src/bin/multiple-args.rs @@ -0,0 +1,23 @@ +#![feature(splat)] +#![feature(tuple_trait)] +#![allow(incomplete_features, clippy::approx_constant)] + +use splat_overload::overload; + +overload! { + fn foo(x: i32, y: f64) { + println!("i32: {}, f64: {}", x, y); + } + fn foo(x: bool, y: i32, z: f64) { + println!("bool: {}, i32: {}, f64: {}", x, y, z); + } + fn foo(a: i32, b: f64, c: bool, d: u8) { + println!("i32: {}, f64: {}, bool: {}, u8: {}", a, b, c, d); + } +} + +fn main() { + foo(42, 3.14); + foo(true, 42, 3.14); + foo(42, 3.14, true, 255); +} diff --git a/splat-overload-test/src/bin/multiple-mixed-args.rs b/splat-overload-test/src/bin/multiple-mixed-args.rs new file mode 100644 index 0000000..0d79f0e --- /dev/null +++ b/splat-overload-test/src/bin/multiple-mixed-args.rs @@ -0,0 +1,27 @@ +#![feature(splat)] +#![feature(tuple_trait)] +#![allow(incomplete_features)] + +use splat_overload::overload; + +overload! { + fn calculate(a: i32, b: i32) { + println!("sum: {}", a + b); + } + fn calculate(a: f64, b: f64, c: f64) { + println!("average: {}", (a + b + c) / 3.0); + } + fn calculate(x: i32, y: i32, z: i32, w: i32) { + println!("product: {}", x * y * z * w); + } + fn calculate(a: f64, b: f64, c: f64, d: f64, e: f64) { + println!("max would need std: {} {} {} {} {}", a, b, c, d, e); + } +} + +fn main() { + calculate(10, 20); + calculate(1.0, 2.0, 3.0); + calculate(2, 3, 4, 5); + calculate(1.0, 2.0, 3.0, 4.0, 5.0); +} diff --git a/splat-overload/Cargo.toml b/splat-overload/Cargo.toml index 4dfc053..2ebc909 100644 --- a/splat-overload/Cargo.toml +++ b/splat-overload/Cargo.toml @@ -1,4 +1,3 @@ -# Modify the workspace Cargo.toml instead of this file (if possible) [package] name = "splat-overload" version.workspace = true @@ -11,6 +10,10 @@ readme.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +proc-macro = true + [dependencies] -# Delete this package and replace it with the actual dependencies -example.workspace = true +syn.workspace = true +quote.workspace = true +proc-macro2.workspace = true diff --git a/splat-overload/src/lib.rs b/splat-overload/src/lib.rs index 793dee2..2e955ab 100644 --- a/splat-overload/src/lib.rs +++ b/splat-overload/src/lib.rs @@ -1 +1,89 @@ -//! Delete this comment and add the actual code here +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + FnArg, ItemFn, Pat, Result, + parse::{Parse, ParseStream}, + parse_macro_input, +}; + +struct OverloadInput { + functions: Vec, +} + +impl Parse for OverloadInput { + fn parse(input: ParseStream) -> Result { + let mut functions = Vec::new(); + while !input.is_empty() { + functions.push(input.parse::()?); + } + Ok(OverloadInput { functions }) + } +} + +#[proc_macro] +pub fn overload(input: TokenStream) -> TokenStream { + let OverloadInput { functions } = parse_macro_input!(input as OverloadInput); + + let fn_name = &functions[0].sig.ident; + + let trait_name = quote::format_ident!( + "{}Args", + fn_name + .to_string() + .chars() + .enumerate() + .map(|(i, c)| if i == 0 { + c.to_uppercase().next().unwrap() + } else { + c + }) + .collect::() + ); + + let mut impls = Vec::new(); + for func in &functions { + // Collect All arguments and names + let mut arg_types = Vec::new(); + let mut arg_names = Vec::new(); + let mut arg_indices = Vec::new(); + let block = &func.block; + for (i, arg) in func.sig.inputs.iter().enumerate() { + if let FnArg::Typed(pat_type) = arg { + let ty = &pat_type.ty; + arg_types.push(quote! { #ty }); + + let arg_name = if let Pat::Ident(pat_ident) = &*pat_type.pat { + let ident = &pat_ident.ident; + quote! { #ident } + } else { + quote! { _arg } + }; + arg_names.push(arg_name); + + let index = syn::Index::from(i); + arg_indices.push(quote! { self.#index }); + } + } + impls.push(quote! { + impl #trait_name for (#(#arg_types),*,) { + fn call(self) { + #(let #arg_names = #arg_indices;)* + #block + } + } + }); + } + let generated = quote! { + trait #trait_name: std::marker::Tuple { + fn call(self); + } + + #(#impls)* + + fn #fn_name(#[splat] args: T) { + args.call() + } + }; + + generated.into() +}