mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10936 from ethereum/customErrorsABI
ABI and Natspec for errors
This commit is contained in:
commit
15fe07bebe
@ -121,6 +121,23 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
abi.emplace(std::move(event));
|
||||
}
|
||||
|
||||
for (ErrorDefinition const* error: _contractDef.interfaceErrors())
|
||||
{
|
||||
Json::Value errorJson;
|
||||
errorJson["type"] = "error";
|
||||
errorJson["name"] = error->name();
|
||||
errorJson["inputs"] = Json::arrayValue;
|
||||
for (auto const& p: error->parameters())
|
||||
{
|
||||
Type const* type = p->annotation().type->interfaceType(false);
|
||||
solAssert(type, "");
|
||||
errorJson["inputs"].append(
|
||||
formatType(p->name(), *type, *p->annotation().type, false)
|
||||
);
|
||||
}
|
||||
abi.emplace(move(errorJson));
|
||||
}
|
||||
|
||||
Json::Value abiJson{Json::arrayValue};
|
||||
for (auto& f: abi)
|
||||
abiJson.append(std::move(f));
|
||||
|
@ -38,11 +38,10 @@ using namespace solidity::frontend;
|
||||
Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
Json::Value events(Json::objectValue);
|
||||
|
||||
doc["version"] = Json::Value(c_natspecVersion);
|
||||
doc["kind"] = Json::Value("user");
|
||||
doc["methods"] = Json::objectValue;
|
||||
|
||||
auto constructorDefinition(_contractDef.constructor());
|
||||
if (constructorDefinition)
|
||||
@ -53,7 +52,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
// add the constructor, only if we have any documentation to add
|
||||
Json::Value user;
|
||||
user["notice"] = Json::Value(value);
|
||||
methods["constructor"] = user;
|
||||
doc["methods"]["constructor"] = user;
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,24 +74,27 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
}
|
||||
|
||||
if (!value.empty())
|
||||
{
|
||||
Json::Value user;
|
||||
// since @notice is the only user tag if missing function should not appear
|
||||
user["notice"] = Json::Value(value);
|
||||
methods[it.second->externalSignature()] = user;
|
||||
}
|
||||
doc["methods"][it.second->externalSignature()]["notice"] = value;
|
||||
}
|
||||
|
||||
for (auto const& event: _contractDef.interfaceEvents())
|
||||
{
|
||||
string value = extractDoc(event->annotation().docTags, "notice");
|
||||
if (!value.empty())
|
||||
events[event->functionType(true)->externalSignature()]["notice"] = value;
|
||||
doc["events"][event->functionType(true)->externalSignature()]["notice"] = value;
|
||||
}
|
||||
|
||||
doc["methods"] = methods;
|
||||
if (!events.empty())
|
||||
doc["events"] = events;
|
||||
for (auto const& error: _contractDef.interfaceErrors())
|
||||
{
|
||||
string value = extractDoc(error->annotation().docTags, "notice");
|
||||
if (!value.empty())
|
||||
{
|
||||
Json::Value errorDoc;
|
||||
errorDoc["notice"] = value;
|
||||
doc["errors"][error->functionType(true)->externalSignature()].append(move(errorDoc));
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
@ -100,7 +102,6 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc = extractCustomDoc(_contractDef.annotation().docTags);
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
doc["version"] = Json::Value(c_natspecVersion);
|
||||
doc["kind"] = Json::Value("dev");
|
||||
@ -115,13 +116,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
if (!dev.empty())
|
||||
doc["details"] = Json::Value(dev);
|
||||
|
||||
doc["methods"] = Json::objectValue;
|
||||
auto constructorDefinition(_contractDef.constructor());
|
||||
if (constructorDefinition)
|
||||
{
|
||||
Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
|
||||
if (!constructor.empty())
|
||||
// add the constructor, only if we have any documentation to add
|
||||
methods["constructor"] = constructor;
|
||||
doc["methods"]["constructor"] = constructor;
|
||||
}
|
||||
|
||||
for (auto const& it: _contractDef.interfaceFunctions())
|
||||
@ -135,34 +137,30 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||
|
||||
if (!jsonReturn.empty())
|
||||
method["returns"] = jsonReturn;
|
||||
method["returns"] = move(jsonReturn);
|
||||
|
||||
if (!method.empty())
|
||||
methods[it.second->externalSignature()] = method;
|
||||
doc["methods"][it.second->externalSignature()] = move(method);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value stateVariables(Json::objectValue);
|
||||
for (VariableDeclaration const* varDecl: _contractDef.stateVariables())
|
||||
{
|
||||
if (auto devDoc = devDocumentation(varDecl->annotation().docTags); !devDoc.empty())
|
||||
stateVariables[varDecl->name()] = devDoc;
|
||||
doc["stateVariables"][varDecl->name()] = devDoc;
|
||||
|
||||
solAssert(varDecl->annotation().docTags.count("return") <= 1, "");
|
||||
if (varDecl->annotation().docTags.count("return") == 1)
|
||||
stateVariables[varDecl->name()]["return"] = extractDoc(varDecl->annotation().docTags, "return");
|
||||
doc["stateVariables"][varDecl->name()]["return"] = extractDoc(varDecl->annotation().docTags, "return");
|
||||
}
|
||||
|
||||
Json::Value events(Json::objectValue);
|
||||
for (auto const& event: _contractDef.events())
|
||||
if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty())
|
||||
events[event->functionType(true)->externalSignature()] = devDoc;
|
||||
doc["events"][event->functionType(true)->externalSignature()] = devDoc;
|
||||
|
||||
doc["methods"] = methods;
|
||||
if (!stateVariables.empty())
|
||||
doc["stateVariables"] = stateVariables;
|
||||
if (!events.empty())
|
||||
doc["events"] = events;
|
||||
for (auto const& error: _contractDef.interfaceErrors())
|
||||
if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty())
|
||||
doc["errors"][error->functionType(true)->externalSignature()].append(devDoc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
93
test/libsolidity/ABIJson/errors.sol
Normal file
93
test/libsolidity/ABIJson/errors.sol
Normal file
@ -0,0 +1,93 @@
|
||||
error FreeError();
|
||||
contract X {
|
||||
error E(uint);
|
||||
error E2(uint a, uint b);
|
||||
error E4(uint a, bytes[][] c);
|
||||
struct S { uint x; }
|
||||
error E5(S t);
|
||||
function f() public pure {
|
||||
revert FreeError();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// :X
|
||||
// [
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "a",
|
||||
// "type": "uint256"
|
||||
// },
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "b",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E2",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "a",
|
||||
// "type": "uint256"
|
||||
// },
|
||||
// {
|
||||
// "internalType": "bytes[][]",
|
||||
// "name": "c",
|
||||
// "type": "bytes[][]"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E4",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "components":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "x",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "internalType": "struct X.S",
|
||||
// "name": "t",
|
||||
// "type": "tuple"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E5",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "FreeError",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "f",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "pure",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
94
test/libsolidity/ABIJson/errors_referenced.sol
Normal file
94
test/libsolidity/ABIJson/errors_referenced.sol
Normal file
@ -0,0 +1,94 @@
|
||||
error E1();
|
||||
function f() {
|
||||
revert E1();
|
||||
}
|
||||
contract C {
|
||||
// unreferenced but inherited
|
||||
error E2();
|
||||
}
|
||||
contract D {
|
||||
// referenced
|
||||
error E3(uint);
|
||||
}
|
||||
contract X is C {
|
||||
// unreferenced but defined
|
||||
error E3();
|
||||
function g(uint x) public {
|
||||
if (x == 0)
|
||||
f();
|
||||
else if (x == 2)
|
||||
revert D.E3(1);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// :C
|
||||
// [
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "E2",
|
||||
// "type": "error"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
//
|
||||
// :D
|
||||
// [
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E3",
|
||||
// "type": "error"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
//
|
||||
// :X
|
||||
// [
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "E1",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "E2",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "E3",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "E3",
|
||||
// "type": "error"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "x",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "g",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
@ -2247,6 +2247,108 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
||||
checkNatspec(sourceCode, "B", natspec2, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(error)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
/// Something failed.
|
||||
/// @dev an error.
|
||||
/// @param a first parameter
|
||||
/// @param b second parameter
|
||||
error E(uint a, uint b);
|
||||
}
|
||||
)";
|
||||
|
||||
char const* devdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [{
|
||||
"details" : "an error.",
|
||||
"params" :
|
||||
{
|
||||
"a" : "first parameter",
|
||||
"b" : "second parameter"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"methods": {}
|
||||
})X";
|
||||
|
||||
checkNatspec(sourceCode, "test", devdoc, false);
|
||||
|
||||
char const* userdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [{
|
||||
"notice" : "Something failed."
|
||||
}]
|
||||
},
|
||||
"methods": {}
|
||||
})X";
|
||||
checkNatspec(sourceCode, "test", userdoc, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(error_multiple)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract A {
|
||||
/// Something failed.
|
||||
/// @dev an error.
|
||||
/// @param x first parameter
|
||||
/// @param y second parameter
|
||||
error E(uint x, uint y);
|
||||
}
|
||||
contract test {
|
||||
/// X Something failed.
|
||||
/// @dev X an error.
|
||||
/// @param a X first parameter
|
||||
/// @param b X second parameter
|
||||
error E(uint a, uint b);
|
||||
function f(bool a) public pure {
|
||||
if (a)
|
||||
revert E(1, 2);
|
||||
else
|
||||
revert A.E(5, 6);
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
char const* devdoc = R"X({
|
||||
"methods": {},
|
||||
"errors": {
|
||||
"E(uint256,uint256)": [
|
||||
{
|
||||
"details" : "an error.",
|
||||
"params" :
|
||||
{
|
||||
"x" : "first parameter",
|
||||
"y" : "second parameter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details" : "X an error.",
|
||||
"params" :
|
||||
{
|
||||
"a" : "X first parameter",
|
||||
"b" : "X second parameter"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})X";
|
||||
|
||||
checkNatspec(sourceCode, "test", devdoc, false);
|
||||
|
||||
char const* userdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [
|
||||
{ "notice" : "Something failed." },
|
||||
{ "notice" : "X Something failed." }
|
||||
]
|
||||
},
|
||||
"methods": {}
|
||||
})X";
|
||||
checkNatspec(sourceCode, "test", userdoc, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(custom)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
|
Loading…
Reference in New Issue
Block a user