Browse Source

Added meaningful exception types.

cl-refactor
Christian 10 years ago
parent
commit
b71e993223
  1. 36
      libsolidity/AST.cpp
  2. 34
      libsolidity/Exceptions.h
  3. 10
      libsolidity/NameAndTypeResolver.cpp
  4. 26
      libsolidity/Parser.cpp
  5. 9
      test/solidityNameAndTypeResolution.cpp
  6. 4
      test/solidityParser.cpp

36
libsolidity/AST.cpp

@ -24,6 +24,7 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
@ -245,7 +246,9 @@ void Literal::accept(ASTVisitor& _visitor)
void Statement::expectType(Expression& _expression, const Type& _expectedType) void Statement::expectType(Expression& _expression, const Type& _expectedType)
{ {
if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType)) if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType))
throw std::exception(); // @todo BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type not implicitly convertible "
"to expected type."));
//@todo provide more information to the exception
} }
ptr<Type> Block::checkTypeRequirements() ptr<Type> Block::checkTypeRequirements()
@ -284,7 +287,9 @@ ptr<Type> Return::checkTypeRequirements()
{ {
BOOST_ASSERT(m_returnParameters != nullptr); BOOST_ASSERT(m_returnParameters != nullptr);
if (m_returnParameters->getParameters().size() != 1) if (m_returnParameters->getParameters().size() != 1)
throw std::exception(); // @todo BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Different number of arguments in "
"return statement than in returns "
"declaration."));
// this could later be changed such that the paramaters type is an anonymous struct type, // this could later be changed such that the paramaters type is an anonymous struct type,
// but for now, we only allow one return parameter // but for now, we only allow one return parameter
@ -318,7 +323,7 @@ ptr<Type> Assignment::checkTypeRequirements()
if (m_assigmentOperator != Token::ASSIGN) { if (m_assigmentOperator != Token::ASSIGN) {
// complex assignment // complex assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type."));
} }
return m_type; return m_type;
} }
@ -328,7 +333,7 @@ ptr<Type> UnaryOperation::checkTypeRequirements()
// INC, DEC, NOT, BIT_NOT, DELETE // INC, DEC, NOT, BIT_NOT, DELETE
m_type = m_subExpression->checkTypeRequirements(); m_type = m_subExpression->checkTypeRequirements();
if (m_type->acceptsUnaryOperator(m_operator)) if (m_type->acceptsUnaryOperator(m_operator))
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Unary operator not compatible with type."));
return m_type; return m_type;
} }
@ -342,7 +347,7 @@ ptr<Type> BinaryOperation::checkTypeRequirements()
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
m_commonType = m_right->getType(); m_commonType = m_right->getType();
else else
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation."));
if (Token::IsCompareOp(m_operator)) { if (Token::IsCompareOp(m_operator)) {
m_type = std::make_shared<BoolType>(); m_type = std::make_shared<BoolType>();
@ -350,7 +355,7 @@ ptr<Type> BinaryOperation::checkTypeRequirements()
BOOST_ASSERT(Token::IsBinaryOp(m_operator)); BOOST_ASSERT(Token::IsBinaryOp(m_operator));
m_type = m_commonType; m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator))) if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator)))
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type."));
} }
return m_type; return m_type;
} }
@ -369,9 +374,11 @@ ptr<Type> FunctionCall::checkTypeRequirements()
//@todo for structs, we have to check the number of arguments to be equal to the //@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members // number of non-mapping members
if (m_arguments.size() != 1) if (m_arguments.size() != 1)
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("More than one argument for "
"explicit type conersion."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType()))
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not "
"allowed."));
m_type = type->getActualType(); m_type = type->getActualType();
} else if (category == Type::Category::FUNCTION) { } else if (category == Type::Category::FUNCTION) {
//@todo would be nice to create a struct type from the arguments //@todo would be nice to create a struct type from the arguments
@ -382,10 +389,12 @@ ptr<Type> FunctionCall::checkTypeRequirements()
FunctionDefinition const& fun = function->getFunction(); FunctionDefinition const& fun = function->getFunction();
vecptr<VariableDeclaration> const& parameters = fun.getParameters(); vecptr<VariableDeclaration> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size()) if (parameters.size() != m_arguments.size())
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for "
"function call."));
for (size_t i = 0; i < m_arguments.size(); ++i) { for (size_t i = 0; i < m_arguments.size(); ++i) {
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
throw std::exception(); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in "
"function call."));
} }
// @todo actually the return type should be an anonymous struct, // @todo actually the return type should be an anonymous struct,
@ -395,7 +404,7 @@ ptr<Type> FunctionCall::checkTypeRequirements()
else else
m_type = fun.getReturnParameterList()->getParameters().front()->getType(); m_type = fun.getReturnParameterList()->getParameters().front()->getType();
} else { } else {
throw std::exception(); // type does not support invocation BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation."));
} }
return m_type; return m_type;
} }
@ -428,7 +437,8 @@ ptr<Type> Identifier::checkTypeRequirements()
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration); VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration);
if (variable != nullptr) { if (variable != nullptr) {
if (variable->getType().get() == nullptr) if (variable->getType().get() == nullptr)
throw std::exception(); // variable used before type could be determined BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type "
"could be determined."));
m_type = variable->getType(); m_type = variable->getType();
return m_type; return m_type;
} }
@ -452,7 +462,7 @@ ptr<Type> Identifier::checkTypeRequirements()
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef));
return m_type; return m_type;
} }
throw std::exception(); // declaration reference of unknown/forbidden type BOOST_ASSERT(false); // declaration reference of unknown/forbidden type
return m_type; return m_type;
} }

34
libsolidity/Exceptions.h

@ -0,0 +1,34 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity exception hierarchy.
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev {
namespace solidity {
struct ParserError : virtual Exception {};
struct TypeError : virtual Exception {};
struct DeclarationError : virtual Exception {};
} }

10
libsolidity/NameAndTypeResolver.cpp

@ -23,6 +23,7 @@
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Exceptions.h>
#include <boost/assert.hpp> #include <boost/assert.hpp>
namespace dev { namespace dev {
@ -115,7 +116,7 @@ void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnPara
virtual bool visit(Identifier& _identifier) override { virtual bool visit(Identifier& _identifier) override {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (declaration == nullptr) if (declaration == nullptr)
throw std::exception(); // @todo BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration); _identifier.setReferencedDeclaration(*declaration);
return false; return false;
} }
@ -151,10 +152,11 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl
virtual bool visit(UserDefinedTypeName& _typeName) override { virtual bool visit(UserDefinedTypeName& _typeName) override {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (declaration == nullptr) if (declaration == nullptr)
throw std::exception(); // @todo BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier."));
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration);
//@todo later, contracts are also valid types
if (referencedStruct == nullptr) if (referencedStruct == nullptr)
throw std::exception(); // @todo we only allow structs as user defined types (later also contracts) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name."));
_typeName.setReferencedStruct(*referencedStruct); _typeName.setReferencedStruct(*referencedStruct);
return false; return false;
} }
@ -176,7 +178,7 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl
void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) void NameAndTypeResolver::registerDeclaration(Declaration& _declaration)
{ {
if (!m_currentScope->registerDeclaration(_declaration)) if (!m_currentScope->registerDeclaration(_declaration))
throw std::exception(); // @todo BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared."));
} }
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)

26
libsolidity/Parser.cpp

@ -20,10 +20,11 @@
* Solidity parser. * Solidity parser.
*/ */
#include "libdevcore/Log.h" #include <libdevcore/Log.h>
#include "libsolidity/BaseTypes.h" #include <libsolidity/BaseTypes.h>
#include "libsolidity/Parser.h" #include <libsolidity/Parser.h>
#include "libsolidity/Scanner.h" #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
@ -530,16 +531,17 @@ ptr<ASTString> Parser::getLiteralAndAdvance()
void Parser::throwExpectationError(const std::string& _description) void Parser::throwExpectationError(const std::string& _description)
{ {
//@todo put some of this stuff into ParserError
int line, column; int line, column;
std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition());
cwarn << "Solidity parser error: " << _description std::stringstream buf;
<< "at line " << (line + 1) buf << "Solidity parser error: " << _description
<< ", column " << (column + 1); << " at line " << (line + 1)
cwarn << m_scanner->getLineAtPosition(getPosition()); << ", column " << (column + 1) << "\n"
cwarn << std::string(column, ' ') << "^"; << m_scanner->getLineAtPosition(getPosition()) << "\n"
<< std::string(column, ' ') << "^";
/// @todo make a proper exception hierarchy
throw std::exception(); BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str()));
} }

9
test/solidityNameAndTypeResolution.cpp

@ -26,6 +26,7 @@
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
namespace dev { namespace dev {
@ -60,7 +61,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
" uint256 variable;\n" " uint256 variable;\n"
" uint128 variable;\n" " uint128 variable;\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError);
} }
BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_function_declaration)
@ -69,7 +70,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
" function fun() { var x; }\n" " function fun() { var x; }\n"
" function fun() { var x; }\n" " function fun() { var x; }\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError);
} }
BOOST_AUTO_TEST_CASE(double_variable_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration)
@ -77,7 +78,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration)
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function f() { uint256 x; if (true) { uint256 x; } }\n" " function f() { uint256 x; if (true) { uint256 x; } }\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError);
} }
BOOST_AUTO_TEST_CASE(name_shadowing) BOOST_AUTO_TEST_CASE(name_shadowing)
@ -104,7 +105,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name)
" uint256 variable;\n" " uint256 variable;\n"
" function f(uint256 arg) { f(notfound); }" " function f(uint256 arg) { f(notfound); }"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

4
test/solidityParser.cpp

@ -25,6 +25,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
namespace dev { namespace dev {
@ -54,8 +55,7 @@ BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration)
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 ;\n" " uint256 ;\n"
"}\n"; "}\n";
cwarn << "The next error is expected."; BOOST_CHECK_THROW(parseText(text), ParserError);
BOOST_CHECK_THROW(parseText(text), std::exception);
} }
BOOST_AUTO_TEST_CASE(empty_function) BOOST_AUTO_TEST_CASE(empty_function)

Loading…
Cancel
Save