semantics: Suggest auto-deduced type when user declares variable with var keyword.

This commit is contained in:
Christian Parpart 2018-07-02 17:00:09 +02:00 committed by chriseth
parent d84976dc87
commit 1505e28b56
4 changed files with 91 additions and 19 deletions

View File

@ -254,15 +254,6 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
return true;
}
bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
{
if (!_declaration.typeName())
{
m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is disallowed.");
}
return true;
}
bool SyntaxChecker::visit(StructDefinition const& _struct)
{
if (_struct.members().empty())

View File

@ -69,8 +69,6 @@ private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(FunctionTypeName const& _node) override;
virtual bool visit(VariableDeclaration const& _declaration) override;
virtual bool visit(StructDefinition const& _struct) override;
ErrorReporter& m_errorReporter;

View File

@ -23,6 +23,7 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <memory>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
@ -1051,6 +1052,49 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
m_insideEmitStatement = false;
}
/**
* Creates a tuple declaration syntax from a vector of variable declarations.
*
* @param decls a tuple of variables
*
* @returns a Solidity language confirming string of a tuple variable declaration.
*/
static string createTupleDecl(vector<VariableDeclaration const*> const & decls)
{
vector<string> components;
for (VariableDeclaration const* decl : decls)
if (decl)
components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name());
else
components.emplace_back();
if (decls.size() == 1)
return components.front();
else
return "(" + boost::algorithm::join(components, ", ") + ")";
}
static bool typeCanBeExpressed(vector<VariableDeclaration const*> const & decls)
{
for (VariableDeclaration const* decl : decls)
{
// skip empty tuples (they can be expressed of course)
if (!decl)
continue;
if (auto functionType = dynamic_cast<FunctionType const*>(decl->annotation().type.get()))
{
if (
functionType->kind() != FunctionType::Kind::Internal &&
functionType->kind() != FunctionType::Kind::External
)
return false;
}
}
return true;
}
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
@ -1161,6 +1205,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
else
assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
bool autoTypeDeductionNeeded = false;
for (size_t i = 0; i < assignments.size(); ++i)
{
if (!assignments[i])
@ -1171,6 +1217,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
solAssert(!!valueComponentType, "");
if (!var.annotation().type)
{
autoTypeDeductionNeeded = true;
// Infer type from value.
solAssert(!var.typeName(), "");
var.annotation().type = valueComponentType->mobileType();
@ -1214,14 +1262,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
}
else
solAssert(dynamic_cast<FixedPointType const*>(var.annotation().type.get()), "Unknown type.");
m_errorReporter.warning(
_statement.location(),
"The type of this variable was inferred as " +
typeName +
extension +
". This is probably not desired. Use an explicit type to silence this warning."
);
}
var.accept(*this);
@ -1258,6 +1298,26 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
}
}
}
if (autoTypeDeductionNeeded)
{
if (!typeCanBeExpressed(assignments))
{
m_errorReporter.syntaxError(_statement.location(),
"Use of the \"var\" keyword is disallowed. "
"Type cannot be expressed in syntax.");
}
else
{
// report with trivial snipped `uint i = ...`
string const typeName = createTupleDecl(assignments);
m_errorReporter.syntaxError(_statement.location(),
"Use of the \"var\" keyword is disallowed. "
"Use explicit declaration `" + typeName + " = ...´ instead.");
}
}
return false;
}

View File

@ -0,0 +1,23 @@
contract C {
function h() internal pure returns (uint, uint, uint) {
return (1, 2, 4);
}
function g(uint x) internal pure returns (uint) {
return x;
}
function f() internal pure {
var i = 31415;
var t = "string";
var g2 = g;
var myblockhash = block.blockhash;
var (a, b) = (2, "troi");
var (x,, z) = h();
}
}
// ----
// SyntaxError: (224-237): Use of the "var" keyword is disallowed. Use explicit declaration `uint16 i = ...´ instead.
// SyntaxError: (247-263): Use of the "var" keyword is disallowed. Use explicit declaration `string memory t = ...´ instead.
// SyntaxError: (273-283): Use of the "var" keyword is disallowed. Use explicit declaration `function (uint256) pure returns (uint256) g2 = ...´ instead.
// SyntaxError: (293-326): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax.
// SyntaxError: (336-360): Use of the "var" keyword is disallowed. Use explicit declaration `(uint8 a, string memory b) = ...´ instead.
// SyntaxError: (370-387): Use of the "var" keyword is disallowed. Use explicit declaration `(uint256 x, , uint256 z) = ...´ instead.