Allow event definitions at file level

This commit is contained in:
Kamil Śliwak 2023-09-07 16:13:10 +02:00
parent 020b59680e
commit b0a986ffff
30 changed files with 536 additions and 6 deletions

View File

@ -1,6 +1,7 @@
### 0.8.22 (unreleased) ### 0.8.22 (unreleased)
Language Features: Language Features:
* Allow defining events at file level.
Compiler Features: Compiler Features:

View File

@ -9,9 +9,10 @@ Events
Solidity events give an abstraction on top of the EVM's logging functionality. Solidity events give an abstraction on top of the EVM's logging functionality.
Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client.
Events are inheritable members of contracts. When you call them, they cause the Events can be defined at file level or as inheritable members of contracts (including interfaces and libraries).
When you call them, they cause the
arguments to be stored in the transaction's log - a special data structure arguments to be stored in the transaction's log - a special data structure
in the blockchain. These logs are associated with the address of the contract, in the blockchain. These logs are associated with the address of the contract that emitted them,
are incorporated into the blockchain, and stay there as long as a block is are incorporated into the blockchain, and stay there as long as a block is
accessible (forever as of now, but this might accessible (forever as of now, but this might
change with Serenity). The Log and its event data is not accessible from within change with Serenity). The Log and its event data is not accessible from within

View File

@ -22,6 +22,7 @@ sourceUnit: (
| enumDefinition | enumDefinition
| userDefinedValueTypeDefinition | userDefinedValueTypeDefinition
| errorDefinition | errorDefinition
| eventDefinition
)* EOF; )* EOF;
//@doc: inline //@doc: inline

View File

@ -112,11 +112,11 @@ Events are convenience interfaces with the EVM logging facilities.
.. code-block:: solidity .. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.9.0; pragma solidity ^0.8.22;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event event HighestBidIncreased(address bidder, uint amount); // Event
contract SimpleAuction {
function bid() public payable { function bid() public payable {
// ... // ...
emit HighestBidIncreased(msg.sender, msg.value); // Triggering event emit HighestBidIncreased(msg.sender, msg.value); // Triggering event

View File

@ -131,6 +131,9 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
case Token::Function: case Token::Function:
nodes.push_back(parseFunctionDefinition(true)); nodes.push_back(parseFunctionDefinition(true));
break; break;
case Token::Event:
nodes.push_back(parseEventDefinition());
break;
default: default:
if ( if (
// Workaround because `error` is not a keyword. // Workaround because `error` is not a keyword.

View File

@ -0,0 +1,107 @@
event Event();
event Event(uint);
event UnusedEvent();
function f() {
emit Event();
}
contract C {
function c_main() public {
emit Event(42);
f();
}
}
contract D is C {
event Event(string);
function d_main() public {
emit Event("abc");
}
}
// ----
// :C
// [
// {
// "anonymous": false,
// "inputs": [],
// "name": "Event",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": false,
// "internalType": "uint256",
// "name": "",
// "type": "uint256"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "inputs": [],
// "name": "c_main",
// "outputs": [],
// "stateMutability": "nonpayable",
// "type": "function"
// }
// ]
//
//
// :D
// [
// {
// "anonymous": false,
// "inputs": [],
// "name": "Event",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": false,
// "internalType": "uint256",
// "name": "",
// "type": "uint256"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": false,
// "internalType": "string",
// "name": "",
// "type": "string"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "inputs": [],
// "name": "c_main",
// "outputs": [],
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "inputs": [],
// "name": "d_main",
// "outputs": [],
// "stateMutability": "nonpayable",
// "type": "function"
// }
// ]

View File

@ -274,7 +274,7 @@ std::optional<AnnotatedEventSignature> SemanticTest::matchEvent(util::h256 const
for (std::string& contractName: m_compiler.contractNames()) for (std::string& contractName: m_compiler.contractNames())
{ {
ContractDefinition const& contract = m_compiler.contractDefinition(contractName); ContractDefinition const& contract = m_compiler.contractDefinition(contractName);
for (EventDefinition const* event: contract.events()) for (EventDefinition const* event: contract.events() + contract.usedInterfaceEvents())
{ {
FunctionTypePointer eventFunctionType = event->functionType(true); FunctionTypePointer eventFunctionType = event->functionType(true);
if (!event->isAnonymous() && keccak256(eventFunctionType->externalSignature()) == hash) if (!event->isAnonymous() && keccak256(eventFunctionType->externalSignature()) == hash)

View File

@ -0,0 +1,39 @@
/// @notice Userdoc for file-level event E.
/// @dev Devdoc for file-level E.
event E();
contract C {
function f() public {
emit E();
}
}
// ----
// ----
// :C devdoc
// {
// "events":
// {
// "E()":
// {
// "details": "Devdoc for file-level E."
// }
// },
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :C userdoc
// {
// "events":
// {
// "E()":
// {
// "notice": "Userdoc for file-level event E."
// }
// },
// "kind": "user",
// "methods": {},
// "version": 1
// }

View File

@ -0,0 +1,69 @@
/// @notice Userdoc for file-level event E.
/// @dev Devdoc for file-level E.
event E();
contract C {
function f() public {
emit E();
}
}
contract D is C {}
// ----
// ----
// :C devdoc
// {
// "events":
// {
// "E()":
// {
// "details": "Devdoc for file-level E."
// }
// },
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :C userdoc
// {
// "events":
// {
// "E()":
// {
// "notice": "Userdoc for file-level event E."
// }
// },
// "kind": "user",
// "methods": {},
// "version": 1
// }
//
// :D devdoc
// {
// "events":
// {
// "E()":
// {
// "details": "Devdoc for file-level E."
// }
// },
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :D userdoc
// {
// "events":
// {
// "E()":
// {
// "notice": "Userdoc for file-level event E."
// }
// },
// "kind": "user",
// "methods": {},
// "version": 1
// }

View File

@ -0,0 +1,60 @@
/// @notice Userdoc for file-level event E.
/// @dev Devdoc for file-level E.
event E();
contract F {
/// @notice Userdoc for event F.E.
/// @dev Devdoc for event F.E.
event E();
}
contract C {
function f() public {
emit E();
emit F.E();
}
}
// ----
// ----
// :C devdoc
// {
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :C userdoc
// {
// "kind": "user",
// "methods": {},
// "version": 1
// }
//
// :F devdoc
// {
// "events":
// {
// "E()":
// {
// "details": "Devdoc for event F.E."
// }
// },
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :F userdoc
// {
// "events":
// {
// "E()":
// {
// "notice": "Userdoc for event F.E."
// }
// },
// "kind": "user",
// "methods": {},
// "version": 1
// }

View File

@ -0,0 +1,21 @@
/// @notice Userdoc for file-level event E.
/// @dev Devdoc for file-level E.
event E();
contract C {}
// ----
// ----
// :C devdoc
// {
// "kind": "dev",
// "methods": {},
// "version": 1
// }
//
// :C userdoc
// {
// "kind": "user",
// "methods": {},
// "version": 1
// }

View File

@ -0,0 +1,10 @@
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
contract ClientReceipt {
function deposit(bytes32 _id) public payable {
emit Deposit(msg.sender, _id, msg.value);
}
}
// ----
// deposit(bytes32), 18 wei: 0x1234 ->
// ~ emit Deposit(address,bytes32,uint256): #0x1212121212121212121212121212120000000012, #0x1234, 0x12

View File

@ -0,0 +1,15 @@
event E();
library L {
event E();
}
contract C {
function main() external pure returns (bytes32, bytes32) {
assert(E.selector == L.E.selector);
return (E.selector, L.E.selector);
}
}
// ----
// main() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028

View File

@ -0,0 +1,38 @@
event E();
library L1 {
event E(string);
}
library L2 {
event E();
}
library K {
function main() internal pure returns (bytes32, bytes32, bytes32) {
// Here E is the global event.
assert(E.selector != L1.E.selector);
assert(E.selector == L2.E.selector);
return (E.selector, L1.E.selector, L2.E.selector);
}
}
contract C {
event E(string);
function main() external pure returns (bytes32, bytes32, bytes32) {
// Here E is the local event.
assert(E.selector == L1.E.selector);
assert(E.selector != L2.E.selector);
return (E.selector, L1.E.selector, L2.E.selector);
}
function k_main() external pure returns (bytes32, bytes32, bytes32) {
return K.main();
}
}
// ----
// main() -> 0x3e9992c940c54ea252d3a34557cc3d3014281525c43d694f89d5f3dfd820b07d, 0x3e9992c940c54ea252d3a34557cc3d3014281525c43d694f89d5f3dfd820b07d, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028
// k_main() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x3e9992c940c54ea252d3a34557cc3d3014281525c43d694f89d5f3dfd820b07d, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028

View File

@ -0,0 +1,32 @@
event Deposit();
event Deposit(address _addr);
event Deposit(address _addr, uint _amount);
event Deposit(address _addr, bool _flag);
contract ClientReceipt {
function deposit() public returns (uint) {
emit Deposit();
return 1;
}
function deposit(address _addr) public returns (uint) {
emit Deposit(_addr);
return 2;
}
function deposit(address _addr, uint _amount) public returns (uint) {
emit Deposit(_addr, _amount);
return 3;
}
function deposit(address _addr, bool _flag) public returns (uint) {
emit Deposit(_addr, _flag);
return 4;
}
}
// ----
// deposit() -> 1
// ~ emit Deposit()
// deposit(address): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988 -> 2
// ~ emit Deposit(address): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988
// deposit(address,uint256): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988, 100 -> 3
// ~ emit Deposit(address,uint256): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988, 0x64
// deposit(address,bool): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988, false -> 4
// ~ emit Deposit(address,bool): 0x5082a85c489be6aa0f2e6693bf09cc1bbd35e988, false

View File

@ -0,0 +1,8 @@
==== Source: M.sol ====
event E();
==== Source: A.sol ====
import "M.sol" as M;
function f() {
emit M.E();
}

View File

@ -0,0 +1,7 @@
event E();
contract C {
function f() public {
emit E();
}
}

View File

@ -0,0 +1,7 @@
event E();
contract C {
function f() external pure returns (bytes32) {
return E.selector;
}
}

View File

@ -0,0 +1,11 @@
event E1();
event E2(uint);
event E3(uint, string indexed, bytes, bool);
event E4(int, int, int) anonymous;
function f() {
emit E1();
emit E2(1);
emit E3(1, "abc", "abc", true);
emit E4(1, 2, 3);
}

View File

@ -0,0 +1,4 @@
event E();
event E();
// ----
// DeclarationError 5883: (0-10): Event with same name and parameter types defined twice.

View File

@ -0,0 +1,8 @@
==== Source: A.sol ====
event E();
==== Source: B.sol ====
import "A.sol";
event E();
// ----
// DeclarationError 5883: (B.sol:17-27): Event with same name and parameter types defined twice.

View File

@ -0,0 +1,12 @@
==== Source: A.sol ====
event EA();
==== Source: B.sol ====
event EB();
==== Source: C.sol ====
import "A.sol";
import {EB} from "B.sol";
function f() {
emit EA();
emit EB();
}

View File

@ -0,0 +1,11 @@
event E();
event E(uint);
event E(uint, string indexed, bytes, bool);
event E(int, int, int) anonymous;
function f() {
emit E();
emit E(1);
emit E(1, "abc", "abc", true);
emit E(1, 2, 3);
}

View File

@ -0,0 +1,13 @@
event E();
contract C {
event E(uint);
function f() public {
emit E();
emit E(1);
}
}
// ----
// Warning 2519: (29-43): This declaration shadows an existing declaration.
// TypeError 6160: (84-87): Wrong argument count for function call: 0 arguments given but expected 1.

View File

@ -0,0 +1,17 @@
event E();
contract C {
event E();
}
library L {
event E();
}
interface I {
event E();
}
// ----
// Warning 2519: (29-39): This declaration shadows an existing declaration.
// Warning 2519: (59-69): This declaration shadows an existing declaration.
// Warning 2519: (91-101): This declaration shadows an existing declaration.

View File

@ -0,0 +1,17 @@
event E();
contract C {
event E() anonymous;
}
library L {
event E() anonymous;
}
interface I {
event E() anonymous;
}
// ----
// Warning 2519: (29-49): This declaration shadows an existing declaration.
// Warning 2519: (69-89): This declaration shadows an existing declaration.
// Warning 2519: (111-131): This declaration shadows an existing declaration.

View File

@ -0,0 +1,4 @@
event E();
contract E {}
// ----
// DeclarationError 2333: (11-24): Identifier already declared.

View File

@ -0,0 +1,4 @@
event E();
error E();
// ----
// DeclarationError 2333: (11-21): Identifier already declared.

View File

@ -0,0 +1,6 @@
event E();
type T is uint;
using {E} for T;
// ----
// TypeError 8187: (35-36): Expected function name.

View File

@ -0,0 +1,4 @@
// Exception for the illegal name list. External interface events
event this();
event super();
event _();