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:
Matthias Seitz 2021-10-31 12:24:02 +01:00 committed by GitHub
parent 46bedb3282
commit 4123823383
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 26 deletions

View File

@ -4,6 +4,7 @@
### 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)
- 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)

View File

@ -282,15 +282,27 @@ impl Context {
// sort functions by number of inputs asc
let mut functions = functions.iter().collect::<Vec<_>>();
functions.sort_by(|f1, f2| f1.inputs.len().cmp(&f2.inputs.len()));
let prev = functions[0];
for duplicate in functions.into_iter().skip(1) {
let first = functions[0];
// 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
let diff = duplicate
.inputs
.iter()
.filter(|i1| prev.inputs.iter().all(|i2| *i1 != i2))
.collect::<Vec<_>>();
let mut diff = Vec::new();
let mut same_params = true;
for (idx, i1) in duplicate.inputs.iter().enumerate() {
if first.inputs.iter().all(|i2| i1 != i2) {
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() {
0 => {
// this should not happen since functions with same name and input are
@ -302,30 +314,46 @@ impl Context {
}
1 => {
// single additional input params
format!(
"{}_with_{}",
duplicate.name.to_snake_case(),
diff[0].name.to_snake_case()
)
if diff[0].name.is_empty() {
add_alias_for_first_with_idx = true;
format!("{}1", duplicate.name.to_snake_case())
} else {
format!(
"{}_with_{}",
duplicate.name.to_snake_case(),
diff[0].name.to_snake_case()
)
}
}
_ => {
// 1 + n additional input params
let and = diff
.iter()
.skip(1)
.map(|i| i.name.to_snake_case())
.collect::<Vec<_>>()
.join("_and_");
format!(
"{}_with_{}_and_{}",
duplicate.name.to_snake_case(),
diff[0].name.to_snake_case(),
and
)
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
let and = diff
.iter()
.skip(1)
.map(|i| i.name.to_snake_case())
.collect::<Vec<_>>()
.join("_and_");
format!(
"{}_with_{}_and_{}",
duplicate.name.to_snake_case(),
diff[0].name.to_snake_case(),
and
)
}
}
};
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)
}

View File

@ -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 `;`
/// This is useful if the contracts use ABIEncoderV2 structs. In which case
/// `abigen!` bundles all type duplicates so that all rust contracts also use

View File

@ -191,6 +191,8 @@ fn can_handle_overloaded_functions() {
getValue() (uint256)
getValue(uint256 otherValue) (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();
assert_eq!(contract_call, decoded_enum);
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]