feat(core, abi): support shorthand function declarations (#472)

* feat(core, abi): support shorthand functions

* simplify a bit but not much lol

* refactor a bit, beautify a lot

* fail on absent output parens for shorthand

* chore: fix clippy lints

* fix: parse shorthand functions in multiline parsing

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
Alexey Shekhirin 2021-09-28 13:24:00 +03:00 committed by GitHub
parent ede76a2feb
commit 7a10944507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 34 deletions

View File

@ -76,13 +76,7 @@ impl AbiParser {
for mut line in types { for mut line in types {
line = line.trim_start(); line = line.trim_start();
if line.starts_with("function") { if line.starts_with("event") {
let function = self.parse_function(line)?;
abi.functions
.entry(function.name.clone())
.or_default()
.push(function);
} else if line.starts_with("event") {
let event = self.parse_event(line)?; let event = self.parse_event(line)?;
abi.events abi.events
.entry(event.name.clone()) .entry(event.name.clone())
@ -91,7 +85,15 @@ impl AbiParser {
} else if line.starts_with("constructor") { } else if line.starts_with("constructor") {
abi.constructor = Some(self.parse_constructor(line)?); abi.constructor = Some(self.parse_constructor(line)?);
} else { } else {
bail!("Illegal abi `{}`", line) // function may have shorthand declaration, so it won't start with "function"
let function = match self.parse_function(line) {
Ok(function) => function,
Err(_) => bail!("Illegal abi `{}`", line),
};
abi.functions
.entry(function.name.clone())
.or_default()
.push(function);
} }
} }
Ok(abi) Ok(abi)
@ -237,41 +239,55 @@ impl AbiParser {
pub fn parse_function(&mut self, s: &str) -> Result<Function> { pub fn parse_function(&mut self, s: &str) -> Result<Function> {
let mut input = s.trim(); let mut input = s.trim();
if !input.starts_with("function ") { let shorthand = !input.starts_with("function ");
bail!("Not a function `{}`", input)
if !shorthand {
input = &input[8..];
} }
input = &input[8..];
let name = parse_identifier(&mut input)?; let name = parse_identifier(&mut input)?;
input = input
.strip_prefix('(')
.ok_or_else(|| format_err!("Expected input args parentheses at `{}`", s))?;
let mut iter = input.split(" returns"); let (input_args_modifiers, output_args) = match input.rsplit_once('(') {
Some((first, second)) => Ok((first, Some(second))),
None if shorthand => Err(format_err!("Expected output args parentheses at `{}`", s)),
None => Ok((input, None)),
}?;
let parens = iter let mut input_args_modifiers_iter = input_args_modifiers
.trim_end()
.strip_suffix(" returns")
.unwrap_or(input_args_modifiers)
.splitn(2, ')');
let input_args = match input_args_modifiers_iter
.next() .next()
.ok_or_else(|| format_err!("Invalid function declaration at `{}`", s))? .ok_or_else(|| format_err!("Expected input args parentheses at `{}`", s))?
.trim_end(); {
"" => None,
input_params_args => Some(input_params_args),
};
let modifiers = match input_args_modifiers_iter
.next()
.ok_or_else(|| format_err!("Expected input args parentheses at `{}`", s))?
{
"" => None,
modifiers => Some(modifiers),
};
let mut parens_iter = parens.rsplitn(2, ')'); let inputs = if let Some(params) = input_args {
let mut modifiers = parens_iter.next(); self.parse_params(params)?
let input_params = if let Some(args) = parens_iter.next() {
args
} else { } else {
modifiers Vec::new()
.take() };
.ok_or(ParseError::ParseError(super::Error::InvalidData))?
}
.trim_start()
.strip_prefix('(')
.ok_or(ParseError::ParseError(super::Error::InvalidData))?;
let inputs = self.parse_params(input_params)?; let outputs = if let Some(params) = output_args {
let outputs = if let Some(params) = iter.next() {
let params = params let params = params
.trim() .trim()
.strip_prefix('(') .strip_suffix(')')
.and_then(|s| s.strip_suffix(')')) .ok_or_else(|| format_err!("Expected output args parentheses at `{}`", s))?;
.ok_or_else(|| format_err!("Expected parentheses at `{}`", s))?;
self.parse_params(params)? self.parse_params(params)?
} else { } else {
Vec::new() Vec::new()
@ -619,11 +635,16 @@ mod tests {
fn can_parse_functions() { fn can_parse_functions() {
[ [
"function foo(uint256[] memory x) external view returns (address)", "function foo(uint256[] memory x) external view returns (address)",
"function bar(uint256[] memory x) returns (address)", "function bar(uint256[] memory x) returns(address)",
"function bar(uint256[] memory x, uint32 y) returns (address, uint256)", "function bar(uint256[] memory x, uint32 y) returns (address, uint256)",
"function foo(address[] memory, bytes memory) returns (bytes memory)", "function foo(address[] memory, bytes memory) returns (bytes memory)",
"function bar(uint256[] memory x)", "function bar(uint256[] memory x)",
"function bar()", "function bar()",
"bar(uint256[] memory x)(address)",
"bar(uint256[] memory x, uint32 y)(address, uint256)",
"foo(address[] memory, bytes memory)(bytes memory)",
"bar(uint256[] memory x)()",
"bar()()",
] ]
.iter() .iter()
.for_each(|x| { .for_each(|x| {
@ -639,6 +660,8 @@ mod tests {
"event FireEvent(Voter v, NestedVoter2 n)", "event FireEvent(Voter v, NestedVoter2 n)",
"function foo(uint256[] memory x) external view returns (address)", "function foo(uint256[] memory x) external view returns (address)",
"function call(Voter memory voter) returns (address, uint256)", "function call(Voter memory voter) returns (address, uint256)",
"foo(uint256[] memory x)()",
"call(Voter memory voter)(address, uint256)",
"struct NestedVoter { Voter voter; bool voted; address delegate; uint vote; }", "struct NestedVoter { Voter voter; bool voted; address delegate; uint vote; }",
"struct NestedVoter2 { NestedVoter[] voter; Voter[10] votes; address delegate; uint vote; }", "struct NestedVoter2 { NestedVoter[] voter; Voter[10] votes; address delegate; uint vote; }",
]; ];