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));
|
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};
|
Json::Value abiJson{Json::arrayValue};
|
||||||
for (auto& f: abi)
|
for (auto& f: abi)
|
||||||
abiJson.append(std::move(f));
|
abiJson.append(std::move(f));
|
||||||
|
@ -38,11 +38,10 @@ using namespace solidity::frontend;
|
|||||||
Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||||
{
|
{
|
||||||
Json::Value doc;
|
Json::Value doc;
|
||||||
Json::Value methods(Json::objectValue);
|
|
||||||
Json::Value events(Json::objectValue);
|
|
||||||
|
|
||||||
doc["version"] = Json::Value(c_natspecVersion);
|
doc["version"] = Json::Value(c_natspecVersion);
|
||||||
doc["kind"] = Json::Value("user");
|
doc["kind"] = Json::Value("user");
|
||||||
|
doc["methods"] = Json::objectValue;
|
||||||
|
|
||||||
auto constructorDefinition(_contractDef.constructor());
|
auto constructorDefinition(_contractDef.constructor());
|
||||||
if (constructorDefinition)
|
if (constructorDefinition)
|
||||||
@ -53,7 +52,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
// add the constructor, only if we have any documentation to add
|
// add the constructor, only if we have any documentation to add
|
||||||
Json::Value user;
|
Json::Value user;
|
||||||
user["notice"] = Json::Value(value);
|
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())
|
if (!value.empty())
|
||||||
{
|
|
||||||
Json::Value user;
|
|
||||||
// since @notice is the only user tag if missing function should not appear
|
// since @notice is the only user tag if missing function should not appear
|
||||||
user["notice"] = Json::Value(value);
|
doc["methods"][it.second->externalSignature()]["notice"] = value;
|
||||||
methods[it.second->externalSignature()] = user;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& event: _contractDef.interfaceEvents())
|
for (auto const& event: _contractDef.interfaceEvents())
|
||||||
{
|
{
|
||||||
string value = extractDoc(event->annotation().docTags, "notice");
|
string value = extractDoc(event->annotation().docTags, "notice");
|
||||||
if (!value.empty())
|
if (!value.empty())
|
||||||
events[event->functionType(true)->externalSignature()]["notice"] = value;
|
doc["events"][event->functionType(true)->externalSignature()]["notice"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc["methods"] = methods;
|
for (auto const& error: _contractDef.interfaceErrors())
|
||||||
if (!events.empty())
|
{
|
||||||
doc["events"] = events;
|
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;
|
return doc;
|
||||||
}
|
}
|
||||||
@ -100,7 +102,6 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||||
{
|
{
|
||||||
Json::Value doc = extractCustomDoc(_contractDef.annotation().docTags);
|
Json::Value doc = extractCustomDoc(_contractDef.annotation().docTags);
|
||||||
Json::Value methods(Json::objectValue);
|
|
||||||
|
|
||||||
doc["version"] = Json::Value(c_natspecVersion);
|
doc["version"] = Json::Value(c_natspecVersion);
|
||||||
doc["kind"] = Json::Value("dev");
|
doc["kind"] = Json::Value("dev");
|
||||||
@ -115,13 +116,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
|||||||
if (!dev.empty())
|
if (!dev.empty())
|
||||||
doc["details"] = Json::Value(dev);
|
doc["details"] = Json::Value(dev);
|
||||||
|
|
||||||
|
doc["methods"] = Json::objectValue;
|
||||||
auto constructorDefinition(_contractDef.constructor());
|
auto constructorDefinition(_contractDef.constructor());
|
||||||
if (constructorDefinition)
|
if (constructorDefinition)
|
||||||
{
|
{
|
||||||
Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
|
Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
|
||||||
if (!constructor.empty())
|
if (!constructor.empty())
|
||||||
// add the constructor, only if we have any documentation to add
|
// add the constructor, only if we have any documentation to add
|
||||||
methods["constructor"] = constructor;
|
doc["methods"]["constructor"] = constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& it: _contractDef.interfaceFunctions())
|
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);
|
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||||
|
|
||||||
if (!jsonReturn.empty())
|
if (!jsonReturn.empty())
|
||||||
method["returns"] = jsonReturn;
|
method["returns"] = move(jsonReturn);
|
||||||
|
|
||||||
if (!method.empty())
|
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())
|
for (VariableDeclaration const* varDecl: _contractDef.stateVariables())
|
||||||
{
|
{
|
||||||
if (auto devDoc = devDocumentation(varDecl->annotation().docTags); !devDoc.empty())
|
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, "");
|
solAssert(varDecl->annotation().docTags.count("return") <= 1, "");
|
||||||
if (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())
|
for (auto const& event: _contractDef.events())
|
||||||
if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty())
|
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;
|
for (auto const& error: _contractDef.interfaceErrors())
|
||||||
if (!stateVariables.empty())
|
if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty())
|
||||||
doc["stateVariables"] = stateVariables;
|
doc["errors"][error->functionType(true)->externalSignature()].append(devDoc);
|
||||||
if (!events.empty())
|
|
||||||
doc["events"] = events;
|
|
||||||
|
|
||||||
return doc;
|
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);
|
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)
|
BOOST_AUTO_TEST_CASE(custom)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
Loading…
Reference in New Issue
Block a user