Merge pull request #14434 from ethereum/fix-natspec-event-ice

Fix ICE when emitting event from another contract
This commit is contained in:
Kamil Śliwak 2023-07-21 23:18:37 +02:00 committed by GitHub
commit 548b033af6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 841 additions and 17 deletions

View File

@ -7,6 +7,7 @@ Compiler Features:
Bugfixes: 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) ### 0.8.21 (2023-07-19)

View File

@ -80,14 +80,6 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
for (auto const& event: uniqueInterfaceEvents(_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"); string value = extractDoc(event->annotation().docTags, "notice");
if (!value.empty()) if (!value.empty())
doc["events"][event->functionType(true)->externalSignature()]["notice"] = value; 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)) for (auto const& event: uniqueInterfaceEvents(_contractDef))
if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty()) 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; doc["events"][event->functionType(true)->externalSignature()] = devDoc;
}
for (auto const& error: _contractDef.interfaceErrors()) for (auto const& error: _contractDef.interfaceErrors())
if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty()) if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty())
doc["errors"][error->functionType(true)->externalSignature()].append(devDoc); doc["errors"][error->functionType(true)->externalSignature()].append(devDoc);

View File

@ -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"
// }
// ]

View File

@ -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"
}

View File

@ -0,0 +1,10 @@
contract C {
event E(address indexed sender);
}
contract D {
function test(address sender) public {
emit C.E(msg.sender);
}
}
// ----

View File

@ -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"
}

View File

@ -485,6 +485,179 @@ BOOST_AUTO_TEST_CASE(event)
checkNatspec(sourceCode, "ERC20", userDoc, true); 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) BOOST_AUTO_TEST_CASE(emit_same_signature_event_library_contract)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -0,0 +1,13 @@
contract C {
event E();
}
contract D {
function test() public {
emit C.E();
}
}
// ----
// test() ->
// ~ emit E()

View File

@ -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

View File

@ -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()

View File

@ -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