feat: allow named parameters in mapping types

Co-authored-by: Hari <webmail.hari@gmail.com>

test: add parser and abi test cases

docs: add example on using named parameters for mappings

- Add changelog

feat: update antlr grammar to allow named parameters in mappings

fix: prevent conflicting mapping parameter names

ref: change order of mapping initializers

test: update expectations and fix build

test: add more tests

fix: use common error & code for conflicting params

fix: issue with accessing nested mapping

test: add conflicting params tests for more nested levels

Update libsolidity/analysis/DeclarationTypeChecker.cpp

Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>

fix: error reported with the same code twice

test: add more tests for 3 level nested mapping

Address review comments
This commit is contained in:
Soham Zemse
2022-12-08 11:56:58 +01:00
committed by Nikola Matic
co-authored by Hari Nikola Matić
parent 1c8745c54a
commit fa78e0f3d4
75 changed files with 530 additions and 30 deletions
+77
View File
@@ -0,0 +1,77 @@
contract test {
mapping(address owner => mapping(address spender => uint value)) public allowance;
mapping(bytes32 => address sender) public commits;
mapping(bytes32 => bytes32) public something;
}
// ----
// :test
// [
// {
// "inputs":
// [
// {
// "internalType": "address",
// "name": "owner",
// "type": "address"
// },
// {
// "internalType": "address",
// "name": "spender",
// "type": "address"
// }
// ],
// "name": "allowance",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "value",
// "type": "uint256"
// }
// ],
// "stateMutability": "view",
// "type": "function"
// },
// {
// "inputs":
// [
// {
// "internalType": "bytes32",
// "name": "",
// "type": "bytes32"
// }
// ],
// "name": "commits",
// "outputs":
// [
// {
// "internalType": "address",
// "name": "sender",
// "type": "address"
// }
// ],
// "stateMutability": "view",
// "type": "function"
// },
// {
// "inputs":
// [
// {
// "internalType": "bytes32",
// "name": "",
// "type": "bytes32"
// }
// ],
// "name": "something",
// "outputs":
// [
// {
// "internalType": "bytes32",
// "name": "",
// "type": "bytes32"
// }
// ],
// "stateMutability": "view",
// "type": "function"
// }
// ]
@@ -48,6 +48,7 @@
"typeName":
{
"id": 3,
"keyName": "",
"keyType":
{
"id": 1,
@@ -67,6 +68,7 @@
"typeIdentifier": "t_mapping$_t_address_$_t_address_payable_$",
"typeString": "mapping(address => address payable)"
},
"valueName": "",
"valueType":
{
"id": 2,
@@ -29,6 +29,7 @@
"typeName":
{
"id": 3,
"keyName": "",
"keyType":
{
"id": 1,
@@ -40,6 +41,7 @@
"nodeType": "Mapping",
"src": "17:35:1",
"typeDescriptions": {},
"valueName": "",
"valueType":
{
"id": 2,
+6
View File
@@ -79,6 +79,7 @@
"typeName":
{
"id": 8,
"keyName": "",
"keyType":
{
"id": 6,
@@ -110,6 +111,7 @@
"typeIdentifier": "t_mapping$_t_contract$_C_$19_$_t_bool_$",
"typeString": "mapping(contract C => bool)"
},
"valueName": "",
"valueType":
{
"id": 7,
@@ -144,6 +146,7 @@
"typeName":
{
"id": 12,
"keyName": "",
"keyType":
{
"id": 10,
@@ -163,6 +166,7 @@
"typeIdentifier": "t_mapping$_t_address_$_t_bool_$",
"typeString": "mapping(address => bool)"
},
"valueName": "",
"valueType":
{
"id": 11,
@@ -197,6 +201,7 @@
"typeName":
{
"id": 17,
"keyName": "",
"keyType":
{
"id": 15,
@@ -228,6 +233,7 @@
"typeIdentifier": "t_mapping$_t_enum$_E_$4_$_t_bool_$",
"typeString": "mapping(enum C.E => bool)"
},
"valueName": "",
"valueType":
{
"id": 16,
@@ -60,6 +60,7 @@
"typeName":
{
"id": 8,
"keyName": "",
"keyType":
{
"id": 6,
@@ -81,6 +82,7 @@
"nodeType": "Mapping",
"src": "40:18:1",
"typeDescriptions": {},
"valueName": "",
"valueType":
{
"id": 7,
@@ -106,6 +108,7 @@
"typeName":
{
"id": 12,
"keyName": "",
"keyType":
{
"id": 10,
@@ -117,6 +120,7 @@
"nodeType": "Mapping",
"src": "66:24:1",
"typeDescriptions": {},
"valueName": "",
"valueType":
{
"id": 11,
@@ -142,6 +146,7 @@
"typeName":
{
"id": 17,
"keyName": "",
"keyType":
{
"id": 15,
@@ -163,6 +168,7 @@
"nodeType": "Mapping",
"src": "98:18:1",
"typeDescriptions": {},
"valueName": "",
"valueType":
{
"id": 16,
@@ -287,6 +287,7 @@
"typeName":
{
"id": 25,
"keyName": "",
"keyType":
{
"id": 22,
@@ -318,6 +319,7 @@
"typeIdentifier": "t_mapping$_t_userDefinedValueType$_MyAddress_$18_$_t_userDefinedValueType$_MyUInt_$20_$",
"typeString": "mapping(C.MyAddress => C.MyUInt)"
},
"valueName": "",
"valueType":
{
"id": 24,
@@ -213,6 +213,7 @@
"typeName":
{
"id": 25,
"keyName": "",
"keyType":
{
"id": 22,
@@ -234,6 +235,7 @@
"nodeType": "Mapping",
"src": "169:28:1",
"typeDescriptions": {},
"valueName": "",
"valueType":
{
"id": 24,
+8 -4
View File
@@ -97,12 +97,16 @@ BOOST_AUTO_TEST_CASE(storage_layout_mapping)
{"first", TypeProvider::fromElementaryTypeName("uint128")},
{"second", TypeProvider::mapping(
TypeProvider::fromElementaryTypeName("uint8"),
TypeProvider::fromElementaryTypeName("uint8")
"",
TypeProvider::fromElementaryTypeName("uint8"),
""
)},
{"third", TypeProvider::fromElementaryTypeName("uint16")},
{"final", TypeProvider::mapping(
TypeProvider::fromElementaryTypeName("uint8"),
TypeProvider::fromElementaryTypeName("uint8")
"",
TypeProvider::fromElementaryTypeName("uint8"),
""
)},
}));
BOOST_REQUIRE_EQUAL(u256(4), members.storageSize());
@@ -199,8 +203,8 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
FunctionType metaFun(TypePointers{keccak256fun}, TypePointers{s.type()}, strings{""}, strings{""});
BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_nonpayable$_t_function_keccak256_nonpayable$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$");
Type const* m = TypeProvider::mapping(TypeProvider::fromElementaryTypeName("bytes32"), s.type());
MappingType m2(TypeProvider::fromElementaryTypeName("uint64"), m);
Type const* m = TypeProvider::mapping(TypeProvider::fromElementaryTypeName("bytes32"), "", s.type(), "");
MappingType m2(TypeProvider::fromElementaryTypeName("uint64"), "", m, "");
BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping$_t_uint64_$_t_mapping$_t_bytes32_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_$");
// TypeType is tested with contract
@@ -10,4 +10,6 @@ contract Error2 {
mapping (address => uint balances; // missing ) before "balances"
}
// ----
// ParserError 6635: (417-425): Expected ')' but got identifier
// ParserError 6635: (425-426): Expected ')' but got ';'
// ParserError 6635: (425-426): Expected identifier but got ';'
// ParserError 6635: (458-459): Expected ';' but got '}'
@@ -17,7 +17,8 @@ contract SendCoin {
}
// ----
// ParserError 6635: (212-220): Expected ')' but got identifier
// ParserError 6635: (220-221): Expected ';' but got ')'
// ParserError 9182: (220-221): Function, variable, struct or modifier declaration expected.
// Warning 3796: (235-236): Recovered in ContractDefinition at '}'.
// ParserError 6635: (235-236): Expected identifier but got '}'
// ParserError 6635: (276-284): Expected ';' but got 'contract'
// ParserError 9182: (276-284): Function, variable, struct or modifier declaration expected.
// Warning 3796: (572-573): Recovered in ContractDefinition at '}'.
// ParserError 7858: (574-575): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition.
@@ -0,0 +1,9 @@
contract C {
mapping(uint a => mapping(uint b => uint c)) public x;
constructor() {
x[1][2] = 3;
}
}
// ----
// x(uint256,uint256): 1, 2 -> 3
// x(uint256,uint256): 0, 0 -> 0
@@ -13,3 +13,4 @@ contract Thing is IThing {
mapping(uint256=>Value) public override value;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address user => bytes32 ipfs) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address => bytes32 ipfs) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address user => bytes32) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address owner => mapping(address spender => bytes32[] notes)) names;
}
// ----
@@ -0,0 +1,5 @@
contract test {
mapping(address uint => bytes32 ipfs) names;
}
// ----
// ParserError 2314: (36-40): Expected '=>' but got 'uint'
@@ -0,0 +1,4 @@
contract test {
mapping(address owner => bytes32[] note) notes;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address => mapping(address => address) hello) world;
}
// ----
@@ -0,0 +1,5 @@
contract test {
address owner;
mapping(address owner => bytes32 ipfs) names;
}
// ----
@@ -0,0 +1,5 @@
contract test {
mapping(address owner => address owner) owner;
}
// ----
// DeclarationError 1809: (20-59): Conflicting parameter name "owner" in mapping.
@@ -0,0 +1,7 @@
contract test {
mapping(address owner => mapping(address owner => address owner)) owner;
}
// ----
// DeclarationError 1809: (45-84): Conflicting parameter name "owner" in mapping.
// DeclarationError 1809: (20-85): Conflicting parameter name "owner" in mapping.
// DeclarationError 1809: (20-85): Conflicting parameter name "owner" in mapping.
@@ -0,0 +1,5 @@
contract test {
mapping(address owner => mapping(address owner => address hello)) world;
}
// ----
// DeclarationError 1809: (20-85): Conflicting parameter name "owner" in mapping.
@@ -0,0 +1,5 @@
contract test {
mapping(address owner => mapping(address hello => address owner)) world;
}
// ----
// DeclarationError 1809: (20-85): Conflicting parameter name "owner" in mapping.
@@ -0,0 +1,5 @@
contract test {
mapping(address hello => mapping(address owner => address owner)) world;
}
// ----
// DeclarationError 1809: (45-84): Conflicting parameter name "owner" in mapping.
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint name1 => uint name2) storage map) internal {
map[1] = 2;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint name1 => uint) storage map) internal {
map[1] = 2;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint => uint name2) storage map) internal {
map[1] = 2;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint name1 => mapping(uint name2 => uint name3) name4) storage map) internal {
map[1][2] = 3;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint name1 => uint[] name2) storage map) internal {
map[1].push(2);
}
}
// ----
@@ -0,0 +1,7 @@
contract test {
function _main(mapping(uint nameSame => mapping(uint name2 => mapping(uint nameSame => uint name3) name4) name5) storage map) internal {
map[1][2][3] = 4;
}
}
// ----
// DeclarationError 1809: (35-132): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,6 @@
contract test {
function _main(mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) storage map) internal {
map[1][2][3] = 4;
}
}
// ----
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint name1 => uint name2) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint name1 => uint) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint => uint name2) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint name1 => mapping(uint name2 => uint name3) name4) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint name1 => uint[] name2) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,5 @@
contract test {
function(mapping(uint nameSame => mapping(uint name2 => mapping(uint nameSame => uint name3) name4) name5) storage) internal stateVariableName;
}
// ----
// DeclarationError 1809: (29-126): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,4 @@
contract test {
function(mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) storage) internal stateVariableName;
}
// ----
@@ -0,0 +1,9 @@
contract test {
mapping(uint name1 => uint name2) map;
function main() external {
mapping(uint name3 => uint name4) storage _map = map;
_map[1] = 2;
}
}
// ----
@@ -0,0 +1,9 @@
contract test {
mapping(uint name1 => uint) map;
function main() external {
mapping(uint => uint name4) storage _map = map;
_map[1] = 2;
}
}
// ----
@@ -0,0 +1,9 @@
contract test {
mapping(uint => uint name2) map;
function main() external {
mapping(uint name3 => uint) storage _map = map;
_map[1] = 2;
}
}
// ----
@@ -0,0 +1,9 @@
contract test {
mapping(uint name1 => mapping(uint name2 => uint name3) name4) map;
function main() external {
mapping(uint name5 => uint name6) storage _map = map[1];
_map[1] = 2;
}
}
// ----
@@ -0,0 +1,9 @@
contract test {
mapping(uint name1 => uint[] name4) map;
function main() external {
mapping(uint name5 => uint[] name6) storage _map = map;
_map[1].push(2);
}
}
// ----
@@ -0,0 +1,10 @@
contract test {
mapping(uint name1 => mapping(uint name2 => uint name3) name4) map;
function main() external {
mapping(uint nameSame => mapping(uint name2 => uint nameSame) name4) storage _map = map;
_map[1][2] = 3;
}
}
// ----
// DeclarationError 1809: (128-196): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,11 @@
contract test {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name3) name6) name4) map;
function main() external {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name3) name6) name4) storage _map = map;
_map[1][2][3] = 4;
}
}
// ----
// DeclarationError 1809: (20-117): Conflicting parameter name "nameSame" in mapping.
// DeclarationError 1809: (163-260): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,9 @@
contract test {
mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) map;
function main() external {
mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) storage _map = map;
_map[1][2][3] = 4;
}
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address owner => mapping(address spender => bytes32 note)) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address => mapping(address spender => bytes32 note)) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address owner => mapping(address => bytes32 note)) names;
}
// ----
@@ -0,0 +1,4 @@
contract test {
mapping(address owner => mapping(address spender => bytes32)) names;
}
// ----
@@ -0,0 +1,5 @@
contract test {
mapping(address owner => mapping(address spender => bytes32 note));
}
// ----
// ParserError 2314: (86-87): Expected identifier but got ';'
@@ -0,0 +1,5 @@
contract test {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name2) name3) name4) name5;
}
// ----
// DeclarationError 1809: (20-117): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,5 @@
contract test {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name3) name6) name4) public name5;
}
// ----
// DeclarationError 1809: (20-117): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,4 @@
contract test {
mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) public name7;
}
// ----
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint phone => uint calls) friends;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint phone => uint) friends;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint => uint calls) friends;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint phone => mapping(uint call => uint time) callTimes) friends;
}
}
// ----
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint phone => uint[] calls) friends;
}
}
// ----
@@ -0,0 +1,7 @@
contract test {
struct Person {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name2) name3) name4) name5;
}
}
// ----
// DeclarationError 1809: (44-141): Conflicting parameter name "nameSame" in mapping.
@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint name1 => mapping(uint name2 => mapping(uint name3 => uint name4) name5) name6) name7;
}
}
// ----
@@ -2,3 +2,4 @@ type MyInt is int;
contract C {
mapping(MyInt => int) m;
}
// ----