diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 63f8fac39..4311e77d3 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -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()) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1579df57b..8ee3df375 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -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; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index da04ee963..8ea0a4d7c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1036,6 +1037,47 @@ void TypeChecker::endVisit(EmitStatement const& _emit) m_insideEmitStatement = false; } +namespace +{ +/** + * @returns a suggested left-hand-side of a multi-variable declaration contairing + * the variable declarations given in @a _decls. + */ +string createTupleDecl(vector const& _decls) +{ + vector 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, ", ") + ")"; +} + +bool typeCanBeExpressed(vector const& decls) +{ + for (VariableDeclaration const* decl: decls) + { + // skip empty tuples (they can be expressed of course) + if (!decl) + continue; + + if (auto functionType = dynamic_cast(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); @@ -1146,6 +1188,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]) @@ -1156,6 +1200,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(); @@ -1199,14 +1245,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) } else solAssert(dynamic_cast(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); @@ -1243,6 +1281,23 @@ 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 + m_errorReporter.syntaxError( + _statement.location(), + "Use of the \"var\" keyword is disallowed. " + "Use explicit declaration `" + createTupleDecl(assignments) + " = ...´ instead." + ); + } + return false; } diff --git a/test/libsolidity/syntaxTests/types/var_type_suggest.sol b/test/libsolidity/syntaxTests/types/var_type_suggest.sol new file mode 100644 index 000000000..176fab960 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_type_suggest.sol @@ -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.