mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
committed by
Nikola Matic
co-authored by
Hari
Nikola Matić
parent
1c8745c54a
commit
fa78e0f3d4
@@ -267,14 +267,59 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
||||
solAssert(dynamic_cast<ElementaryTypeName const*>(&_mapping.keyType()), "");
|
||||
|
||||
Type const* keyType = _mapping.keyType().annotation().type;
|
||||
ASTString keyName = _mapping.keyName();
|
||||
|
||||
Type const* valueType = _mapping.valueType().annotation().type;
|
||||
ASTString valueName = _mapping.valueName();
|
||||
|
||||
// Convert key type to memory.
|
||||
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);
|
||||
|
||||
// Convert value type to storage reference.
|
||||
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
|
||||
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
|
||||
_mapping.annotation().type = TypeProvider::mapping(keyType, keyName, valueType, valueName);
|
||||
|
||||
// Check if parameter names are conflicting.
|
||||
if (!keyName.empty())
|
||||
{
|
||||
auto childMappingType = dynamic_cast<MappingType const*>(valueType);
|
||||
ASTString currentValueName = valueName;
|
||||
bool loop = true;
|
||||
while (loop)
|
||||
{
|
||||
bool isError = false;
|
||||
// Value type is a mapping.
|
||||
if (childMappingType)
|
||||
{
|
||||
// Compare top mapping's key name with child mapping's key name.
|
||||
ASTString childKeyName = childMappingType->keyName();
|
||||
if (keyName == childKeyName)
|
||||
isError = true;
|
||||
|
||||
auto valueType = childMappingType->valueType();
|
||||
currentValueName = childMappingType->valueName();
|
||||
childMappingType = dynamic_cast<MappingType const*>(valueType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compare top mapping's key name with the value name.
|
||||
if (keyName == currentValueName)
|
||||
isError = true;
|
||||
|
||||
loop = false; // We arrived at the end of mapping recursion.
|
||||
}
|
||||
|
||||
// Report error.
|
||||
if (isError)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
1809_error,
|
||||
_mapping.location(),
|
||||
"Conflicting parameter name \"" + keyName + "\" in mapping."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
|
||||
+13
-2
@@ -1428,18 +1428,29 @@ public:
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<TypeName> _keyType,
|
||||
ASTPointer<TypeName> _valueType
|
||||
ASTPointer<ASTString> _keyName,
|
||||
ASTPointer<TypeName> _valueType,
|
||||
ASTPointer<ASTString> _valueName
|
||||
):
|
||||
TypeName(_id, _location), m_keyType(std::move(_keyType)), m_valueType(std::move(_valueType)) {}
|
||||
TypeName(_id, _location),
|
||||
m_keyType(std::move(_keyType)),
|
||||
m_keyName(std::move(_keyName)),
|
||||
m_valueType(std::move(_valueType)),
|
||||
m_valueName(std::move(_valueName))
|
||||
{}
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
TypeName const& keyType() const { return *m_keyType; }
|
||||
ASTString keyName() const { return *m_keyName; }
|
||||
TypeName const& valueType() const { return *m_valueType; }
|
||||
ASTString valueName() const { return *m_valueName; }
|
||||
|
||||
private:
|
||||
ASTPointer<TypeName> m_keyType;
|
||||
ASTPointer<ASTString> m_keyName;
|
||||
ASTPointer<TypeName> m_valueType;
|
||||
ASTPointer<ASTString> m_valueName;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -598,7 +598,9 @@ bool ASTJsonExporter::visit(Mapping const& _node)
|
||||
{
|
||||
setJsonNode(_node, "Mapping", {
|
||||
make_pair("keyType", toJson(_node.keyType())),
|
||||
make_pair("keyName", _node.keyName()),
|
||||
make_pair("valueType", toJson(_node.valueType())),
|
||||
make_pair("valueName", _node.valueName()),
|
||||
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -648,7 +648,9 @@ ASTPointer<Mapping> ASTJsonImporter::createMapping(Json::Value const& _node)
|
||||
return createASTNode<Mapping>(
|
||||
_node,
|
||||
convertJsonToASTNode<TypeName>(member(_node, "keyType")),
|
||||
convertJsonToASTNode<TypeName>(member(_node, "valueType"))
|
||||
memberAsASTString(_node, "keyName"),
|
||||
convertJsonToASTNode<TypeName>(member(_node, "valueType")),
|
||||
memberAsASTString(_node, "valueName")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -571,9 +571,9 @@ MagicType const* TypeProvider::meta(Type const* _type)
|
||||
return createAndGet<MagicType>(_type);
|
||||
}
|
||||
|
||||
MappingType const* TypeProvider::mapping(Type const* _keyType, Type const* _valueType)
|
||||
MappingType const* TypeProvider::mapping(Type const* _keyType, ASTString _keyName, Type const* _valueType, ASTString _valueName)
|
||||
{
|
||||
return createAndGet<MappingType>(_keyType, _valueType);
|
||||
return createAndGet<MappingType>(_keyType, _keyName, _valueType, _valueName);
|
||||
}
|
||||
|
||||
UserDefinedValueType const* TypeProvider::userDefinedValueType(UserDefinedValueTypeDefinition const& _definition)
|
||||
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
|
||||
static MagicType const* meta(Type const* _type);
|
||||
|
||||
static MappingType const* mapping(Type const* _keyType, Type const* _valueType);
|
||||
static MappingType const* mapping(Type const* _keyType, ASTString _keyName, Type const* _valueType, ASTString _valueName);
|
||||
|
||||
static UserDefinedValueType const* userDefinedValueType(UserDefinedValueTypeDefinition const& _definition);
|
||||
|
||||
|
||||
@@ -2788,14 +2788,16 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
m_declaration(&_varDecl)
|
||||
{
|
||||
auto returnType = _varDecl.annotation().type;
|
||||
ASTString returnName;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType))
|
||||
{
|
||||
m_parameterTypes.push_back(mappingType->keyType());
|
||||
m_parameterNames.emplace_back("");
|
||||
m_parameterNames.push_back(mappingType->keyName());
|
||||
returnType = mappingType->valueType();
|
||||
returnName = mappingType->valueName();
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType))
|
||||
{
|
||||
@@ -2834,7 +2836,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
DataLocation::Memory,
|
||||
returnType
|
||||
));
|
||||
m_returnParameterNames.emplace_back("");
|
||||
m_returnParameterNames.emplace_back(returnName);
|
||||
}
|
||||
|
||||
solAssert(
|
||||
|
||||
@@ -1510,8 +1510,8 @@ private:
|
||||
class MappingType: public CompositeType
|
||||
{
|
||||
public:
|
||||
MappingType(Type const* _keyType, Type const* _valueType):
|
||||
m_keyType(_keyType), m_valueType(_valueType) {}
|
||||
MappingType(Type const* _keyType, ASTString _keyName, Type const* _valueType, ASTString _valueName):
|
||||
m_keyType(_keyType), m_keyName(_keyName), m_valueType(_valueType), m_valueName(_valueName) {}
|
||||
|
||||
Category category() const override { return Category::Mapping; }
|
||||
|
||||
@@ -1531,14 +1531,18 @@ public:
|
||||
std::vector<std::tuple<std::string, Type const*>> makeStackItems() const override;
|
||||
|
||||
Type const* keyType() const { return m_keyType; }
|
||||
ASTString keyName() const { return m_keyName; }
|
||||
Type const* valueType() const { return m_valueType; }
|
||||
ASTString valueName() const { return m_valueName; }
|
||||
|
||||
protected:
|
||||
std::vector<Type const*> decomposition() const override { return {m_valueType}; }
|
||||
|
||||
private:
|
||||
Type const* m_keyType;
|
||||
ASTString m_keyName;
|
||||
Type const* m_valueType;
|
||||
ASTString m_valueName;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1187,11 +1187,21 @@ ASTPointer<Mapping> Parser::parseMapping()
|
||||
}
|
||||
else
|
||||
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
|
||||
ASTPointer<ASTString> keyName;
|
||||
if (m_scanner->currentToken() == Token::Identifier)
|
||||
keyName = getLiteralAndAdvance();
|
||||
else
|
||||
keyName = make_shared<ASTString>("");
|
||||
expectToken(Token::DoubleArrow);
|
||||
ASTPointer<TypeName> valueType = parseTypeName();
|
||||
ASTPointer<ASTString> valueName;
|
||||
if (m_scanner->currentToken() == Token::Identifier)
|
||||
valueName = getLiteralAndAdvance();
|
||||
else
|
||||
valueName = make_shared<ASTString>("");
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RParen);
|
||||
return nodeFactory.createNode<Mapping>(keyType, valueType);
|
||||
return nodeFactory.createNode<Mapping>(keyType, keyName, valueType, valueName);
|
||||
}
|
||||
|
||||
ASTPointer<ParameterList> Parser::parseParameterList(
|
||||
|
||||
Reference in New Issue
Block a user