diff --git a/Changelog.md b/Changelog.md index f0f8b8485..196fbf101 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,10 @@ ### 0.8.17 (unreleased) Important Bugfixes: + * ABI: Include events in the ABI that are emitted by a contract but defined outside of it. +AST Changes: + * Add field ``emittedEvents`` to ``ContractDefinition`` which contains the AST IDs of all inherited and emitted events. Language Features: diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 97167818c..b5d43df89 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -232,7 +232,7 @@ vector const& ContractDefinition::definedInterfaceEvents vector const ContractDefinition::usedInterfaceEvents() const { - solAssert(annotation().creationCallGraph.set(), ""); + solAssert(annotation().creationCallGraph.set()); return util::convertContainer>( (*annotation().creationCallGraph)->emittedEvents + @@ -240,14 +240,29 @@ vector const ContractDefinition::usedInterfaceEvents() c ); } +vector ContractDefinition::interfaceEvents(bool _requireCallGraph) const +{ + set result; + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) + result += contract->events(); + solAssert(annotation().creationCallGraph.set() == annotation().deployedCallGraph.set()); + if (_requireCallGraph) + solAssert(annotation().creationCallGraph.set()); + if (annotation().creationCallGraph.set()) + result += usedInterfaceEvents(); + // We could filter out all events that do not have an external interface + // if _requireCallGraph is false. + return util::convertContainer>(std::move(result)); +} + vector ContractDefinition::interfaceErrors(bool _requireCallGraph) const { set result; for (ContractDefinition const* contract: annotation().linearizedBaseContracts) result += filteredNodes(contract->m_subNodes); - solAssert(annotation().creationCallGraph.set() == annotation().deployedCallGraph.set(), ""); + solAssert(annotation().creationCallGraph.set() == annotation().deployedCallGraph.set()); if (_requireCallGraph) - solAssert(annotation().creationCallGraph.set(), ""); + solAssert(annotation().creationCallGraph.set()); if (annotation().creationCallGraph.set()) result += (*annotation().creationCallGraph)->usedErrors + diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d6e41bb5f..5dfec8e72 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -522,6 +522,10 @@ public: std::vector events() const { return filteredNodes(m_subNodes); } std::vector const& definedInterfaceEvents() const; std::vector const usedInterfaceEvents() const; + /// @return all events defined in this contract and its base contracts and all events + /// that are emitted during the execution of the contract. + /// @param _requireCallGraph if false, do not fail if the call graph has not been computed yet. + std::vector interfaceEvents(bool _requireCallGraph = true) const; /// @returns all errors defined in this contract or any base contract /// and all errors referenced during execution. /// @param _requireCallGraph if false, do not fail if the call graph has not been computed yet. diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index d308cd90c..9df01d50a 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -285,6 +285,8 @@ bool ASTJsonExporter::visit(ContractDefinition const& _node) make_pair("abstract", _node.abstract()), make_pair("baseContracts", toJson(_node.baseContracts())), make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies | ranges::views::keys)), + // Do not require call graph because the AST is also created for incorrect sources. + make_pair("emittedEvents", getContainerIds(_node.interfaceEvents(false))), make_pair("usedErrors", getContainerIds(_node.interfaceErrors(false))), make_pair("nodes", toJson(_node.subNodes())), make_pair("scope", idOrNull(_node.scope())) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index cbfa36122..bfc9c07a5 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -101,7 +101,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); abi.emplace(std::move(method)); } - for (auto const& it: _contractDef.definedInterfaceEvents()) + for (auto const& it: _contractDef.interfaceEvents()) { Json::Value event{Json::objectValue}; event["type"] = "event"; diff --git a/test/cmdlineTests/events_in_abi/args b/test/cmdlineTests/events_in_abi/args new file mode 100644 index 000000000..d27d3ef12 --- /dev/null +++ b/test/cmdlineTests/events_in_abi/args @@ -0,0 +1 @@ +--abi --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/events_in_abi/input.sol b/test/cmdlineTests/events_in_abi/input.sol new file mode 100644 index 000000000..3d73639e0 --- /dev/null +++ b/test/cmdlineTests/events_in_abi/input.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +library L { + event e1(uint b); +} + +function f() { + emit L.e1(5); +} + +contract C { + event e1(uint indexed a); + function g() public { + f(); + } +} diff --git a/test/cmdlineTests/events_in_abi/output b/test/cmdlineTests/events_in_abi/output new file mode 100644 index 000000000..75e3fe614 --- /dev/null +++ b/test/cmdlineTests/events_in_abi/output @@ -0,0 +1,8 @@ + +======= events_in_abi/input.sol:C ======= +Contract JSON ABI +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"b","type":"uint256"}],"name":"e1","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"a","type":"uint256"}],"name":"e1","type":"event"},{"inputs":[],"name":"g","outputs":[],"stateMutability":"nonpayable","type":"function"}] + +======= events_in_abi/input.sol:L ======= +Contract JSON ABI +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"b","type":"uint256"}],"name":"e1","type":"event"}] diff --git a/test/libsolidity/ABIJson/events_indirect.sol b/test/libsolidity/ABIJson/events_indirect.sol new file mode 100644 index 000000000..86368028c --- /dev/null +++ b/test/libsolidity/ABIJson/events_indirect.sol @@ -0,0 +1,88 @@ +library L { + event e1(uint b); + event e2(); + event e2(uint a); + event e3() anonymous; +} +contract test { + function f() public { + emit L.e1(1); + emit L.e3(); + } +} +// ---- +// :L +// [ +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": false, +// "internalType": "uint256", +// "name": "b", +// "type": "uint256" +// } +// ], +// "name": "e1", +// "type": "event" +// }, +// { +// "anonymous": false, +// "inputs": [], +// "name": "e2", +// "type": "event" +// }, +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": false, +// "internalType": "uint256", +// "name": "a", +// "type": "uint256" +// } +// ], +// "name": "e2", +// "type": "event" +// }, +// { +// "anonymous": true, +// "inputs": [], +// "name": "e3", +// "type": "event" +// } +// ] +// +// +// :test +// [ +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": false, +// "internalType": "uint256", +// "name": "b", +// "type": "uint256" +// } +// ], +// "name": "e1", +// "type": "event" +// }, +// { +// "anonymous": true, +// "inputs": [], +// "name": "e3", +// "type": "event" +// }, +// { +// "inputs": [], +// "name": "f", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// } +// ] diff --git a/test/libsolidity/ABIJson/events_repetitions.sol b/test/libsolidity/ABIJson/events_repetitions.sol new file mode 100644 index 000000000..8dd980a6a --- /dev/null +++ b/test/libsolidity/ABIJson/events_repetitions.sol @@ -0,0 +1,45 @@ +library L { + event e(); +} +contract C { + constructor() { + emit L.e(); + } + function f() public { + emit L.e(); + } +} + +// ---- +// :C +// [ +// { +// "inputs": [], +// "stateMutability": "nonpayable", +// "type": "constructor" +// }, +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// }, +// { +// "inputs": [], +// "name": "f", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// } +// ] +// +// +// :L +// [ +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// } +// ] diff --git a/test/libsolidity/ABIJson/same_event_defined_twice.sol b/test/libsolidity/ABIJson/same_event_defined_twice.sol new file mode 100644 index 000000000..9a65e61d6 --- /dev/null +++ b/test/libsolidity/ABIJson/same_event_defined_twice.sol @@ -0,0 +1,61 @@ +library L1 { event e(); } +library L2 { event e(); } +contract C { + constructor() { + emit L1.e(); + } + function f() public { + emit L2.e(); + } +} + +// ---- +// :C +// [ +// { +// "inputs": [], +// "stateMutability": "nonpayable", +// "type": "constructor" +// }, +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// }, +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// }, +// { +// "inputs": [], +// "name": "f", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// } +// ] +// +// +// :L1 +// [ +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// } +// ] +// +// +// :L2 +// [ +// { +// "anonymous": false, +// "inputs": [], +// "name": "e", +// "type": "event" +// } +// ] diff --git a/test/libsolidity/ASTJSON/indirect_event.json b/test/libsolidity/ASTJSON/indirect_event.json new file mode 100644 index 000000000..197744896 --- /dev/null +++ b/test/libsolidity/ASTJSON/indirect_event.json @@ -0,0 +1,313 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 25 + ], + "L": + [ + 10 + ] + }, + "id": 26, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "canonicalName": "L", + "contractDependencies": [], + "contractKind": "library", + "emittedEvents": + [ + 2 + ], + "fullyImplemented": true, + "id": 10, + "linearizedBaseContracts": + [ + 10 + ], + "name": "L", + "nameLocation": "44:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 2, + "name": "E", + "nameLocation": "58:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "59:2:1" + }, + "src": "52:10:1" + }, + { + "body": + { + "id": 8, + "nodeType": "Block", + "src": "89:13:1", + "statements": + [ + { + "eventCall": + { + "arguments": [], + "expression": + { + "argumentTypes": [], + "id": 5, + "name": "E", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2, + "src": "96:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_function_event_nonpayable$__$returns$__$", + "typeString": "function ()" + } + }, + "id": 6, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "96:3:1", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 7, + "nodeType": "EmitStatement", + "src": "91:8:1" + } + ] + }, + "id": 9, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "76:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": [], + "src": "77:2:1" + }, + "returnParameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": [], + "src": "89:0:1" + }, + "scope": 10, + "src": "67:35:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + } + ], + "scope": 26, + "src": "36:68:1", + "usedErrors": [] + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "emittedEvents": + [ + 2, + 12 + ], + "fullyImplemented": true, + "id": 25, + "linearizedBaseContracts": + [ + 25 + ], + "name": "C", + "nameLocation": "114:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 12, + "name": "H", + "nameLocation": "128:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "129:2:1" + }, + "src": "122:10:1" + }, + { + "body": + { + "id": 23, + "nodeType": "Block", + "src": "157:20:1", + "statements": + [ + { + "expression": + { + "arguments": [], + "expression": + { + "argumentTypes": [], + "expression": + { + "id": 15, + "name": "L", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 10, + "src": "159:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_type$_t_contract$_L_$10_$", + "typeString": "type(library L)" + } + }, + "id": 17, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "f", + "nodeType": "MemberAccess", + "referencedDeclaration": 9, + "src": "159:3:1", + "typeDescriptions": + { + "typeIdentifier": "t_function_internal_nonpayable$__$returns$__$", + "typeString": "function ()" + } + }, + "id": 18, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "159:5:1", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 19, + "nodeType": "ExpressionStatement", + "src": "159:5:1" + }, + { + "eventCall": + { + "arguments": [], + "expression": + { + "argumentTypes": [], + "id": 20, + "name": "H", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 12, + "src": "171:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_function_event_nonpayable$__$returns$__$", + "typeString": "function ()" + } + }, + "id": 21, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "171:3:1", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 22, + "nodeType": "EmitStatement", + "src": "166:8:1" + } + ] + }, + "functionSelector": "e2179b8e", + "id": 24, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nameLocation": "146:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 13, + "nodeType": "ParameterList", + "parameters": [], + "src": "147:2:1" + }, + "returnParameters": + { + "id": 14, + "nodeType": "ParameterList", + "parameters": [], + "src": "157:0:1" + }, + "scope": 25, + "src": "137:40:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 26, + "src": "105:74:1", + "usedErrors": [] + } + ], + "src": "36:144:1" +} diff --git a/test/libsolidity/ASTJSON/indirect_event.sol b/test/libsolidity/ASTJSON/indirect_event.sol new file mode 100644 index 000000000..c4bba01c2 --- /dev/null +++ b/test/libsolidity/ASTJSON/indirect_event.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0 +library L { + event E(); + function f() internal { emit E(); } +} +contract C { + event H(); + function g() public { L.f(); emit H(); } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/indirect_event_parseOnly.json b/test/libsolidity/ASTJSON/indirect_event_parseOnly.json new file mode 100644 index 000000000..e3eece5fd --- /dev/null +++ b/test/libsolidity/ASTJSON/indirect_event_parseOnly.json @@ -0,0 +1,224 @@ +{ + "absolutePath": "a", + "id": 26, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "library", + "emittedEvents": [], + "id": 10, + "name": "L", + "nameLocation": "44:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 2, + "name": "E", + "nameLocation": "58:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "59:2:1" + }, + "src": "52:10:1" + }, + { + "body": + { + "id": 8, + "nodeType": "Block", + "src": "89:13:1", + "statements": + [ + { + "eventCall": + { + "arguments": [], + "expression": + { + "id": 5, + "name": "E", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "96:1:1", + "typeDescriptions": {} + }, + "id": 6, + "names": [], + "nodeType": "FunctionCall", + "src": "96:3:1", + "tryCall": false, + "typeDescriptions": {} + }, + "id": 7, + "nodeType": "EmitStatement", + "src": "91:8:1" + } + ] + }, + "id": 9, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "76:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": [], + "src": "77:2:1" + }, + "returnParameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": [], + "src": "89:0:1" + }, + "src": "67:35:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + } + ], + "src": "36:68:1", + "usedErrors": [] + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "emittedEvents": [], + "id": 25, + "name": "C", + "nameLocation": "114:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 12, + "name": "H", + "nameLocation": "128:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "129:2:1" + }, + "src": "122:10:1" + }, + { + "body": + { + "id": 23, + "nodeType": "Block", + "src": "157:20:1", + "statements": + [ + { + "expression": + { + "arguments": [], + "expression": + { + "expression": + { + "id": 15, + "name": "L", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "159:1:1", + "typeDescriptions": {} + }, + "id": 17, + "memberName": "f", + "nodeType": "MemberAccess", + "src": "159:3:1", + "typeDescriptions": {} + }, + "id": 18, + "names": [], + "nodeType": "FunctionCall", + "src": "159:5:1", + "tryCall": false, + "typeDescriptions": {} + }, + "id": 19, + "nodeType": "ExpressionStatement", + "src": "159:5:1" + }, + { + "eventCall": + { + "arguments": [], + "expression": + { + "id": 20, + "name": "H", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "171:1:1", + "typeDescriptions": {} + }, + "id": 21, + "names": [], + "nodeType": "FunctionCall", + "src": "171:3:1", + "tryCall": false, + "typeDescriptions": {} + }, + "id": 22, + "nodeType": "EmitStatement", + "src": "166:8:1" + } + ] + }, + "id": 24, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nameLocation": "146:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 13, + "nodeType": "ParameterList", + "parameters": [], + "src": "147:2:1" + }, + "returnParameters": + { + "id": 14, + "nodeType": "ParameterList", + "parameters": [], + "src": "157:0:1" + }, + "src": "137:40:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "105:74:1", + "usedErrors": [] + } + ], + "src": "36:144:1" +} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 319a60885..4573da096 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -485,7 +485,8 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK_EQUAL( util::jsonCompactPrint(result["sources"]["fileA"]["ast"]), "{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"id\":2,\"nodeType\":\"SourceUnit\",\"nodes\":[{\"abstract\":false," - "\"baseContracts\":[],\"canonicalName\":\"A\",\"contractDependencies\":[],\"contractKind\":\"contract\",\"fullyImplemented\":true,\"id\":1," + "\"baseContracts\":[],\"canonicalName\":\"A\",\"contractDependencies\":[]," + "\"contractKind\":\"contract\",\"emittedEvents\":[],\"fullyImplemented\":true,\"id\":1," "\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nameLocation\":\"9:1:0\",\"nodeType\":\"ContractDefinition\",\"nodes\":[],\"scope\":2," "\"src\":\"0:14:0\",\"usedErrors\":[]}],\"src\":\"0:14:0\"}" );