From 9277b7637ee0792a2b66ea11d76eb1c54c5cb746 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 17 Mar 2024 13:54:45 -0700 Subject: [PATCH 1/9] refactor: break SolInputs to its own crate --- Cargo.toml | 1 + crates/sol-macro-input/Cargo.toml | 26 +++ crates/sol-macro-input/src/expander.rs | 9 + crates/sol-macro-input/src/input.rs | 138 +++++++++++++++ .../src/json.rs | 110 +++++++----- crates/sol-macro-input/src/lib.rs | 26 +++ crates/sol-macro/Cargo.toml | 6 +- crates/sol-macro/src/input.rs | 164 ------------------ crates/sol-macro/src/lib.rs | 59 +++++-- 9 files changed, 318 insertions(+), 221 deletions(-) create mode 100644 crates/sol-macro-input/Cargo.toml create mode 100644 crates/sol-macro-input/src/expander.rs create mode 100644 crates/sol-macro-input/src/input.rs rename crates/{sol-macro => sol-macro-input}/src/json.rs (79%) create mode 100644 crates/sol-macro-input/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 5e72f6bcf..aa098e247 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ alloy-dyn-abi = { version = "0.6.4", path = "crates/dyn-abi", default-features = alloy-json-abi = { version = "0.6.4", path = "crates/json-abi", default-features = false } alloy-primitives = { version = "0.6.4", path = "crates/primitives", default-features = false } alloy-sol-macro = { version = "0.6.4", path = "crates/sol-macro", default-features = false } +alloy-sol-macro-input = { version = "0.6.4", path = "crates/sol-macro-input", default-features = false } alloy-sol-type-parser = { version = "0.6.4", path = "crates/sol-type-parser", default-features = false } alloy-sol-types = { version = "0.6.4", path = "crates/sol-types", default-features = false } syn-solidity = { version = "0.6.4", path = "crates/syn-solidity", default-features = false } diff --git a/crates/sol-macro-input/Cargo.toml b/crates/sol-macro-input/Cargo.toml new file mode 100644 index 000000000..a72a4fc6f --- /dev/null +++ b/crates/sol-macro-input/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "alloy-sol-macro-input" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dunce = "1.0.4" +proc-macro2.workspace = true +syn.workspace = true +syn-solidity.workspace = true + +# json +alloy-json-abi = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } +quote = { workspace = true, optional = true } + +[features] +json = ["dep:alloy-json-abi", "dep:serde_json", "dep:quote"] diff --git a/crates/sol-macro-input/src/expander.rs b/crates/sol-macro-input/src/expander.rs new file mode 100644 index 000000000..b73ade9dd --- /dev/null +++ b/crates/sol-macro-input/src/expander.rs @@ -0,0 +1,9 @@ +use proc_macro2::TokenStream; + +use crate::SolInput; + +/// Expands a `SolInput` into a `TokenStream`. +pub trait SolInputExpander { + /// Expand a `SolInput` into a `TokenStream`. + fn expand(&mut self, input: SolInput) -> syn::Result; +} diff --git a/crates/sol-macro-input/src/input.rs b/crates/sol-macro-input/src/input.rs new file mode 100644 index 000000000..78815e11d --- /dev/null +++ b/crates/sol-macro-input/src/input.rs @@ -0,0 +1,138 @@ +use ast::Spanned; +use std::path::PathBuf; +use syn::{ + parse::{discouraged::Speculative, Parse, ParseStream}, + Attribute, Error, Ident, LitStr, Result, Token, +}; + +/// Parsed input for `sol!`-like macro expanders. This enum represents a `Sol` file, a JSON ABI, or +/// a Solidity type. +#[derive(Clone, Debug)] +pub enum SolInputKind { + /// Solidity type. + Type(ast::Type), + /// Solidity file or snippet. + Sol(ast::File), + /// JSON ABI file + #[cfg(feature = "json")] + Json(Ident, alloy_json_abi::ContractObject), +} + +impl Parse for SolInputKind { + fn parse(input: ParseStream<'_>) -> Result { + let fork = input.fork(); + match fork.parse() { + Ok(file) => { + input.advance_to(&fork); + Ok(Self::Sol(file)) + } + Err(e) => match input.parse() { + Ok(ast::Type::Custom(_)) | Err(_) => Err(e), + + Ok(ast::Type::Mapping(m)) => { + Err(Error::new(m.span(), "mapping types are not yet supported")) + } + + Ok(ty) => Ok(Self::Type(ty)), + }, + } + } +} + +/// Parsed input for `sol!`-like macro expanders. This struct represents a list +/// of expandable items parsed from either solidity code snippets, or from a +/// JSON abi. +#[derive(Clone, Debug)] +pub struct SolInput { + /// Attributes attached to the input, of the form `#[...]`. + pub attrs: Vec, + /// Path to the input, if any. + pub path: Option, + /// The input kind. + pub kind: SolInputKind, +} + +impl Parse for SolInput { + fn parse(input: ParseStream<'_>) -> Result { + let attrs = Attribute::parse_inner(input)?; + + // ignore attributes when peeking + let fork = input.fork(); + let _inner = Attribute::parse_outer(&fork)?; + + if fork.peek(LitStr) || (fork.peek(Ident) && fork.peek2(Token![,]) && fork.peek3(LitStr)) { + Self::parse_abigen(attrs, input) + } else { + input.parse().map(|kind| Self { attrs, path: None, kind }) + } + } +} + +impl SolInput { + /// `abigen`-like syntax: `sol!(name, "path/to/file")` + fn parse_abigen(mut attrs: Vec, input: ParseStream<'_>) -> Result { + attrs.extend(Attribute::parse_outer(input)?); + + let name = input.parse::>()?; + if name.is_some() { + input.parse::()?; + } + let lit = input.parse::()?; + + let mut value = lit.value(); + let mut path = None; + let span = lit.span(); + + let is_path = { + let s = value.trim(); + !(s.is_empty() + || (s.starts_with('{') && s.ends_with('}')) + || (s.starts_with('[') && s.ends_with(']'))) + }; + if is_path { + let mut p = PathBuf::from(value); + if p.is_relative() { + let dir = std::env::var_os("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .ok_or_else(|| Error::new(span, "failed to get manifest dir"))?; + p = dir.join(p); + } + p = dunce::canonicalize(&p) + .map_err(|e| Error::new(span, format!("failed to canonicalize path {p:?}: {e}")))?; + value = std::fs::read_to_string(&p) + .map_err(|e| Error::new(span, format!("failed to read file {p:?}: {e}")))?; + path = Some(p); + } + + let s = value.trim(); + if s.is_empty() { + let msg = if is_path { "file path is empty" } else { "empty input is not allowed" }; + Err(Error::new(span, msg)) + } else if (s.starts_with('{') && s.ends_with('}')) + || (s.starts_with('[') && s.ends_with(']')) + { + #[cfg(feature = "json")] + { + let json = serde_json::from_str(s) + .map_err(|e| Error::new(span, format!("invalid JSON: {e}")))?; + let name = name.ok_or_else(|| Error::new(span, "need a name for JSON ABI"))?; + Ok(Self { attrs, path, kind: SolInputKind::Json(name, json) }) + } + #[cfg(not(feature = "json"))] + { + let msg = "JSON support must be enabled with the \"json\" feature"; + Err(Error::new(span, msg)) + } + } else { + if let Some(name) = name { + let msg = "names are not allowed outside of JSON ABI"; + return Err(Error::new(name.span(), msg)); + } + let kind = syn::parse_str(s).map_err(|e| { + let msg = format!("expected a valid JSON ABI string or Solidity string: {e}"); + Error::new(span, msg) + })?; + Ok(Self { attrs, path, kind }) + } + } +} diff --git a/crates/sol-macro/src/json.rs b/crates/sol-macro-input/src/json.rs similarity index 79% rename from crates/sol-macro/src/json.rs rename to crates/sol-macro-input/src/json.rs index f5f348ce5..a5a4bd9af 100644 --- a/crates/sol-macro/src/json.rs +++ b/crates/sol-macro-input/src/json.rs @@ -1,54 +1,76 @@ +use crate::{SolInput, SolInputKind}; + use alloy_json_abi::{ContractObject, JsonAbi, ToSolConfig}; use proc_macro2::{Ident, TokenStream}; use quote::{quote, TokenStreamExt}; -use syn::{Attribute, Result}; - -pub fn expand(name: Ident, json: ContractObject, attrs: Vec) -> Result { - let ContractObject { abi, bytecode, deployed_bytecode } = json; - - let mut abi = abi.ok_or_else(|| syn::Error::new(name.span(), "ABI not found in JSON"))?; - let sol = abi_to_sol(&name, &mut abi); - let sol_interface_tokens = tokens_for_sol(&name, &sol)?; - let bytecode = bytecode.map(|bytes| { - let s = bytes.to_string(); - quote!(bytecode = #s,) - }); - let deployed_bytecode = deployed_bytecode.map(|bytes| { - let s = bytes.to_string(); - quote!(deployed_bytecode = #s) - }); - - let doc_str = format!( - "\n\n\ -Generated by the following Solidity interface... -```solidity -{sol} -``` - -...which was generated by the following JSON ABI: -```json -{json_s} -```", - json_s = serde_json::to_string_pretty(&abi).unwrap() - ); - let tokens = quote! { - #(#attrs)* - #[doc = #doc_str] - #[sol(#bytecode #deployed_bytecode)] - #sol_interface_tokens - }; +use syn::Result; - let ast = syn::parse2(tokens).map_err(|e| { - let msg = format!( - "failed to parse ABI-generated tokens into a Solidity AST: {e}.\n\ - This is a bug. We would appreciate a bug report: \ - https://github.com/alloy-rs/core/issues/new/choose" +impl SolInput { + /// Normalize JSON ABI inputs into Sol inputs. + pub fn normalize_json(self) -> Result { + if !matches!(self.kind, SolInputKind::Json(..)) { + return Ok(self); + } + + // irrefutable, as we just checked + let SolInput { + attrs, + path, + kind: SolInputKind::Json(name, ContractObject { abi, bytecode, deployed_bytecode }), + } = self + else { + panic!() + }; + + let mut abi = abi.ok_or_else(|| syn::Error::new(name.span(), "ABI not found in JSON"))?; + let sol = abi_to_sol(&name, &mut abi); + let sol_interface_tokens = tokens_for_sol(&name, &sol)?; + let bytecode = bytecode.map(|bytes| { + let s = bytes.to_string(); + quote!(bytecode = #s,) + }); + let deployed_bytecode = deployed_bytecode.map(|bytes| { + let s = bytes.to_string(); + quote!(deployed_bytecode = #s) + }); + + let attrs_iter = attrs.iter(); + let doc_str = format!( + "\n\n\ + Generated by the following Solidity interface... + ```solidity + {sol} + ``` + + ...which was generated by the following JSON ABI: + ```json + {json_s} + ```", + json_s = serde_json::to_string_pretty(&abi).unwrap() ); - syn::Error::new(name.span(), msg) - })?; - crate::expand::expand(ast) + let tokens = quote! { + #(#attrs_iter)* + #[doc = #doc_str] + #[sol(#bytecode #deployed_bytecode)] + #sol_interface_tokens + }; + + let ast: ast::File = syn::parse2(tokens).map_err(|e| { + let msg = format!( + "failed to parse ABI-generated tokens into a Solidity AST: {e}.\n\ + This is a bug. We would appreciate a bug report: \ + https://github.com/alloy-rs/core/issues/new/choose" + ); + syn::Error::new(name.span(), msg) + })?; + + let kind = SolInputKind::Sol(ast); + Ok(SolInput { attrs, path, kind }) + } } +// doesn't parse Json + fn abi_to_sol(name: &Ident, abi: &mut JsonAbi) -> String { abi.dedup(); abi.to_sol(&name.to_string(), Some(ToSolConfig::new().print_constructors(true))) diff --git a/crates/sol-macro-input/src/lib.rs b/crates/sol-macro-input/src/lib.rs new file mode 100644 index 000000000..a7c926a64 --- /dev/null +++ b/crates/sol-macro-input/src/lib.rs @@ -0,0 +1,26 @@ +//! This crate contains inputs to the `sol!` macro. It sits in-between +//! the `sol-macro` and `syn-solidity` crates, and contains an intermediate +//! representation of Solidity items. These items are then expanded into +//! Rust code by the `alloy-sol-macro` crate. +//! +//! This crate is not meant to be used directly, but rather is a tool for +//! writing macros that generate Rust code from Solidity code. +#![doc( + html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", + html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" +)] +#![warn(missing_copy_implementations, missing_debug_implementations, missing_docs, rustdoc::all)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod input; +pub use input::{SolInput, SolInputKind}; + +mod expander; +pub use expander::SolInputExpander; + +#[cfg(feature = "json")] +mod json; + +extern crate syn_solidity as ast; diff --git a/crates/sol-macro/Cargo.toml b/crates/sol-macro/Cargo.toml index 063ad79d8..2e68dbe6d 100644 --- a/crates/sol-macro/Cargo.toml +++ b/crates/sol-macro/Cargo.toml @@ -21,13 +21,14 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +alloy-sol-macro-input.workspace = true + syn-solidity = { workspace = true, features = ["visit", "visit-mut"] } proc-macro2.workspace = true quote.workspace = true syn = { workspace = true, features = ["extra-traits"] } -dunce = "1.0" heck = "0.4" hex.workspace = true indexmap = "2" @@ -36,7 +37,6 @@ tiny-keccak = { workspace = true, features = ["keccak"] } # json alloy-json-abi = { workspace = true, optional = true } -serde_json = { workspace = true, optional = true } [features] -json = ["dep:alloy-json-abi", "dep:serde_json"] +json = ["dep:alloy-json-abi", "alloy-sol-macro-input/json"] diff --git a/crates/sol-macro/src/input.rs b/crates/sol-macro/src/input.rs index d6e525f2d..8b1378917 100644 --- a/crates/sol-macro/src/input.rs +++ b/crates/sol-macro/src/input.rs @@ -1,165 +1 @@ -use ast::Spanned; -use proc_macro2::TokenStream; -use quote::quote; -use std::path::PathBuf; -use syn::{ - parse::{discouraged::Speculative, Parse, ParseStream}, - Attribute, Error, Ident, LitStr, Result, Token, -}; -#[derive(Clone, Debug)] -pub enum SolInputKind { - Sol(ast::File), - Type(ast::Type), - #[cfg(feature = "json")] - Json(Ident, alloy_json_abi::ContractObject), -} - -// doesn't parse Json -impl Parse for SolInputKind { - fn parse(input: ParseStream<'_>) -> Result { - let fork = input.fork(); - match fork.parse() { - Ok(file) => { - input.advance_to(&fork); - Ok(Self::Sol(file)) - } - Err(e) => match input.parse() { - Ok(ast::Type::Custom(_)) | Err(_) => Err(e), - - Ok(ast::Type::Mapping(m)) => { - Err(Error::new(m.span(), "mapping types are not yet supported")) - } - - Ok(ty) => Ok(Self::Type(ty)), - }, - } - } -} - -#[derive(Clone, Debug)] -pub struct SolInput { - pub attrs: Vec, - pub path: Option, - pub kind: SolInputKind, -} - -impl Parse for SolInput { - fn parse(input: ParseStream<'_>) -> Result { - let attrs = Attribute::parse_inner(input)?; - - // ignore attributes when peeking - let fork = input.fork(); - let _inner = Attribute::parse_outer(&fork)?; - - if fork.peek(LitStr) || (fork.peek(Ident) && fork.peek2(Token![,]) && fork.peek3(LitStr)) { - Self::parse_abigen(attrs, input) - } else { - input.parse().map(|kind| Self { attrs, path: None, kind }) - } - } -} - -impl SolInput { - /// `abigen`-like syntax: `sol!(name, "path/to/file")` - fn parse_abigen(mut attrs: Vec, input: ParseStream<'_>) -> Result { - attrs.extend(Attribute::parse_outer(input)?); - - let name = input.parse::>()?; - if name.is_some() { - input.parse::()?; - } - let lit = input.parse::()?; - - let mut value = lit.value(); - let mut path = None; - let span = lit.span(); - - let is_path = { - let s = value.trim(); - !(s.is_empty() - || (s.starts_with('{') && s.ends_with('}')) - || (s.starts_with('[') && s.ends_with(']'))) - }; - if is_path { - let mut p = PathBuf::from(value); - if p.is_relative() { - let dir = std::env::var_os("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .ok_or_else(|| Error::new(span, "failed to get manifest dir"))?; - p = dir.join(p); - } - p = dunce::canonicalize(&p) - .map_err(|e| Error::new(span, format!("failed to canonicalize path {p:?}: {e}")))?; - value = std::fs::read_to_string(&p) - .map_err(|e| Error::new(span, format!("failed to read file {p:?}: {e}")))?; - path = Some(p); - } - - let s = value.trim(); - if s.is_empty() { - let msg = if is_path { "file path is empty" } else { "empty input is not allowed" }; - Err(Error::new(span, msg)) - } else if (s.starts_with('{') && s.ends_with('}')) - || (s.starts_with('[') && s.ends_with(']')) - { - #[cfg(feature = "json")] - { - let json = serde_json::from_str(s) - .map_err(|e| Error::new(span, format!("invalid JSON: {e}")))?; - let name = name.ok_or_else(|| Error::new(span, "need a name for JSON ABI"))?; - Ok(Self { attrs, path, kind: SolInputKind::Json(name, json) }) - } - #[cfg(not(feature = "json"))] - { - let msg = "JSON support must be enabled with the \"json\" feature"; - Err(Error::new(span, msg)) - } - } else { - if let Some(name) = name { - let msg = "names are not allowed outside of JSON ABI"; - return Err(Error::new(name.span(), msg)); - } - let kind = syn::parse_str(s).map_err(|e| { - let msg = format!("expected a valid JSON ABI string or Solidity string: {e}"); - Error::new(span, msg) - })?; - Ok(Self { attrs, path, kind }) - } - } - - pub fn expand(self) -> Result { - let Self { attrs, path, kind } = self; - let include = path.map(|p| { - let p = p.to_str().unwrap(); - quote! { const _: &'static [u8] = ::core::include_bytes!(#p); } - }); - - let tokens = match kind { - SolInputKind::Sol(mut file) => { - file.attrs.extend(attrs); - crate::expand::expand(file) - } - SolInputKind::Type(ty) => { - let (sol_attrs, rest) = crate::attr::SolAttrs::parse(&attrs)?; - if !rest.is_empty() { - return Err(Error::new_spanned( - rest.first().unwrap(), - "only `#[sol]` attributes are allowed here", - )); - } - - let mut crates = crate::expand::ExternCrates::default(); - crates.fill(&sol_attrs); - Ok(crate::expand::expand_type(&ty, &crates)) - } - #[cfg(feature = "json")] - SolInputKind::Json(name, json) => crate::json::expand(name, json, attrs), - }?; - - Ok(quote! { - #include - #tokens - }) - } -} diff --git a/crates/sol-macro/src/lib.rs b/crates/sol-macro/src/lib.rs index 120a446ff..cc0895832 100644 --- a/crates/sol-macro/src/lib.rs +++ b/crates/sol-macro/src/lib.rs @@ -20,20 +20,17 @@ extern crate proc_macro_error; extern crate syn_solidity as ast; +use alloy_sol_macro_input::{SolInput, SolInputExpander, SolInputKind}; use proc_macro::TokenStream; use syn::parse_macro_input; mod attr; mod expand; -mod input; mod utils; #[cfg(feature = "json")] mod verbatim; -#[cfg(feature = "json")] -mod json; - /// Generate types that implement [`alloy-sol-types`] traits, which can be used /// for type-safe [ABI] and [EIP-712] serialization to interface with Ethereum /// smart contracts. @@ -95,11 +92,11 @@ mod json; /// construct `eth_call`s to an on-chain contract through Ethereum JSON RPC, similar to the /// default behavior of [`abigen`]. This makes use of the [`alloy-contract`](https://github.com/alloy-rs/alloy) /// crate. -/// +/// /// N.B: at the time of writing, the `alloy-contract` crate is not yet released on `crates.io`, /// and its API is completely unstable and subject to change, so this feature is not yet /// recommended for use. -/// +/// /// Generates: /// - `struct {name}Instance { ... }` /// - `pub fn new(...) -> {name}Instance

` + getters and setters @@ -233,8 +230,50 @@ mod json; #[proc_macro] #[proc_macro_error] pub fn sol(input: TokenStream) -> TokenStream { - parse_macro_input!(input as input::SolInput) - .expand() - .unwrap_or_else(syn::Error::into_compile_error) - .into() + let input = parse_macro_input!(input as alloy_sol_macro_input::SolInput); + + SolMacroExpander.expand(input).unwrap_or_else(syn::Error::into_compile_error).into() +} + +struct SolMacroExpander; + +impl SolInputExpander for SolMacroExpander { + fn expand(&mut self, input: SolInput) -> syn::Result { + // Convert JSON input to Solidity input + #[cfg(feature = "json")] + let input = input.normalize_json()?; + + let SolInput { attrs, path, kind } = input; + let include = path.map(|p| { + let p = p.to_str().unwrap(); + quote! { const _: &'static [u8] = ::core::include_bytes!(#p); } + }); + + let tokens = match kind { + SolInputKind::Sol(mut file) => { + file.attrs.extend(attrs); + crate::expand::expand(file) + } + SolInputKind::Type(ty) => { + let (sol_attrs, rest) = crate::attr::SolAttrs::parse(&attrs)?; + if !rest.is_empty() { + return Err(syn::Error::new_spanned( + rest.first().unwrap(), + "only `#[sol]` attributes are allowed here", + )); + } + + let mut crates = crate::expand::ExternCrates::default(); + crates.fill(&sol_attrs); + Ok(crate::expand::expand_type(&ty, &crates)) + } + #[cfg(feature = "json")] + SolInputKind::Json(_, _) => unreachable!("input already normalized"), + }?; + + Ok(quote! { + #include + #tokens + }) + } } From ff70655345c52cca1ac5f470b5d0c0bdce08ffc8 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 17 Mar 2024 16:43:13 -0700 Subject: [PATCH 2/9] fix: explicit quote import --- crates/sol-macro/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/sol-macro/src/lib.rs b/crates/sol-macro/src/lib.rs index cc0895832..77ebd4063 100644 --- a/crates/sol-macro/src/lib.rs +++ b/crates/sol-macro/src/lib.rs @@ -22,6 +22,7 @@ extern crate syn_solidity as ast; use alloy_sol_macro_input::{SolInput, SolInputExpander, SolInputKind}; use proc_macro::TokenStream; +use quote::quote; use syn::parse_macro_input; mod attr; From 68ad52d43f7128fb494161b69db1a6f6726e5623 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 17 Mar 2024 16:57:07 -0700 Subject: [PATCH 3/9] nits: cargo.toml keys for new package --- crates/sol-macro-input/Cargo.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/sol-macro-input/Cargo.toml b/crates/sol-macro-input/Cargo.toml index a72a4fc6f..65e748939 100644 --- a/crates/sol-macro-input/Cargo.toml +++ b/crates/sol-macro-input/Cargo.toml @@ -1,16 +1,19 @@ [package] name = "alloy-sol-macro-input" +description = "Input types for sol!-like macros" +keywords = ["ethereum", "abi", "encoding", "evm", "solidity"] +categories = ["encoding", "cryptography::cryptocurrencies"] +homepage = "https://github.com/alloy-rs/core/tree/main/crates/sol-macro-input" + + version.workspace = true edition.workspace = true rust-version.workspace = true authors.workspace = true license.workspace = true -homepage.workspace = true repository.workspace = true exclude.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] dunce = "1.0.4" proc-macro2.workspace = true From 01170fd30d3912b7700f4bdaaa3db12d54f39f01 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 12:33:41 -0700 Subject: [PATCH 4/9] nit: newline --- crates/sol-macro-input/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/sol-macro-input/Cargo.toml b/crates/sol-macro-input/Cargo.toml index 65e748939..8ff0f246a 100644 --- a/crates/sol-macro-input/Cargo.toml +++ b/crates/sol-macro-input/Cargo.toml @@ -5,7 +5,6 @@ keywords = ["ethereum", "abi", "encoding", "evm", "solidity"] categories = ["encoding", "cryptography::cryptocurrencies"] homepage = "https://github.com/alloy-rs/core/tree/main/crates/sol-macro-input" - version.workspace = true edition.workspace = true rust-version.workspace = true From be83e2d2d9bbc31bc97a893c67819f1a409442d2 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 13:25:40 -0700 Subject: [PATCH 5/9] refator: push attr down --- crates/sol-macro-input/Cargo.toml | 6 ++++-- .../src/attr.rs | 20 +++++++++++++++++++ crates/sol-macro-input/src/expander.rs | 2 +- crates/sol-macro-input/src/lib.rs | 4 ++++ crates/sol-macro/src/expand/contract.rs | 17 ++++++++-------- crates/sol-macro/src/expand/enum.rs | 8 ++++---- crates/sol-macro/src/expand/error.rs | 6 +++--- crates/sol-macro/src/expand/event.rs | 6 +++--- crates/sol-macro/src/expand/function.rs | 12 +++++------ crates/sol-macro/src/expand/mod.rs | 4 ++-- crates/sol-macro/src/expand/struct.rs | 7 ++++--- crates/sol-macro/src/expand/udt.rs | 3 ++- crates/sol-macro/src/lib.rs | 11 +++++----- 13 files changed, 68 insertions(+), 38 deletions(-) rename crates/{sol-macro => sol-macro-input}/src/attr.rs (93%) diff --git a/crates/sol-macro-input/Cargo.toml b/crates/sol-macro-input/Cargo.toml index 8ff0f246a..6e46ab7c5 100644 --- a/crates/sol-macro-input/Cargo.toml +++ b/crates/sol-macro-input/Cargo.toml @@ -15,14 +15,16 @@ exclude.workspace = true [dependencies] dunce = "1.0.4" +heck = "0.5.0" +hex.workspace = true proc-macro2.workspace = true syn.workspace = true syn-solidity.workspace = true +quote.workspace = true # json alloy-json-abi = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } -quote = { workspace = true, optional = true } [features] -json = ["dep:alloy-json-abi", "dep:serde_json", "dep:quote"] +json = ["dep:alloy-json-abi", "dep:serde_json"] diff --git a/crates/sol-macro/src/attr.rs b/crates/sol-macro-input/src/attr.rs similarity index 93% rename from crates/sol-macro/src/attr.rs rename to crates/sol-macro-input/src/attr.rs index 6af32e3d7..664ee1143 100644 --- a/crates/sol-macro/src/attr.rs +++ b/crates/sol-macro-input/src/attr.rs @@ -6,18 +6,22 @@ use syn::{punctuated::Punctuated, Attribute, Error, LitBool, LitStr, Path, Resul const DUPLICATE_ERROR: &str = "duplicate attribute"; const UNKNOWN_ERROR: &str = "unknown `sol` attribute"; +/// Wraps the argument in a doc attribute. pub fn mk_doc(s: impl quote::ToTokens) -> TokenStream { quote!(#[doc = #s]) } +/// Returns `true` if the attribute is `#[doc = "..."]`. pub fn is_doc(attr: &Attribute) -> bool { attr.path().is_ident("doc") } +/// Returns `true` if the attribute is `#[derive(...)]`. pub fn is_derive(attr: &Attribute) -> bool { attr.path().is_ident("derive") } +/// Returns an iterator over all the `#[doc = "..."]` attributes. pub fn docs(attrs: &[Attribute]) -> impl Iterator { attrs.iter().filter(|a| is_doc(a)) } @@ -45,10 +49,13 @@ pub fn docs_str(attrs: &[Attribute]) -> String { doc } +/// Returns an iterator over all the `#[derive(...)]` attributes. pub fn derives(attrs: &[Attribute]) -> impl Iterator { attrs.iter().filter(|a| is_derive(a)) } +/// Returns an iterator over all the rust `::` paths in the `#[derive(...)]` +/// attributes. pub fn derives_mapped(attrs: &[Attribute]) -> impl Iterator + '_ { derives(attrs).flat_map(|attr| { attr.parse_args_with(Punctuated::::parse_terminated).unwrap_or_default() @@ -66,27 +73,40 @@ pub fn derives_mapped(attrs: &[Attribute]) -> impl Iterator + '_ { /// See [`crate::sol!`] for a list of all possible attributes. #[derive(Debug, Default, PartialEq, Eq)] pub struct SolAttrs { + /// `#[sol(rpc)]` pub rpc: Option, + /// `#[sol(abi)]` pub abi: Option, + /// `#[sol(all_derives)]` pub all_derives: Option, + /// `#[sol(extra_methods)]` pub extra_methods: Option, + /// `#[sol(docs)]` pub docs: Option, + /// `#[sol(alloy_sol_types = alloy_core::sol_types)]` pub alloy_sol_types: Option, + /// `#[sol(alloy_contract = alloy_contract)]` pub alloy_contract: Option, // TODO: Implement + /// UNIMPLEMENTED: `#[sol(rename = "new_name")]` pub rename: Option, // TODO: Implement + /// UNIMPLMENTED: `#[sol(rename_all = "camelCase")]` pub rename_all: Option, + /// `#[sol(bytecode = "0x1234")]` pub bytecode: Option, + /// `#[sol(deployed_bytecode = "0x1234")]` pub deployed_bytecode: Option, + /// UDVT only `#[sol(type_check = "my_function")]` pub type_check: Option, } impl SolAttrs { + /// Parse the `#[sol(...)]` attributes from a list of attributes. pub fn parse(attrs: &[Attribute]) -> Result<(Self, Vec)> { let mut this = Self::default(); let mut others = Vec::with_capacity(attrs.len()); diff --git a/crates/sol-macro-input/src/expander.rs b/crates/sol-macro-input/src/expander.rs index b73ade9dd..0a8e66016 100644 --- a/crates/sol-macro-input/src/expander.rs +++ b/crates/sol-macro-input/src/expander.rs @@ -5,5 +5,5 @@ use crate::SolInput; /// Expands a `SolInput` into a `TokenStream`. pub trait SolInputExpander { /// Expand a `SolInput` into a `TokenStream`. - fn expand(&mut self, input: SolInput) -> syn::Result; + fn expand(&mut self, input: &SolInput) -> syn::Result; } diff --git a/crates/sol-macro-input/src/lib.rs b/crates/sol-macro-input/src/lib.rs index a7c926a64..593369813 100644 --- a/crates/sol-macro-input/src/lib.rs +++ b/crates/sol-macro-input/src/lib.rs @@ -14,6 +14,10 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +/// Tools for working with `#[...]` attributes. +mod attr; +pub use attr::{derives_mapped, docs_str, mk_doc, SolAttrs}; + mod input; pub use input::{SolInput, SolInputKind}; diff --git a/crates/sol-macro/src/expand/contract.rs b/crates/sol-macro/src/expand/contract.rs index 66acfbbb5..ab02ca32c 100644 --- a/crates/sol-macro/src/expand/contract.rs +++ b/crates/sol-macro/src/expand/contract.rs @@ -1,7 +1,8 @@ //! [`ItemContract`] expansion. use super::{anon_name, ty, ExpCtxt}; -use crate::{attr, utils::ExprArray}; +use crate::utils::ExprArray; +use alloy_sol_macro_input::{docs_str, mk_doc, SolAttrs}; use ast::{Item, ItemContract, ItemError, ItemEvent, ItemFunction, SolIdent, Spanned}; use heck::ToSnakeCase; use proc_macro2::{Ident, TokenStream}; @@ -30,7 +31,7 @@ use syn::{parse_quote, Attribute, Result}; pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result { let ItemContract { attrs, name, body, .. } = contract; - let (sol_attrs, attrs) = attr::SolAttrs::parse(attrs)?; + let (sol_attrs, attrs) = SolAttrs::parse(attrs)?; let extra_methods = sol_attrs.extra_methods.or(cx.attrs.extra_methods).unwrap_or(false); let rpc = sol_attrs.rpc.or(cx.attrs.rpc).unwrap_or(false); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); @@ -158,10 +159,10 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result, enumm: &ItemEnum) -> Result { let ItemEnum { name, variants, attrs, .. } = enumm; - let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.derives(&mut attrs, [], false); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); @@ -41,7 +41,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result let invalid_variant = has_invalid_variant.then(|| { let comma = (!variants.trailing_punct()).then(syn::token::Comma::default); - let has_serde = attr::derives_mapped(&attrs).any(|path| { + let has_serde = derives_mapped(&attrs).any(|path| { let Some(last) = path.segments.last() else { return false; }; @@ -70,7 +70,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result let uint8 = quote!(alloy_sol_types::sol_data::Uint<8>); let uint8_st = quote!(<#uint8 as alloy_sol_types::SolType>); - let doc = docs.then(|| attr::mk_doc(format!("```solidity\n{enumm}\n```"))); + let doc = docs.then(|| mk_doc(format!("```solidity\n{enumm}\n```"))); let tokens = quote! { #(#attrs)* #doc diff --git a/crates/sol-macro/src/expand/error.rs b/crates/sol-macro/src/expand/error.rs index 06fa22bf0..97c4670b2 100644 --- a/crates/sol-macro/src/expand/error.rs +++ b/crates/sol-macro/src/expand/error.rs @@ -1,7 +1,7 @@ //! [`ItemError`] expansion. use super::{expand_fields, expand_from_into_tuples, expand_tokenize, ExpCtxt}; -use crate::attr; +use alloy_sol_macro_input::{mk_doc, SolAttrs}; use ast::ItemError; use proc_macro2::TokenStream; use quote::quote; @@ -22,7 +22,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result let ItemError { parameters: params, name, attrs, .. } = error; cx.assert_resolved(params)?; - let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.derives(&mut attrs, params, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); @@ -38,7 +38,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result let fields = expand_fields(params, cx); let doc = docs.then(|| { let selector = hex::encode_prefixed(selector.array.as_slice()); - attr::mk_doc(format!( + mk_doc(format!( "Custom error with signature `{signature}` and selector `{selector}`.\n\ ```solidity\n{error}\n```" )) diff --git a/crates/sol-macro/src/expand/event.rs b/crates/sol-macro/src/expand/event.rs index fc11a4285..e2c5e4128 100644 --- a/crates/sol-macro/src/expand/event.rs +++ b/crates/sol-macro/src/expand/event.rs @@ -1,7 +1,7 @@ //! [`ItemEvent`] expansion. use super::{anon_name, expand_event_tokenize, expand_tuple_types, expand_type, ty, ExpCtxt}; -use crate::attr; +use alloy_sol_macro_input::{mk_doc, SolAttrs}; use ast::{EventParameter, ItemEvent, SolIdent, Spanned}; use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; @@ -22,7 +22,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result let ItemEvent { attrs, .. } = event; let params = event.params(); - let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.derives(&mut attrs, ¶ms, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); @@ -109,7 +109,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result let doc = docs.then(|| { let selector = hex::encode_prefixed(selector.array.as_slice()); - attr::mk_doc(format!( + mk_doc(format!( "Event with signature `{signature}` and selector `{selector}`.\n\ ```solidity\n{event}\n```" )) diff --git a/crates/sol-macro/src/expand/function.rs b/crates/sol-macro/src/expand/function.rs index 987b7b8ff..a932abcf9 100644 --- a/crates/sol-macro/src/expand/function.rs +++ b/crates/sol-macro/src/expand/function.rs @@ -1,7 +1,7 @@ //! [`ItemFunction`] expansion. use super::{expand_fields, expand_from_into_tuples, expand_tokenize, expand_tuple_types, ExpCtxt}; -use crate::attr; +use alloy_sol_macro_input::{mk_doc, SolAttrs}; use ast::{FunctionKind, ItemFunction, Spanned}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -42,7 +42,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result, function: &ItemFunction) -> Result, function: &ItemFunction) -> Result, constructor: &ItemFunction) -> Result { let ItemFunction { attrs, parameters, .. } = constructor; - let (sol_attrs, call_attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, call_attrs) = SolAttrs::parse(attrs)?; let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let alloy_sol_types = &cx.crates.sol_types; @@ -173,7 +173,7 @@ fn expand_constructor(cx: &ExpCtxt<'_>, constructor: &ItemFunction) -> Result ExpCtxt<'ast> { // resolve impl<'ast> ExpCtxt<'ast> { fn parse_file_attributes(&mut self) -> Result<()> { - let (attrs, others) = attr::SolAttrs::parse(&self.ast.attrs)?; + let (attrs, others) = SolAttrs::parse(&self.ast.attrs)?; self.attrs = attrs; self.crates.fill(&self.attrs); diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 270c5586d..054ab7cd7 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -1,6 +1,7 @@ //! [`ItemStruct`] expansion. -use super::{attr, expand_fields, expand_from_into_tuples, expand_tokenize, expand_type, ExpCtxt}; +use super::{expand_fields, expand_from_into_tuples, expand_tokenize, expand_type, ExpCtxt}; +use alloy_sol_macro_input::{mk_doc, SolAttrs}; use ast::{Item, ItemStruct, Spanned, Type}; use proc_macro2::TokenStream; use quote::quote; @@ -26,7 +27,7 @@ use syn::Result; pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { let ItemStruct { name, fields, attrs, .. } = s; - let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.derives(&mut attrs, fields, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); @@ -58,7 +59,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { let name_s = name.as_string(); let fields = expand_fields(fields, cx); - let doc = docs.then(|| attr::mk_doc(format!("```solidity\n{s}\n```"))); + let doc = docs.then(|| mk_doc(format!("```solidity\n{s}\n```"))); let tokens = quote! { #(#attrs)* #doc diff --git a/crates/sol-macro/src/expand/udt.rs b/crates/sol-macro/src/expand/udt.rs index 52256f733..4f6e7d385 100644 --- a/crates/sol-macro/src/expand/udt.rs +++ b/crates/sol-macro/src/expand/udt.rs @@ -2,6 +2,7 @@ use super::{ty::expand_rust_type, ExpCtxt}; use crate::expand::expand_type; +use alloy_sol_macro_input::SolAttrs; use ast::ItemUdt; use proc_macro2::TokenStream; use quote::quote; @@ -10,7 +11,7 @@ use syn::Result; pub(super) fn expand(cx: &ExpCtxt<'_>, udt: &ItemUdt) -> Result { let ItemUdt { name, ty, attrs, .. } = udt; - let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.type_derives(&mut attrs, std::iter::once(ty), true); let underlying_sol = expand_type(ty, &cx.crates); diff --git a/crates/sol-macro/src/lib.rs b/crates/sol-macro/src/lib.rs index 77ebd4063..15fead089 100644 --- a/crates/sol-macro/src/lib.rs +++ b/crates/sol-macro/src/lib.rs @@ -20,12 +20,11 @@ extern crate proc_macro_error; extern crate syn_solidity as ast; -use alloy_sol_macro_input::{SolInput, SolInputExpander, SolInputKind}; +use alloy_sol_macro_input::{SolAttrs, SolInput, SolInputExpander, SolInputKind}; use proc_macro::TokenStream; use quote::quote; use syn::parse_macro_input; -mod attr; mod expand; mod utils; @@ -233,14 +232,16 @@ mod verbatim; pub fn sol(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as alloy_sol_macro_input::SolInput); - SolMacroExpander.expand(input).unwrap_or_else(syn::Error::into_compile_error).into() + SolMacroExpander.expand(&input).unwrap_or_else(syn::Error::into_compile_error).into() } struct SolMacroExpander; impl SolInputExpander for SolMacroExpander { - fn expand(&mut self, input: SolInput) -> syn::Result { + fn expand(&mut self, input: &SolInput) -> syn::Result { + let input = input.clone(); // Convert JSON input to Solidity input + #[cfg(feature = "json")] let input = input.normalize_json()?; @@ -256,7 +257,7 @@ impl SolInputExpander for SolMacroExpander { crate::expand::expand(file) } SolInputKind::Type(ty) => { - let (sol_attrs, rest) = crate::attr::SolAttrs::parse(&attrs)?; + let (sol_attrs, rest) = SolAttrs::parse(&attrs)?; if !rest.is_empty() { return Err(syn::Error::new_spanned( rest.first().unwrap(), From 6f30ca5597c99fa544897548f57e08e7c0ebb213 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 13:27:04 -0700 Subject: [PATCH 6/9] nit: remove empty file --- crates/sol-macro/src/input.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 crates/sol-macro/src/input.rs diff --git a/crates/sol-macro/src/input.rs b/crates/sol-macro/src/input.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/sol-macro/src/input.rs +++ /dev/null @@ -1 +0,0 @@ - From d29869a4bf8054031b69f15ee04208da1d21707b Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 13:44:40 -0700 Subject: [PATCH 7/9] refactor: introduce ContainsSolAttrs --- crates/sol-macro-input/src/attr.rs | 60 +++++++++++++++++++++++++ crates/sol-macro-input/src/lib.rs | 2 +- crates/sol-macro/src/expand/contract.rs | 7 +-- crates/sol-macro/src/expand/enum.rs | 6 +-- crates/sol-macro/src/expand/error.rs | 6 +-- crates/sol-macro/src/expand/event.rs | 5 +-- crates/sol-macro/src/expand/function.rs | 10 ++--- crates/sol-macro/src/expand/mod.rs | 4 +- crates/sol-macro/src/expand/struct.rs | 7 +-- 9 files changed, 84 insertions(+), 23 deletions(-) diff --git a/crates/sol-macro-input/src/attr.rs b/crates/sol-macro-input/src/attr.rs index 664ee1143..9d4a684e0 100644 --- a/crates/sol-macro-input/src/attr.rs +++ b/crates/sol-macro-input/src/attr.rs @@ -185,6 +185,66 @@ impl SolAttrs { } } +/// Trait for items that contain `#[sol(...)]` attributes among other +/// attributes. This is usually a shortcut for [`SolAttrs::parse`]. +pub trait ContainsSolAttrs { + /// Get the list of attributes. + fn attrs(&self) -> &[Attribute]; + + /// Parse the `#[sol(...)]` attributes from the list of attributes. + fn split_attrs(&self) -> syn::Result<(SolAttrs, Vec)> { + SolAttrs::parse(self.attrs()) + } +} + +impl ContainsSolAttrs for syn_solidity::File { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemContract { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemEnum { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemError { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemEvent { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemFunction { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemStruct { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + +impl ContainsSolAttrs for syn_solidity::ItemUdt { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + /// Defines the casing for the attributes long representation. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CasingStyle { diff --git a/crates/sol-macro-input/src/lib.rs b/crates/sol-macro-input/src/lib.rs index 593369813..1402105ee 100644 --- a/crates/sol-macro-input/src/lib.rs +++ b/crates/sol-macro-input/src/lib.rs @@ -16,7 +16,7 @@ /// Tools for working with `#[...]` attributes. mod attr; -pub use attr::{derives_mapped, docs_str, mk_doc, SolAttrs}; +pub use attr::{derives_mapped, docs_str, mk_doc, ContainsSolAttrs, SolAttrs}; mod input; pub use input::{SolInput, SolInputKind}; diff --git a/crates/sol-macro/src/expand/contract.rs b/crates/sol-macro/src/expand/contract.rs index ab02ca32c..1dfcf4357 100644 --- a/crates/sol-macro/src/expand/contract.rs +++ b/crates/sol-macro/src/expand/contract.rs @@ -2,7 +2,7 @@ use super::{anon_name, ty, ExpCtxt}; use crate::utils::ExprArray; -use alloy_sol_macro_input::{docs_str, mk_doc, SolAttrs}; +use alloy_sol_macro_input::{docs_str, mk_doc, ContainsSolAttrs}; use ast::{Item, ItemContract, ItemError, ItemEvent, ItemFunction, SolIdent, Spanned}; use heck::ToSnakeCase; use proc_macro2::{Ident, TokenStream}; @@ -29,9 +29,10 @@ use syn::{parse_quote, Attribute, Result}; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result { - let ItemContract { attrs, name, body, .. } = contract; + let ItemContract { name, body, .. } = contract; + + let (sol_attrs, attrs) = contract.split_attrs()?; - let (sol_attrs, attrs) = SolAttrs::parse(attrs)?; let extra_methods = sol_attrs.extra_methods.or(cx.attrs.extra_methods).unwrap_or(false); let rpc = sol_attrs.rpc.or(cx.attrs.rpc).unwrap_or(false); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 719ff5876..7ea6f7e64 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -1,7 +1,7 @@ //! [`ItemEnum`] expansion. use super::ExpCtxt; -use alloy_sol_macro_input::{derives_mapped, mk_doc, SolAttrs}; +use alloy_sol_macro_input::{derives_mapped, mk_doc, ContainsSolAttrs}; use ast::{ItemEnum, Spanned}; use proc_macro2::TokenStream; use quote::quote; @@ -20,9 +20,9 @@ use syn::Result; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result { - let ItemEnum { name, variants, attrs, .. } = enumm; + let ItemEnum { name, variants, .. } = enumm; - let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = enumm.split_attrs()?; cx.derives(&mut attrs, [], false); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); diff --git a/crates/sol-macro/src/expand/error.rs b/crates/sol-macro/src/expand/error.rs index 97c4670b2..4fb5c6ecd 100644 --- a/crates/sol-macro/src/expand/error.rs +++ b/crates/sol-macro/src/expand/error.rs @@ -1,7 +1,7 @@ //! [`ItemError`] expansion. use super::{expand_fields, expand_from_into_tuples, expand_tokenize, ExpCtxt}; -use alloy_sol_macro_input::{mk_doc, SolAttrs}; +use alloy_sol_macro_input::{mk_doc, ContainsSolAttrs}; use ast::ItemError; use proc_macro2::TokenStream; use quote::quote; @@ -19,10 +19,10 @@ use syn::Result; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result { - let ItemError { parameters: params, name, attrs, .. } = error; + let ItemError { parameters: params, name, .. } = error; cx.assert_resolved(params)?; - let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = error.split_attrs()?; cx.derives(&mut attrs, params, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); diff --git a/crates/sol-macro/src/expand/event.rs b/crates/sol-macro/src/expand/event.rs index e2c5e4128..5538d6c3b 100644 --- a/crates/sol-macro/src/expand/event.rs +++ b/crates/sol-macro/src/expand/event.rs @@ -1,7 +1,7 @@ //! [`ItemEvent`] expansion. use super::{anon_name, expand_event_tokenize, expand_tuple_types, expand_type, ty, ExpCtxt}; -use alloy_sol_macro_input::{mk_doc, SolAttrs}; +use alloy_sol_macro_input::{mk_doc, ContainsSolAttrs}; use ast::{EventParameter, ItemEvent, SolIdent, Spanned}; use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; @@ -19,10 +19,9 @@ use syn::Result; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result { - let ItemEvent { attrs, .. } = event; let params = event.params(); - let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = event.split_attrs()?; cx.derives(&mut attrs, ¶ms, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); diff --git a/crates/sol-macro/src/expand/function.rs b/crates/sol-macro/src/expand/function.rs index a932abcf9..6a45da6f4 100644 --- a/crates/sol-macro/src/expand/function.rs +++ b/crates/sol-macro/src/expand/function.rs @@ -1,7 +1,7 @@ //! [`ItemFunction`] expansion. use super::{expand_fields, expand_from_into_tuples, expand_tokenize, expand_tuple_types, ExpCtxt}; -use alloy_sol_macro_input::{mk_doc, SolAttrs}; +use alloy_sol_macro_input::{mk_doc, ContainsSolAttrs}; use ast::{FunctionKind, ItemFunction, Spanned}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -24,7 +24,7 @@ use syn::Result; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result { - let ItemFunction { attrs, parameters, returns, name, kind, .. } = function; + let ItemFunction { parameters, returns, name, kind, .. } = function; if matches!(kind, FunctionKind::Constructor(_)) { return expand_constructor(cx, function); @@ -42,7 +42,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result, function: &ItemFunction) -> Result, constructor: &ItemFunction) -> Result { - let ItemFunction { attrs, parameters, .. } = constructor; + let ItemFunction { parameters, .. } = constructor; - let (sol_attrs, call_attrs) = SolAttrs::parse(attrs)?; + let (sol_attrs, call_attrs) = constructor.split_attrs()?; let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let alloy_sol_types = &cx.crates.sol_types; diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs index a5f77c8cc..d5a2eec22 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -4,7 +4,7 @@ use crate::{ expand::ty::expand_rust_type, utils::{self, ExprArray}, }; -use alloy_sol_macro_input::SolAttrs; +use alloy_sol_macro_input::{ContainsSolAttrs, SolAttrs}; use ast::{ EventParameter, File, Item, ItemError, ItemEvent, ItemFunction, Parameters, SolIdent, SolPath, Spanned, Type, VariableDeclaration, Visit, @@ -124,7 +124,7 @@ impl<'ast> ExpCtxt<'ast> { // resolve impl<'ast> ExpCtxt<'ast> { fn parse_file_attributes(&mut self) -> Result<()> { - let (attrs, others) = SolAttrs::parse(&self.ast.attrs)?; + let (attrs, others) = self.ast.split_attrs()?; self.attrs = attrs; self.crates.fill(&self.attrs); diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 054ab7cd7..a4e486e5c 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -1,7 +1,7 @@ //! [`ItemStruct`] expansion. use super::{expand_fields, expand_from_into_tuples, expand_tokenize, expand_type, ExpCtxt}; -use alloy_sol_macro_input::{mk_doc, SolAttrs}; +use alloy_sol_macro_input::{mk_doc, ContainsSolAttrs}; use ast::{Item, ItemStruct, Spanned, Type}; use proc_macro2::TokenStream; use quote::quote; @@ -25,9 +25,10 @@ use syn::Result; /// } /// ``` pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { - let ItemStruct { name, fields, attrs, .. } = s; + let ItemStruct { name, fields, .. } = s; + + let (sol_attrs, mut attrs) = s.split_attrs()?; - let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; cx.derives(&mut attrs, fields, true); let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); From 72481c5b542fd687613ef64e47461405818e93de Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 13:45:20 -0700 Subject: [PATCH 8/9] docs: delete broken link --- crates/sol-macro-input/src/attr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/sol-macro-input/src/attr.rs b/crates/sol-macro-input/src/attr.rs index 9d4a684e0..6c6279c3a 100644 --- a/crates/sol-macro-input/src/attr.rs +++ b/crates/sol-macro-input/src/attr.rs @@ -70,7 +70,6 @@ pub fn derives_mapped(attrs: &[Attribute]) -> impl Iterator + '_ { // 5. document the attribute in the [`crate::sol!`] macro docs. /// `#[sol(...)]` attributes. -/// See [`crate::sol!`] for a list of all possible attributes. #[derive(Debug, Default, PartialEq, Eq)] pub struct SolAttrs { /// `#[sol(rpc)]` From c7db85ae4e3320fa59ef032f6aa1cd4534b3f2f5 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Mar 2024 13:47:01 -0700 Subject: [PATCH 9/9] fix: migrate udt and fix docs --- crates/sol-macro-input/src/attr.rs | 4 ++-- crates/sol-macro/src/expand/udt.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/sol-macro-input/src/attr.rs b/crates/sol-macro-input/src/attr.rs index 6c6279c3a..a7ab4dd3d 100644 --- a/crates/sol-macro-input/src/attr.rs +++ b/crates/sol-macro-input/src/attr.rs @@ -66,8 +66,8 @@ pub fn derives_mapped(attrs: &[Attribute]) -> impl Iterator + '_ { // 1. add a field to this struct, // 2. add a match arm in the `parse` function below, // 3. add test cases in the `tests` module at the bottom of this file, -// 4. implement the attribute in the `expand` module, -// 5. document the attribute in the [`crate::sol!`] macro docs. +// 4. implement the attribute in your `SolInputExpander` implementation, +// 5. document the attribute in the [`sol!`] macro docs. /// `#[sol(...)]` attributes. #[derive(Debug, Default, PartialEq, Eq)] diff --git a/crates/sol-macro/src/expand/udt.rs b/crates/sol-macro/src/expand/udt.rs index 4f6e7d385..342362e70 100644 --- a/crates/sol-macro/src/expand/udt.rs +++ b/crates/sol-macro/src/expand/udt.rs @@ -2,16 +2,16 @@ use super::{ty::expand_rust_type, ExpCtxt}; use crate::expand::expand_type; -use alloy_sol_macro_input::SolAttrs; +use alloy_sol_macro_input::ContainsSolAttrs; use ast::ItemUdt; use proc_macro2::TokenStream; use quote::quote; use syn::Result; pub(super) fn expand(cx: &ExpCtxt<'_>, udt: &ItemUdt) -> Result { - let ItemUdt { name, ty, attrs, .. } = udt; + let ItemUdt { name, ty, .. } = udt; - let (sol_attrs, mut attrs) = SolAttrs::parse(attrs)?; + let (sol_attrs, mut attrs) = udt.split_attrs()?; cx.type_derives(&mut attrs, std::iter::once(ty), true); let underlying_sol = expand_type(ty, &cx.crates);