"unrecognised token '.' when "if (error.length > 0)"
I try to use solang-parser to parse some contract and found "unrecognised token '.' error.
This sample contract code can trigger this issue:
pragma solidity 0.6.12;
contract A {
address init;
constructor(address init_addr) {
init = init_addr;
}
function f(bytes calldata _calldata) public {
(bool success, bytes memory error) = _init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
revert(string(error));
} else {
revert("aa");
}
}
}
}
"unrecognised token '.', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"+=\", \",\", \"-\", \"-=\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \">\", \">=\", \">>\", \">>=\", \"?\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"default\", \"error\", \"leave\", \"memory\", \"revert\", \"storage\", \"switch\", \"|\", \"|=\", \"||\", \"}\", identifier"
and I found the error range is:
if (error.length > 0) {
It seems like solang-parser does not properly parse this error.length?
oh, error is the keyword of solidity, I got it.
Emm... I found that the keyword "error" can be a legal variable name.
pragma solidity ^0.8.0;
contract A {
address init;
constructor(address init_addr) {
init = init_addr;
}
function f(bytes calldata _calldata) public {
(bool success, bytes memory error) = init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
revert(string(error));
} else {
revert("aa");
}
}
}
}
This sample code can be compiled successfully. So I think we should reopen this issue.
I try to fix it by adding a special case in solidity.lalrpop. But I don't know if this will have other effects.
NoFunctionTyPrecedence0: Expression = {
<a:@L> <e:Precedence0> "++" <b:@R> => Expression::PostIncrement(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> <e:Precedence0> "--" <b:@R> => Expression::PostDecrement(Loc::File(file_no, a, b), Box::new(e)),
<FunctionCall> => <>,
<a:@L> <e:Precedence0> "[" <i:Expression?> "]" <b:@R> => Expression::ArraySubscript(Loc::File(file_no, a, b), Box::new(e), i.map(Box::new)),
<a:@L> <e:Precedence0> "[" <l:Expression?> ":" <r:Expression?> "]" <b:@R> => Expression::ArraySlice(Loc::File(file_no, a, b), Box::new(e), l.map(Box::new), r.map(Box::new)),
<a:@L> <e:Precedence0> "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e), i),
<a:@L> "error" <al:@L> "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(Expression::Variable(Identifier { loc: Loc::File(file_no, a, al), name: "error".to_string()})), i),
// Solidity has ".address" members on external function types. Address is a keyword, so special casing needed
<a:@L> <e:Precedence0> "." <al:@L> "address" <b:@R> => {
Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
Identifier { loc: Loc::File(file_no, al, b), name: "address".to_string() })
},
<a:@L> "error" al:@L "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(Expression::Variable(Identifier { loc: Loc::File(file_no, a, al), name: "error".to_string()})), i),
Maybe have any better solutions? I'm not very familiar with lalrpop. Thanks.
You are right: in solc, error is not a keyword, even though it can be used for declaring error definitions using error Foo(int);.
The way to fix this is to add:
<l:@L> "error" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "error".to_string()},
Unfortunately this makes the grammar ambiguous which is not permitted in lalrpop.
This is quite a tricky issue to solve with lalrpop :disappointed:
@seanyoung Above contract compiles fine for the substrate target, I conclude that this has been solved in the meantime.