From 964bdc711ccd81253251ebc0af4fb1919de5681e Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 20 Jul 2023 13:28:03 +0200 Subject: [PATCH] Fix ICE when emitting event from another contract --- Changelog.md | 1 + libsolidity/interface/Natspec.cpp | 17 -- .../event_emitted_from_foreign_contract.sol | 112 +++++++ .../event_emitted_from_foreign_contract.json | 289 ++++++++++++++++++ .../event_emitted_from_foreign_contract.sol | 10 + ...itted_from_foreign_contract_parseOnly.json | 194 ++++++++++++ test/libsolidity/SolidityNatspecJSON.cpp | 173 +++++++++++ .../event_emit_from_a_foreign_contract.sol | 13 + ...emit_from_a_foreign_contract_same_name.sol | 17 ++ ...event_emit_interface_event_via_library.sol | 19 ++ .../events/event_emit_via_interface.sol | 13 + 11 files changed, 841 insertions(+), 17 deletions(-) create mode 100644 test/libsolidity/ABIJson/event_emitted_from_foreign_contract.sol create mode 100644 test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.json create mode 100644 test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.sol create mode 100644 test/libsolidity/ASTJSON/event_emitted_from_foreign_contract_parseOnly.json create mode 100644 test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract.sol create mode 100644 test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract_same_name.sol create mode 100644 test/libsolidity/semanticTests/events/event_emit_interface_event_via_library.sol create mode 100644 test/libsolidity/semanticTests/events/event_emit_via_interface.sol diff --git a/Changelog.md b/Changelog.md index 4205e7416..38a8aae41 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface. ### 0.8.21 (2023-07-19) diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index b192dfda1..5733d3063 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -80,14 +80,6 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) for (auto const& event: uniqueInterfaceEvents(_contractDef)) { - ContractDefinition const* eventOrigin = event->annotation().contract; - solAssert(eventOrigin); - solAssert( - *eventOrigin == _contractDef || - (!eventOrigin->isLibrary() && _contractDef.derivesFrom(*eventOrigin)) || - (eventOrigin->isLibrary() && !_contractDef.derivesFrom(*eventOrigin)) - ); - string value = extractDoc(event->annotation().docTags, "notice"); if (!value.empty()) doc["events"][event->functionType(true)->externalSignature()]["notice"] = value; @@ -178,16 +170,7 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) for (auto const& event: uniqueInterfaceEvents(_contractDef)) if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty()) - { - ContractDefinition const* eventOrigin = event->annotation().contract; - solAssert(eventOrigin); - solAssert( - *eventOrigin == _contractDef || - (!eventOrigin->isLibrary() && _contractDef.derivesFrom(*eventOrigin)) || - (eventOrigin->isLibrary() && !_contractDef.derivesFrom(*eventOrigin)) - ); doc["events"][event->functionType(true)->externalSignature()] = devDoc; - } for (auto const& error: _contractDef.interfaceErrors()) if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty()) doc["errors"][error->functionType(true)->externalSignature()].append(devDoc); diff --git a/test/libsolidity/ABIJson/event_emitted_from_foreign_contract.sol b/test/libsolidity/ABIJson/event_emitted_from_foreign_contract.sol new file mode 100644 index 000000000..30c115669 --- /dev/null +++ b/test/libsolidity/ABIJson/event_emitted_from_foreign_contract.sol @@ -0,0 +1,112 @@ +interface I { + event Event(uint256 value); +} +contract C { + event Event(address indexed sender); +} +contract D { + event Event(address indexed sender); + function test(address sender) public { + emit I.Event(1); + emit C.Event(msg.sender); + emit Event(msg.sender); + } +} +// ---- +// :C +// [ +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": true, +// "internalType": "address", +// "name": "sender", +// "type": "address" +// } +// ], +// "name": "Event", +// "type": "event" +// } +// ] +// +// +// :D +// [ +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": false, +// "internalType": "uint256", +// "name": "value", +// "type": "uint256" +// } +// ], +// "name": "Event", +// "type": "event" +// }, +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": true, +// "internalType": "address", +// "name": "sender", +// "type": "address" +// } +// ], +// "name": "Event", +// "type": "event" +// }, +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": true, +// "internalType": "address", +// "name": "sender", +// "type": "address" +// } +// ], +// "name": "Event", +// "type": "event" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "address", +// "name": "sender", +// "type": "address" +// } +// ], +// "name": "test", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// } +// ] +// +// +// :I +// [ +// { +// "anonymous": false, +// "inputs": +// [ +// { +// "indexed": false, +// "internalType": "uint256", +// "name": "value", +// "type": "uint256" +// } +// ], +// "name": "Event", +// "type": "event" +// } +// ] diff --git a/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.json b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.json new file mode 100644 index 000000000..2d70e2f9f --- /dev/null +++ b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.json @@ -0,0 +1,289 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ], + "D": + [ + 19 + ] + }, + "id": 20, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nameLocation": "9:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "eventSelector": "bd1155618a34fd53d1a2de9f705f42f3582842cba0b985b25c59888d86e0c929", + "id": 4, + "name": "E", + "nameLocation": "23:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "indexed": true, + "mutability": "mutable", + "name": "sender", + "nameLocation": "41:6:1", + "nodeType": "VariableDeclaration", + "scope": 4, + "src": "25:22:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "25:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "24:24:1" + }, + "src": "17:32:1" + } + ], + "scope": 20, + "src": "0:51:1", + "usedErrors": [], + "usedEvents": + [ + 4 + ] + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "D", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 19, + "linearizedBaseContracts": + [ + 19 + ], + "name": "D", + "nameLocation": "61:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 17, + "nodeType": "Block", + "src": "106:37:1", + "statements": + [ + { + "eventCall": + { + "arguments": + [ + { + "expression": + { + "id": 13, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "125:3:1", + "typeDescriptions": + { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 14, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "129:6:1", + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "125:10:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + ], + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "expression": + { + "id": 10, + "name": "C", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 5, + "src": "121:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_type$_t_contract$_C_$5_$", + "typeString": "type(contract C)" + } + }, + "id": 12, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "123:1:1", + "memberName": "E", + "nodeType": "MemberAccess", + "referencedDeclaration": 4, + "src": "121:3:1", + "typeDescriptions": + { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$returns$__$", + "typeString": "function (address)" + } + }, + "id": 15, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "121:15:1", + "tryCall": false, + "typeDescriptions": + { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 16, + "nodeType": "EmitStatement", + "src": "116:20:1" + } + ] + }, + "functionSelector": "bb29998e", + "id": 18, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "test", + "nameLocation": "78:4:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 7, + "mutability": "mutable", + "name": "sender", + "nameLocation": "91:6:1", + "nodeType": "VariableDeclaration", + "scope": 18, + "src": "83:14:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": + { + "id": 6, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "83:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "82:16:1" + }, + "returnParameters": + { + "id": 9, + "nodeType": "ParameterList", + "parameters": [], + "src": "106:0:1" + }, + "scope": 19, + "src": "69:74:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 20, + "src": "52:93:1", + "usedErrors": [], + "usedEvents": + [ + 4 + ] + } + ], + "src": "0:146:1" +} diff --git a/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.sol b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.sol new file mode 100644 index 000000000..6cad968f6 --- /dev/null +++ b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract.sol @@ -0,0 +1,10 @@ +contract C { + event E(address indexed sender); +} +contract D { + function test(address sender) public { + emit C.E(msg.sender); + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract_parseOnly.json b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract_parseOnly.json new file mode 100644 index 000000000..b6c0ca5e0 --- /dev/null +++ b/test/libsolidity/ASTJSON/event_emitted_from_foreign_contract_parseOnly.json @@ -0,0 +1,194 @@ +{ + "absolutePath": "a", + "id": 20, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nameLocation": "9:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 4, + "name": "E", + "nameLocation": "23:1:1", + "nodeType": "EventDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "indexed": true, + "mutability": "mutable", + "name": "sender", + "nameLocation": "41:6:1", + "nodeType": "VariableDeclaration", + "src": "25:22:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "25:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "24:24:1" + }, + "src": "17:32:1" + } + ], + "src": "0:51:1", + "usedErrors": [], + "usedEvents": [] + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 19, + "name": "D", + "nameLocation": "61:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 17, + "nodeType": "Block", + "src": "106:37:1", + "statements": + [ + { + "eventCall": + { + "arguments": + [ + { + "expression": + { + "id": 13, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "125:3:1", + "typeDescriptions": {} + }, + "id": 14, + "memberLocation": "129:6:1", + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "125:10:1", + "typeDescriptions": {} + } + ], + "expression": + { + "expression": + { + "id": 10, + "name": "C", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "121:1:1", + "typeDescriptions": {} + }, + "id": 12, + "memberLocation": "123:1:1", + "memberName": "E", + "nodeType": "MemberAccess", + "src": "121:3:1", + "typeDescriptions": {} + }, + "id": 15, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "121:15:1", + "tryCall": false, + "typeDescriptions": {} + }, + "id": 16, + "nodeType": "EmitStatement", + "src": "116:20:1" + } + ] + }, + "id": 18, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "test", + "nameLocation": "78:4:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 7, + "mutability": "mutable", + "name": "sender", + "nameLocation": "91:6:1", + "nodeType": "VariableDeclaration", + "src": "83:14:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 6, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "83:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "82:16:1" + }, + "returnParameters": + { + "id": 9, + "nodeType": "ParameterList", + "parameters": [], + "src": "106:0:1" + }, + "src": "69:74:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "52:93:1", + "usedErrors": [], + "usedEvents": [] + } + ], + "src": "0:146:1" +} diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index a54460eb6..9ecd35aef 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -485,6 +485,179 @@ BOOST_AUTO_TEST_CASE(event) checkNatspec(sourceCode, "ERC20", userDoc, true); } +BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract) +{ + char const* sourceCode = R"( + contract X { + /// @notice Userdoc for event E. + /// @dev Devdoc for event E. + event E(); + } + + contract C { + function g() public { + emit X.E(); + } + } + )"; + + char const* devDoc = R"ABCDEF( + { + "events": + { + "E()": + { + "details": "Devdoc for event E." + } + }, + "kind": "dev", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "C", devDoc, false); + + char const* userDoc = R"ABCDEF( + { + "events": + { + "E()": + { + "notice": "Userdoc for event E." + } + }, + "kind": "user", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "C", userDoc, true); +} + +BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract_with_same_signature) +{ + char const* sourceCode = R"( + contract C { + /// @notice C.E event + /// @dev C.E event + event E(uint256 value); + } + + contract D { + /// @notice D.E event + /// @dev D.E event + event E(uint256 value); + + function test() public { + emit C.E(1); + emit E(2); + } + } + )"; + + char const* devDocC = R"ABCDEF( + { + "events": + { + "E(uint256)": + { + "details": "C.E event" + } + }, + "kind": "dev", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "C", devDocC, false); + + char const* devDocD = R"ABCDEF( + { + "events": + { + "E(uint256)": + { + "details": "D.E event" + } + }, + "kind": "dev", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "D", devDocD, false); + + char const* userDocC = R"ABCDEF( + { + "events": + { + "E(uint256)": + { + "notice": "C.E event" + } + }, + "kind": "user", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "C", userDocC, true); + + char const* userDocD = R"ABCDEF( + { + "events": + { + "E(uint256)": + { + "notice": "D.E event" + } + }, + "kind": "user", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "D", userDocD, true); +} + +// Tests that emitting an event from contract C in contract D does not inherit natspec from C.E +BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract_no_inheritance) +{ + char const* sourceCode = R"( + contract C { + /// @notice C.E event + /// @dev C.E event + event E(); + } + + contract D { + event E(); + + function test() public { + emit C.E(); + } + } + )"; + + char const* devDoc = R"ABCDEF( + { + "kind": "dev", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "D", devDoc, false); + + char const* userDoc = R"ABCDEF( + { + "kind": "user", + "methods": {}, + "version": 1 + } + )ABCDEF"; + checkNatspec(sourceCode, "D", userDoc, true); +} + BOOST_AUTO_TEST_CASE(emit_same_signature_event_library_contract) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract.sol b/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract.sol new file mode 100644 index 000000000..fcd7b6f4c --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract.sol @@ -0,0 +1,13 @@ +contract C { + event E(); +} + +contract D { + function test() public { + emit C.E(); + } +} + +// ---- +// test() -> +// ~ emit E() diff --git a/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract_same_name.sol b/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract_same_name.sol new file mode 100644 index 000000000..05c03c20f --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_emit_from_a_foreign_contract_same_name.sol @@ -0,0 +1,17 @@ +contract C { + event E(uint256 value); +} + +contract D { + event E(uint256 value); + + function test() public { + emit C.E(1); + emit E(2); + } +} + +// ---- +// test() -> +// ~ emit E(uint256): 0x01 +// ~ emit E(uint256): 0x02 diff --git a/test/libsolidity/semanticTests/events/event_emit_interface_event_via_library.sol b/test/libsolidity/semanticTests/events/event_emit_interface_event_via_library.sol new file mode 100644 index 000000000..9e24287ab --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_emit_interface_event_via_library.sol @@ -0,0 +1,19 @@ +interface I { + event E(); +} + +library L { + function f() internal { + emit I.E(); + } +} + +contract C { + function g() public { + L.f(); + } +} + +// ---- +// g() -> +// ~ emit E() diff --git a/test/libsolidity/semanticTests/events/event_emit_via_interface.sol b/test/libsolidity/semanticTests/events/event_emit_via_interface.sol new file mode 100644 index 000000000..7268e9ac5 --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_emit_via_interface.sol @@ -0,0 +1,13 @@ +interface I { + event Event(address indexed _from, uint256 _value); +} + +contract C { + function emitEvent(uint256 _value) public { + emit I.Event(msg.sender, _value); + } +} + +// ---- +// emitEvent(uint256): 100 -> +// ~ emit Event(address,uint256): #0x1212121212121212121212121212120000000012, 0x64