diff --git a/crates/sol-macro/src/expand/ty.rs b/crates/sol-macro/src/expand/ty.rs index 60b887e626..77eaee85fd 100644 --- a/crates/sol-macro/src/expand/ty.rs +++ b/crates/sol-macro/src/expand/ty.rs @@ -102,7 +102,7 @@ fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) { Type::Array(ref array) => { let ty = expand_type(&array.ty); let span = array.span(); - if let Some(size) = &array.size { + if let Some(size) = array.size() { quote_spanned! {span=> ::alloy_sol_types::sol_data::FixedArray<#ty, #size> } @@ -148,11 +148,13 @@ pub(super) fn type_base_data_size(cx: &ExpCtxt<'_>, ty: &Type) -> usize { Type::String(_) | Type::Bytes(_) | Type::Array(TypeArray { size: None, .. }) => 64, // fixed array: size * encoded size - Type::Array(TypeArray { - ty: inner, - size: Some(size), - .. - }) => type_base_data_size(cx, inner) * size.base10_parse::().unwrap(), + Type::Array( + a @ TypeArray { + ty: inner, + size: Some(_), + .. + }, + ) => type_base_data_size(cx, inner) * a.size().unwrap(), // tuple: sum of encoded sizes Type::Tuple(tuple) => tuple @@ -290,7 +292,7 @@ impl fmt::Display for TypePrinter<'_> { Type::Array(array) => { Self::new(self.cx, &array.ty).fmt(f)?; f.write_str("[")?; - if let Some(size) = &array.size { + if let Some(size) = array.size() { size.fmt(f)?; } f.write_str("]") diff --git a/crates/syn-solidity/src/attribute/function.rs b/crates/syn-solidity/src/attribute/function.rs index 6861010605..347f50c70a 100644 --- a/crates/syn-solidity/src/attribute/function.rs +++ b/crates/syn-solidity/src/attribute/function.rs @@ -1,7 +1,6 @@ use crate::{kw, Modifier, Mutability, Override, SolPath, Spanned, VariableAttribute, Visibility}; use proc_macro2::Span; use std::{ - collections::HashSet, fmt, hash::{Hash, Hasher}, mem, @@ -17,7 +16,7 @@ use syn::{ /// A list of unique function attributes. Used in /// [ItemFunction][crate::ItemFunction]. #[derive(Clone, Default, PartialEq, Eq)] -pub struct FunctionAttributes(pub HashSet); +pub struct FunctionAttributes(pub Vec); impl fmt::Debug for FunctionAttributes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -26,7 +25,7 @@ impl fmt::Debug for FunctionAttributes { } impl Deref for FunctionAttributes { - type Target = HashSet; + type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 @@ -41,19 +40,19 @@ impl DerefMut for FunctionAttributes { impl Parse for FunctionAttributes { fn parse(input: ParseStream<'_>) -> Result { - let mut attributes = HashSet::::new(); + let mut attributes = Vec::::new(); while !(input.is_empty() || input.peek(kw::returns) || input.peek(Token![;]) || input.peek(Brace)) { - let attr = input.parse()?; - if let Some(prev) = attributes.get(&attr) { + let attr: FunctionAttribute = input.parse()?; + if let Some(prev) = attributes.iter().find(|a| **a == attr) { let mut e = Error::new(attr.span(), "duplicate attribute"); e.combine(Error::new(prev.span(), "previous declaration is here")); return Err(e) } - attributes.insert(attr); + attributes.push(attr); } Ok(Self(attributes)) } @@ -72,7 +71,7 @@ impl Spanned for FunctionAttributes { impl FunctionAttributes { #[inline] pub fn new() -> Self { - Self(HashSet::new()) + Self(Vec::new()) } pub fn visibility(&self) -> Option { diff --git a/crates/syn-solidity/src/attribute/variable.rs b/crates/syn-solidity/src/attribute/variable.rs index 49b0b0c742..43514e9206 100644 --- a/crates/syn-solidity/src/attribute/variable.rs +++ b/crates/syn-solidity/src/attribute/variable.rs @@ -1,7 +1,6 @@ use crate::{kw, Override, SolPath, Spanned, Visibility}; use proc_macro2::Span; use std::{ - collections::HashSet, fmt, hash::{Hash, Hasher}, mem, @@ -13,11 +12,11 @@ use syn::{ /// A list of unique variable attributes. #[derive(Clone, Debug)] -pub struct VariableAttributes(pub HashSet); +pub struct VariableAttributes(pub Vec); impl Parse for VariableAttributes { fn parse(input: ParseStream<'_>) -> Result { - let mut attributes = HashSet::new(); + let mut attributes = Vec::new(); while let Ok(attribute) = input.parse::() { let error = |prev: &VariableAttribute| { let mut e = Error::new(attribute.span(), "duplicate attribute"); @@ -28,15 +27,17 @@ impl Parse for VariableAttributes { // Only one of: `constant`, `immutable` match attribute { VariableAttribute::Constant(_) => { - if let Some(prev) = - attributes.get(&VariableAttribute::Immutable(Default::default())) + if let Some(prev) = attributes + .iter() + .find(|a| matches!(a, VariableAttribute::Immutable(_))) { return Err(error(prev)) } } VariableAttribute::Immutable(_) => { - if let Some(prev) = - attributes.get(&VariableAttribute::Constant(Default::default())) + if let Some(prev) = attributes + .iter() + .find(|a| matches!(a, VariableAttribute::Constant(_))) { return Err(error(prev)) } @@ -44,10 +45,10 @@ impl Parse for VariableAttributes { _ => {} } - if let Some(prev) = attributes.get(&attribute) { + if let Some(prev) = attributes.iter().find(|a| **a == attribute) { return Err(error(prev)) } - attributes.insert(attribute); + attributes.push(attribute); } Ok(Self(attributes)) } diff --git a/crates/syn-solidity/src/expr/mod.rs b/crates/syn-solidity/src/expr/mod.rs index ff05685d32..14dbe4b403 100644 --- a/crates/syn-solidity/src/expr/mod.rs +++ b/crates/syn-solidity/src/expr/mod.rs @@ -1,4 +1,6 @@ -use crate::{kw, utils::ParseNested, Lit, SolIdent, Spanned, Type}; +use crate::{ + kw, utils::ParseNested, Lit, LitDenominated, SolIdent, Spanned, SubDenomination, Type, +}; use proc_macro2::{Ident, Span}; use std::fmt; use syn::{ @@ -64,6 +66,9 @@ pub enum Expr { /// A literal: `hex"1234"`. Lit(Lit), + /// A number literal with a sub-denomination: `1 ether`. + LitDenominated(LitDenominated), + /// Access of a named member: `obj.k`. Member(ExprMember), @@ -106,6 +111,7 @@ impl fmt::Debug for Expr { Self::Ident(ident) => ident.fmt(f), Self::Index(expr) => expr.fmt(f), Self::Lit(lit) => lit.fmt(f), + Self::LitDenominated(lit) => lit.fmt(f), Self::Member(expr) => expr.fmt(f), Self::New(expr) => expr.fmt(f), Self::Payable(expr) => expr.fmt(f), @@ -150,6 +156,7 @@ impl Spanned for Expr { Self::Ident(ident) => ident.span(), Self::Index(expr) => expr.span(), Self::Lit(lit) => lit.span(), + Self::LitDenominated(lit) => lit.span(), Self::Member(expr) => expr.span(), Self::New(expr) => expr.span(), Self::Payable(expr) => expr.span(), @@ -172,6 +179,7 @@ impl Spanned for Expr { Self::Ident(ident) => ident.set_span(span), Self::Index(expr) => expr.set_span(span), Self::Lit(lit) => lit.set_span(span), + Self::LitDenominated(lit) => lit.set_span(span), Self::Member(expr) => expr.set_span(span), Self::New(expr) => expr.set_span(span), Self::Payable(expr) => expr.set_span(span), @@ -195,7 +203,16 @@ impl Expr { } else if UnOp::peek(input, &lookahead) { input.parse().map(Self::Unary) } else if Lit::peek(&lookahead) { - input.parse().map(Self::Lit) + match (input.parse()?, input.call(SubDenomination::parse_opt)?) { + (Lit::Number(number), Some(denom)) => { + Ok(Self::LitDenominated(LitDenominated { number, denom })) + } + (lit, None) => Ok(Self::Lit(lit)), + (_, Some(denom)) => Err(syn::Error::new( + denom.span(), + "unexpected subdenomination for literal", + )), + } } else if lookahead.peek(kw::payable) { input.parse().map(Self::Payable) } else if lookahead.peek(Token![type]) { diff --git a/crates/syn-solidity/src/ident/mod.rs b/crates/syn-solidity/src/ident/mod.rs index 9797d58031..a598ad98d7 100644 --- a/crates/syn-solidity/src/ident/mod.rs +++ b/crates/syn-solidity/src/ident/mod.rs @@ -11,8 +11,6 @@ use syn::{ mod path; pub use path::SolPath; -// TODO: Deny Solidity keywords - /// A Solidity identifier. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] @@ -58,9 +56,16 @@ impl From for Ident { } } +impl From<&str> for SolIdent { + fn from(value: &str) -> Self { + Self::new(value) + } +} + impl Parse for SolIdent { fn parse(input: ParseStream<'_>) -> Result { - input.call(Ident::parse_any).map(Self) + // TODO: Deny Solidity keywords + Self::parse_any(input) } } @@ -98,7 +103,12 @@ impl SolIdent { s } - /// See `[Ident::peek_any]`. + /// Parses any identifier including keywords. + pub fn parse_any(input: ParseStream<'_>) -> Result { + input.call(Ident::parse_any).map(Self) + } + + /// Peeks any identifier including keywords. pub fn peek_any(input: ParseStream<'_>) -> bool { input.peek(Ident::peek_any) } diff --git a/crates/syn-solidity/src/ident/path.rs b/crates/syn-solidity/src/ident/path.rs index db32097e8d..f09c967145 100644 --- a/crates/syn-solidity/src/ident/path.rs +++ b/crates/syn-solidity/src/ident/path.rs @@ -21,12 +21,6 @@ macro_rules! sol_path { $(path.push($crate::SolIdent::from($e));)+ path }}; - - ($($id:ident).+) => {{ - let mut path = $crate::SolPath::new(); - $(path.push($crate::SolIdent::new(stringify!($id))));+ - path - }}; } /// A list of identifiers, separated by dots. diff --git a/crates/syn-solidity/src/lib.rs b/crates/syn-solidity/src/lib.rs index 0e5413c94b..5c1c6badf8 100644 --- a/crates/syn-solidity/src/lib.rs +++ b/crates/syn-solidity/src/lib.rs @@ -49,7 +49,7 @@ pub use item::{ mod lit; pub use lit::{ - HexStr, Lit, LitHexStr, LitNumber, LitNumberKind, LitStr, LitUnicodeStr, SubDenomination, + HexStr, Lit, LitDenominated, LitHexStr, LitNumber, LitStr, LitUnicodeStr, SubDenomination, UnicodeStr, }; diff --git a/crates/syn-solidity/src/lit/mod.rs b/crates/syn-solidity/src/lit/mod.rs index bc77deb848..b080322607 100644 --- a/crates/syn-solidity/src/lit/mod.rs +++ b/crates/syn-solidity/src/lit/mod.rs @@ -1,14 +1,13 @@ -use std::fmt; - use crate::{kw, Spanned}; use proc_macro2::Span; +use std::fmt; use syn::{ parse::{Lookahead1, Parse, ParseStream}, LitBool, Result, }; mod number; -pub use number::{LitNumber, LitNumberKind, SubDenomination}; +pub use number::{LitDenominated, LitNumber, SubDenomination}; mod str; pub use self::str::{HexStr, LitHexStr, LitStr, LitUnicodeStr, UnicodeStr}; diff --git a/crates/syn-solidity/src/lit/number.rs b/crates/syn-solidity/src/lit/number.rs index 963f5bf2e6..452c0ab2b3 100644 --- a/crates/syn-solidity/src/lit/number.rs +++ b/crates/syn-solidity/src/lit/number.rs @@ -9,55 +9,13 @@ use syn::{ // TODO: Fixed point numbers /// An integer or fixed-point number literal: `1` or `1.0`. -#[derive(Clone, Debug)] -pub struct LitNumber { - pub kind: LitNumberKind, - pub denom: Option, -} - -impl Parse for LitNumber { - fn parse(input: ParseStream<'_>) -> Result { - Ok(Self { - kind: input.parse()?, - denom: input.call(SubDenomination::parse_opt)?, - }) - } -} - -impl Spanned for LitNumber { - fn span(&self) -> Span { - let span = self.kind.span(); - self.denom - .as_ref() - .and_then(|d| d.span().join(span)) - .unwrap_or(span) - } - - fn set_span(&mut self, span: Span) { - self.kind.set_span(span); - if let Some(denom) = &mut self.denom { - denom.set_span(span); - } - } -} - -impl LitNumber { - pub fn new(kind: LitNumberKind) -> Self { - Self { kind, denom: None } - } - - pub fn peek(lookahead: &Lookahead1<'_>) -> bool { - LitNumberKind::peek(lookahead) - } -} - #[derive(Clone)] -pub enum LitNumberKind { +pub enum LitNumber { Int(LitInt), Float(LitFloat), } -impl fmt::Debug for LitNumberKind { +impl fmt::Debug for LitNumber { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Int(lit) => lit.fmt(f), @@ -66,7 +24,7 @@ impl fmt::Debug for LitNumberKind { } } -impl Parse for LitNumberKind { +impl Parse for LitNumber { fn parse(input: ParseStream<'_>) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(LitInt) { @@ -79,7 +37,7 @@ impl Parse for LitNumberKind { } } -impl Spanned for LitNumberKind { +impl Spanned for LitNumber { fn span(&self) -> Span { match self { Self::Int(lit) => lit.span(), @@ -95,7 +53,7 @@ impl Spanned for LitNumberKind { } } -impl LitNumberKind { +impl LitNumber { pub fn new_int(repr: &str, span: Span) -> Self { Self::Int(LitInt::new(repr, span)) } @@ -147,6 +105,33 @@ impl LitNumberKind { } } +#[derive(Clone, Debug)] +pub struct LitDenominated { + pub number: LitNumber, + pub denom: SubDenomination, +} + +impl Parse for LitDenominated { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + number: input.parse()?, + denom: input.parse()?, + }) + } +} + +impl Spanned for LitDenominated { + fn span(&self) -> Span { + let span = self.number.span(); + span.join(self.denom.span()).unwrap_or(span) + } + + fn set_span(&mut self, span: Span) { + self.number.set_span(span); + self.denom.set_span(span); + } +} + kw_enum! { pub enum SubDenomination { Wei(kw::wei), diff --git a/crates/syn-solidity/src/stmt/for.rs b/crates/syn-solidity/src/stmt/for.rs index 835317a4a8..c8d3d4305a 100644 --- a/crates/syn-solidity/src/stmt/for.rs +++ b/crates/syn-solidity/src/stmt/for.rs @@ -16,10 +16,10 @@ use syn::{ pub struct StmtFor { pub for_token: Token![for], pub paren_token: Paren, - pub init: ForInitStmt, - pub cond: Option, + pub init: Box, + pub cond: Option>, pub semi_token: Token![;], - pub post: Option, + pub post: Option>, pub body: Box, } diff --git a/crates/syn-solidity/src/stmt/try.rs b/crates/syn-solidity/src/stmt/try.rs index 46a475ef43..dd1abf9640 100644 --- a/crates/syn-solidity/src/stmt/try.rs +++ b/crates/syn-solidity/src/stmt/try.rs @@ -15,7 +15,7 @@ use syn::{ #[derive(Clone)] pub struct StmtTry { pub try_token: Token![try], - pub expr: Expr, + pub expr: Box, pub returns: Option, /// The try block. pub block: Block, diff --git a/crates/syn-solidity/src/stmt/var_decl.rs b/crates/syn-solidity/src/stmt/var_decl.rs index 9598cbe26a..db3d794ab5 100644 --- a/crates/syn-solidity/src/stmt/var_decl.rs +++ b/crates/syn-solidity/src/stmt/var_decl.rs @@ -15,7 +15,7 @@ use syn::{ /// #[derive(Clone)] pub struct StmtVarDecl { - pub declaration: VarDeclDecl, + pub declaration: Box, pub assignment: Option<(Token![=], Expr)>, pub semi_token: Token![;], } @@ -43,7 +43,7 @@ impl Parse for StmtVarDecl { let semi_token = input.parse()?; Ok(Self { - declaration, + declaration: Box::new(declaration), assignment, semi_token, }) diff --git a/crates/syn-solidity/src/type/array.rs b/crates/syn-solidity/src/type/array.rs index 46de9457e3..1d467104e7 100644 --- a/crates/syn-solidity/src/type/array.rs +++ b/crates/syn-solidity/src/type/array.rs @@ -1,4 +1,4 @@ -use crate::{Spanned, Type}; +use crate::{Expr, Lit, LitNumber, Spanned, Type}; use proc_macro2::Span; use std::{ fmt, @@ -7,9 +7,9 @@ use std::{ }; use syn::{ bracketed, - parse::{Parse, ParseStream}, + parse::{discouraged::Speculative, Parse, ParseStream}, token::Bracket, - LitInt, Result, + Result, }; /// An array type. @@ -17,12 +17,12 @@ use syn::{ pub struct TypeArray { pub ty: Box, pub bracket_token: Bracket, - pub size: Option, + pub size: Option>, } impl PartialEq for TypeArray { fn eq(&self, other: &Self) -> bool { - self.ty == other.ty && self.size == other.size + self.ty == other.ty && self.size() == other.size() } } @@ -31,7 +31,7 @@ impl Eq for TypeArray {} impl Hash for TypeArray { fn hash(&self, state: &mut H) { self.ty.hash(state); - self.size.hash(state); + self.size().hash(state); } } @@ -39,7 +39,7 @@ impl fmt::Debug for TypeArray { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("TypeArray") .field(&self.ty) - .field(&self.size.as_ref().map(|s| s.base10_digits())) + .field(&self.size()) .finish() } } @@ -48,7 +48,7 @@ impl fmt::Display for TypeArray { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.ty.fmt(f)?; f.write_str("[")?; - if let Some(s) = &self.size { + if let Some(s) = self.size_lit() { f.write_str(s.base10_digits())?; } f.write_str("]") @@ -80,7 +80,15 @@ impl Spanned for TypeArray { impl TypeArray { /// Returns the size of the array, or None if dynamic. pub fn size(&self) -> Option { - self.size.as_ref().map(|s| s.base10_parse().unwrap()) + self.size_lit().map(|s| s.base10_parse().unwrap()) + } + + /// Returns the size of the array, or None if dynamic. + pub fn size_lit(&self) -> Option<&LitNumber> { + self.size.as_ref().map(|s| match &**s { + Expr::Lit(Lit::Number(n)) => n, + _ => panic!("unevaluated literal in array size"), + }) } /// See [`Type::is_abi_dynamic`]. @@ -98,16 +106,27 @@ impl TypeArray { ty, bracket_token: bracketed!(content in input), size: { - let size = if content.is_empty() { + if content.is_empty() { None } else { - Some(content.parse::()?) - }; - // Validate the size - if let Some(sz) = &size { + let fork = content.fork(); + let sz = match fork.parse::() { + Ok(lit) => lit, + Err(e) => { + return Err(match fork.parse::() { + Ok(_) => syn::Error::new( + e.span(), + "generic expressions are not supported in array type sizes", + ), + Err(e) => e, + }) + } + }; + content.advance_to(&fork); + // Validate the size sz.base10_parse::()?; + Some(Box::new(Expr::Lit(Lit::Number(LitNumber::Int(sz))))) } - size }, }) } diff --git a/crates/syn-solidity/tests/ident.rs b/crates/syn-solidity/tests/ident.rs index 367f211db1..ef7594db38 100644 --- a/crates/syn-solidity/tests/ident.rs +++ b/crates/syn-solidity/tests/ident.rs @@ -1,7 +1,4 @@ -use syn_solidity::{SolIdent, SolPath}; - -#[macro_use] -mod macros; +use syn_solidity::{sol_path, SolIdent, SolPath}; #[test] fn ident() { @@ -12,7 +9,7 @@ fn ident() { #[test] fn ident_path() { let path: SolPath = syn::parse_str("a.b.c").unwrap(); - assert_eq!(path, path![a, b, c]); + assert_eq!(path, sol_path!["a", "b", "c"]); } #[test] diff --git a/crates/syn-solidity/tests/macros.rs b/crates/syn-solidity/tests/macros.rs deleted file mode 100644 index 4d6a287c58..0000000000 --- a/crates/syn-solidity/tests/macros.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(unused_macros, unused_macro_rules)] - -macro_rules! path { - ($($e:ident),* $(,)?) => {{ - let mut path = syn_solidity::SolPath::new(); - $(path.push(syn_solidity::SolIdent::new(stringify!($e)));)+ - path - }} -}