feat: enumerate overloaded functions if they are nameless (#545)
* fix: overloaded with same type arguments * fix: numerate overloaded functions * docs: add note * chore: update changelog * style: rename vars
This commit is contained in:
parent
46bedb3282
commit
4123823383
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- use enumerated aliases for overloaded functions [#545](https://github.com/gakonst/ethers-rs/pull/545)
|
||||||
- move `AbiEncode` `AbiDecode` trait to ethers-core and implement for core types [#531](https://github.com/gakonst/ethers-rs/pull/531)
|
- move `AbiEncode` `AbiDecode` trait to ethers-core and implement for core types [#531](https://github.com/gakonst/ethers-rs/pull/531)
|
||||||
- add `EthCall` trait and derive macro which generates matching structs for contract calls [#517](https://github.com/gakonst/ethers-rs/pull/517)
|
- add `EthCall` trait and derive macro which generates matching structs for contract calls [#517](https://github.com/gakonst/ethers-rs/pull/517)
|
||||||
- `abigen!` now generates `Display` for all events using the new `EthDisplay` macro [#513](https://github.com/gakonst/ethers-rs/pull/513)
|
- `abigen!` now generates `Display` for all events using the new `EthDisplay` macro [#513](https://github.com/gakonst/ethers-rs/pull/513)
|
||||||
|
|
|
@ -282,15 +282,27 @@ impl Context {
|
||||||
// sort functions by number of inputs asc
|
// sort functions by number of inputs asc
|
||||||
let mut functions = functions.iter().collect::<Vec<_>>();
|
let mut functions = functions.iter().collect::<Vec<_>>();
|
||||||
functions.sort_by(|f1, f2| f1.inputs.len().cmp(&f2.inputs.len()));
|
functions.sort_by(|f1, f2| f1.inputs.len().cmp(&f2.inputs.len()));
|
||||||
let prev = functions[0];
|
let first = functions[0];
|
||||||
for duplicate in functions.into_iter().skip(1) {
|
// assuming here that if there are overloaded functions with nameless params like `log;,
|
||||||
|
// log(string); log(string, string)` `log()` should also be aliased with its
|
||||||
|
// index to `log0`
|
||||||
|
let mut add_alias_for_first_with_idx = false;
|
||||||
|
for (idx, duplicate) in functions.into_iter().enumerate().skip(1) {
|
||||||
// attempt to find diff in the input arguments
|
// attempt to find diff in the input arguments
|
||||||
let diff = duplicate
|
let mut diff = Vec::new();
|
||||||
.inputs
|
let mut same_params = true;
|
||||||
.iter()
|
for (idx, i1) in duplicate.inputs.iter().enumerate() {
|
||||||
.filter(|i1| prev.inputs.iter().all(|i2| *i1 != i2))
|
if first.inputs.iter().all(|i2| i1 != i2) {
|
||||||
.collect::<Vec<_>>();
|
diff.push(i1);
|
||||||
|
same_params = false;
|
||||||
|
} else {
|
||||||
|
// check for cases like `log(string); log(string, string)` by keep track of
|
||||||
|
// same order
|
||||||
|
if same_params && idx + 1 > first.inputs.len() {
|
||||||
|
diff.push(i1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let alias = match diff.len() {
|
let alias = match diff.len() {
|
||||||
0 => {
|
0 => {
|
||||||
// this should not happen since functions with same name and input are
|
// this should not happen since functions with same name and input are
|
||||||
|
@ -302,13 +314,22 @@ impl Context {
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
// single additional input params
|
// single additional input params
|
||||||
|
if diff[0].name.is_empty() {
|
||||||
|
add_alias_for_first_with_idx = true;
|
||||||
|
format!("{}1", duplicate.name.to_snake_case())
|
||||||
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{}_with_{}",
|
"{}_with_{}",
|
||||||
duplicate.name.to_snake_case(),
|
duplicate.name.to_snake_case(),
|
||||||
diff[0].name.to_snake_case()
|
diff[0].name.to_snake_case()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
if diff.iter().any(|d| d.name.is_empty()) {
|
||||||
|
add_alias_for_first_with_idx = true;
|
||||||
|
format!("{}{}", duplicate.name.to_snake_case(), idx)
|
||||||
|
} else {
|
||||||
// 1 + n additional input params
|
// 1 + n additional input params
|
||||||
let and = diff
|
let and = diff
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -323,9 +344,16 @@ impl Context {
|
||||||
and
|
and
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
aliases.insert(duplicate.abi_signature(), util::safe_ident(&alias));
|
aliases.insert(duplicate.abi_signature(), util::safe_ident(&alias));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if add_alias_for_first_with_idx {
|
||||||
|
// insert an alias for the root duplicated call
|
||||||
|
let prev_alias = format!("{}0", first.name.to_snake_case());
|
||||||
|
aliases.insert(first.abi_signature(), util::safe_ident(&prev_alias));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(aliases)
|
Ok(aliases)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,9 @@ pub(crate) mod utils;
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// Aliases for overloaded functions with no aliases provided in the `method` section are derived
|
||||||
|
/// automatically.
|
||||||
|
///
|
||||||
/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
|
/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
|
||||||
/// This is useful if the contracts use ABIEncoderV2 structs. In which case
|
/// This is useful if the contracts use ABIEncoderV2 structs. In which case
|
||||||
/// `abigen!` bundles all type duplicates so that all rust contracts also use
|
/// `abigen!` bundles all type duplicates so that all rust contracts also use
|
||||||
|
|
|
@ -191,6 +191,8 @@ fn can_handle_overloaded_functions() {
|
||||||
getValue() (uint256)
|
getValue() (uint256)
|
||||||
getValue(uint256 otherValue) (uint256)
|
getValue(uint256 otherValue) (uint256)
|
||||||
getValue(uint256 otherValue, address addr) (uint256)
|
getValue(uint256 otherValue, address addr) (uint256)
|
||||||
|
setValue(string, string)
|
||||||
|
setValue(string)
|
||||||
]"#
|
]"#
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -238,6 +240,30 @@ fn can_handle_overloaded_functions() {
|
||||||
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
|
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
|
||||||
assert_eq!(contract_call, decoded_enum);
|
assert_eq!(contract_call, decoded_enum);
|
||||||
assert_eq!(encoded_call, contract_call.encode().into());
|
assert_eq!(encoded_call, contract_call.encode().into());
|
||||||
|
|
||||||
|
let call = SetValue0Call("message".to_string());
|
||||||
|
let _contract_call = SimpleContractCalls::SetValue0(call);
|
||||||
|
let call = SetValue1Call("message".to_string(), "message".to_string());
|
||||||
|
let _contract_call = SimpleContractCalls::SetValue1(call);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_handle_even_more_overloaded_functions() {
|
||||||
|
abigen!(
|
||||||
|
ConsoleLog,
|
||||||
|
r#"[
|
||||||
|
log()
|
||||||
|
log(string, string)
|
||||||
|
log(string)
|
||||||
|
]"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let _call = Log0Call;
|
||||||
|
let _contract_call = ConsoleLogCalls::Log0;
|
||||||
|
let call = Log1Call("message".to_string());
|
||||||
|
let _contract_call = ConsoleLogCalls::Log1(call);
|
||||||
|
let call = Log2Call("message".to_string(), "message".to_string());
|
||||||
|
let _contract_call = ConsoleLogCalls::Log2(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
Loading…
Reference in New Issue