mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Disallow multi variable declarations with mismatching number of values.
This commit is contained in:
parent
5d8a8f7265
commit
fc370591f0
@ -1043,10 +1043,10 @@ namespace
|
|||||||
* @returns a suggested left-hand-side of a multi-variable declaration contairing
|
* @returns a suggested left-hand-side of a multi-variable declaration contairing
|
||||||
* the variable declarations given in @a _decls.
|
* the variable declarations given in @a _decls.
|
||||||
*/
|
*/
|
||||||
string createTupleDecl(vector<VariableDeclaration const*> const& _decls)
|
string createTupleDecl(vector<ASTPointer<VariableDeclaration>> const& _decls)
|
||||||
{
|
{
|
||||||
vector<string> components;
|
vector<string> components;
|
||||||
for (VariableDeclaration const* decl: _decls)
|
for (ASTPointer<VariableDeclaration> const& decl: _decls)
|
||||||
if (decl)
|
if (decl)
|
||||||
components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name());
|
components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name());
|
||||||
else
|
else
|
||||||
@ -1058,9 +1058,9 @@ string createTupleDecl(vector<VariableDeclaration const*> const& _decls)
|
|||||||
return "(" + boost::algorithm::join(components, ", ") + ")";
|
return "(" + boost::algorithm::join(components, ", ") + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool typeCanBeExpressed(vector<VariableDeclaration const*> const& decls)
|
bool typeCanBeExpressed(vector<ASTPointer<VariableDeclaration>> const& decls)
|
||||||
{
|
{
|
||||||
for (VariableDeclaration const* decl: decls)
|
for (ASTPointer<VariableDeclaration> const& decl: decls)
|
||||||
{
|
{
|
||||||
// skip empty tuples (they can be expressed of course)
|
// skip empty tuples (they can be expressed of course)
|
||||||
if (!decl)
|
if (!decl)
|
||||||
@ -1080,7 +1080,6 @@ bool typeCanBeExpressed(vector<VariableDeclaration const*> const& decls)
|
|||||||
|
|
||||||
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||||
{
|
{
|
||||||
bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
|
||||||
if (!_statement.initialValue())
|
if (!_statement.initialValue())
|
||||||
{
|
{
|
||||||
// No initial value is only permitted for single variables with specified type.
|
// No initial value is only permitted for single variables with specified type.
|
||||||
@ -1119,82 +1118,27 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
else
|
else
|
||||||
valueTypes = TypePointers{type(*_statement.initialValue())};
|
valueTypes = TypePointers{type(*_statement.initialValue())};
|
||||||
|
|
||||||
// Determine which component is assigned to which variable.
|
|
||||||
// If numbers do not match, fill up if variables begin or end empty (not both).
|
|
||||||
vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
|
|
||||||
assignments.resize(valueTypes.size(), nullptr);
|
|
||||||
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
|
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
|
||||||
if (variables.empty())
|
if (variables.empty())
|
||||||
{
|
// We already have an error for this in the SyntaxChecker.
|
||||||
if (!valueTypes.empty())
|
solAssert(m_errorReporter.hasErrors(), "");
|
||||||
m_errorReporter.fatalTypeError(
|
|
||||||
_statement.location(),
|
|
||||||
"Too many components (" +
|
|
||||||
toString(valueTypes.size()) +
|
|
||||||
") in value for variable assignment (0) needed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (valueTypes.size() != variables.size())
|
else if (valueTypes.size() != variables.size())
|
||||||
{
|
m_errorReporter.typeError(
|
||||||
if (v050)
|
|
||||||
m_errorReporter.fatalTypeError(
|
|
||||||
_statement.location(),
|
|
||||||
"Different number of components on the left hand side (" +
|
|
||||||
toString(variables.size()) +
|
|
||||||
") than on the right hand side (" +
|
|
||||||
toString(valueTypes.size()) +
|
|
||||||
")."
|
|
||||||
);
|
|
||||||
else if (!variables.front() && !variables.back())
|
|
||||||
m_errorReporter.fatalTypeError(
|
|
||||||
_statement.location(),
|
|
||||||
"Wildcard both at beginning and end of variable declaration list is only allowed "
|
|
||||||
"if the number of components is equal."
|
|
||||||
);
|
|
||||||
else
|
|
||||||
m_errorReporter.warning(
|
|
||||||
_statement.location(),
|
|
||||||
"Different number of components on the left hand side (" +
|
|
||||||
toString(variables.size()) +
|
|
||||||
") than on the right hand side (" +
|
|
||||||
toString(valueTypes.size()) +
|
|
||||||
")."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
size_t minNumValues = variables.size();
|
|
||||||
if (!variables.empty() && (!variables.back() || !variables.front()))
|
|
||||||
--minNumValues;
|
|
||||||
if (valueTypes.size() < minNumValues)
|
|
||||||
m_errorReporter.fatalTypeError(
|
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Not enough components (" +
|
"Different number of components on the left hand side (" +
|
||||||
|
toString(variables.size()) +
|
||||||
|
") than on the right hand side (" +
|
||||||
toString(valueTypes.size()) +
|
toString(valueTypes.size()) +
|
||||||
") in value to assign all variables (" +
|
")."
|
||||||
toString(minNumValues) + ")."
|
|
||||||
);
|
);
|
||||||
if (valueTypes.size() > variables.size() && variables.front() && variables.back())
|
|
||||||
m_errorReporter.fatalTypeError(
|
|
||||||
_statement.location(),
|
|
||||||
"Too many components (" +
|
|
||||||
toString(valueTypes.size()) +
|
|
||||||
") in value for variable assignment (" +
|
|
||||||
toString(minNumValues) +
|
|
||||||
" needed)."
|
|
||||||
);
|
|
||||||
bool fillRight = !variables.empty() && (!variables.back() || variables.front());
|
|
||||||
for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
|
|
||||||
if (fillRight)
|
|
||||||
assignments[i] = variables[i].get();
|
|
||||||
else
|
|
||||||
assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
|
|
||||||
|
|
||||||
bool autoTypeDeductionNeeded = false;
|
bool autoTypeDeductionNeeded = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < assignments.size(); ++i)
|
for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
|
||||||
{
|
{
|
||||||
if (!assignments[i])
|
if (!variables[i])
|
||||||
continue;
|
continue;
|
||||||
VariableDeclaration const& var = *assignments[i];
|
VariableDeclaration const& var = *variables[i];
|
||||||
solAssert(!var.value(), "Value has to be tied to statement.");
|
solAssert(!var.value(), "Value has to be tied to statement.");
|
||||||
TypePointer const& valueComponentType = valueTypes[i];
|
TypePointer const& valueComponentType = valueTypes[i];
|
||||||
solAssert(!!valueComponentType, "");
|
solAssert(!!valueComponentType, "");
|
||||||
@ -1284,7 +1228,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
|
|
||||||
if (autoTypeDeductionNeeded)
|
if (autoTypeDeductionNeeded)
|
||||||
{
|
{
|
||||||
if (!typeCanBeExpressed(assignments))
|
if (!typeCanBeExpressed(variables))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Use of the \"var\" keyword is disallowed. "
|
"Use of the \"var\" keyword is disallowed. "
|
||||||
@ -1294,7 +1238,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Use of the \"var\" keyword is disallowed. "
|
"Use of the \"var\" keyword is disallowed. "
|
||||||
"Use explicit declaration `" + createTupleDecl(assignments) + " = ...´ instead."
|
"Use explicit declaration `" + createTupleDecl(variables) + " = ...´ instead."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,13 +535,6 @@ ReturnAnnotation& Return::annotation() const
|
|||||||
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
|
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
|
|
||||||
{
|
|
||||||
if (!m_annotation)
|
|
||||||
m_annotation = new VariableDeclarationStatementAnnotation();
|
|
||||||
return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionAnnotation& Expression::annotation() const
|
ExpressionAnnotation& Expression::annotation() const
|
||||||
{
|
{
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
|
@ -1270,8 +1270,6 @@ public:
|
|||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
VariableDeclarationStatementAnnotation& annotation() const override;
|
|
||||||
|
|
||||||
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
|
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
|
||||||
Expression const* initialValue() const { return m_initialValue.get(); }
|
Expression const* initialValue() const { return m_initialValue.get(); }
|
||||||
|
|
||||||
|
@ -164,13 +164,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
|
|||||||
ContractDefinition const* contractScope = nullptr;
|
ContractDefinition const* contractScope = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VariableDeclarationStatementAnnotation: StatementAnnotation
|
|
||||||
{
|
|
||||||
/// Information about which component of the value is assigned to which variable.
|
|
||||||
/// The pointer can be null to signify that the component is discarded.
|
|
||||||
std::vector<VariableDeclaration const*> assignments;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ExpressionAnnotation: ASTAnnotation
|
struct ExpressionAnnotation: ASTAnnotation
|
||||||
{
|
{
|
||||||
/// Inferred type of the expression.
|
/// Inferred type of the expression.
|
||||||
|
@ -555,8 +555,8 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
|
|||||||
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
|
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
|
||||||
{
|
{
|
||||||
Json::Value varDecs(Json::arrayValue);
|
Json::Value varDecs(Json::arrayValue);
|
||||||
for (auto const& v: _node.annotation().assignments)
|
for (auto const& v: _node.declarations())
|
||||||
appendMove(varDecs, idOrNull(v));
|
appendMove(varDecs, idOrNull(v.get()));
|
||||||
setJsonNode(_node, "VariableDeclarationStatement", {
|
setJsonNode(_node, "VariableDeclarationStatement", {
|
||||||
make_pair("assignments", std::move(varDecs)),
|
make_pair("assignments", std::move(varDecs)),
|
||||||
make_pair("declarations", toJson(_node.declarations())),
|
make_pair("declarations", toJson(_node.declarations())),
|
||||||
|
@ -833,20 +833,19 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar
|
|||||||
valueTypes = tupleType->components();
|
valueTypes = tupleType->components();
|
||||||
else
|
else
|
||||||
valueTypes = TypePointers{expression->annotation().type};
|
valueTypes = TypePointers{expression->annotation().type};
|
||||||
auto const& assignments = _variableDeclarationStatement.annotation().assignments;
|
auto const& declarations = _variableDeclarationStatement.declarations();
|
||||||
solAssert(assignments.size() == valueTypes.size(), "");
|
solAssert(declarations.size() == valueTypes.size(), "");
|
||||||
for (size_t i = 0; i < assignments.size(); ++i)
|
for (size_t i = 0; i < declarations.size(); ++i)
|
||||||
{
|
{
|
||||||
size_t j = assignments.size() - i - 1;
|
size_t j = declarations.size() - i - 1;
|
||||||
solAssert(!!valueTypes[j], "");
|
solAssert(!!valueTypes[j], "");
|
||||||
VariableDeclaration const* varDecl = assignments[j];
|
if (VariableDeclaration const* varDecl = declarations[j].get())
|
||||||
if (!varDecl)
|
|
||||||
utils.popStackElement(*valueTypes[j]);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
utils.convertType(*valueTypes[j], *varDecl->annotation().type);
|
utils.convertType(*valueTypes[j], *varDecl->annotation().type);
|
||||||
utils.moveToStackVariable(*varDecl);
|
utils.moveToStackVariable(*varDecl);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
utils.popStackElement(*valueTypes[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checker.check();
|
checker.check();
|
||||||
|
Loading…
Reference in New Issue
Block a user