diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 325ef50b3..926f1c685 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -24,6 +24,7 @@ #include #include +#include namespace dev { namespace solidity { @@ -245,7 +246,9 @@ void Literal::accept(ASTVisitor& _visitor) void Statement::expectType(Expression& _expression, const Type& _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 Block::checkTypeRequirements() @@ -284,7 +287,9 @@ ptr Return::checkTypeRequirements() { BOOST_ASSERT(m_returnParameters != nullptr); 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, // but for now, we only allow one return parameter @@ -318,7 +323,7 @@ ptr Assignment::checkTypeRequirements() if (m_assigmentOperator != Token::ASSIGN) { // complex assignment 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; } @@ -328,7 +333,7 @@ ptr UnaryOperation::checkTypeRequirements() // INC, DEC, NOT, BIT_NOT, DELETE m_type = m_subExpression->checkTypeRequirements(); if (m_type->acceptsUnaryOperator(m_operator)) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Unary operator not compatible with type.")); return m_type; } @@ -342,7 +347,7 @@ ptr BinaryOperation::checkTypeRequirements() else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) m_commonType = m_right->getType(); else - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); if (Token::IsCompareOp(m_operator)) { m_type = std::make_shared(); @@ -350,7 +355,7 @@ ptr BinaryOperation::checkTypeRequirements() BOOST_ASSERT(Token::IsBinaryOp(m_operator)); m_type = m_commonType; 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; } @@ -369,9 +374,11 @@ ptr FunctionCall::checkTypeRequirements() //@todo for structs, we have to check the number of arguments to be equal to the // number of non-mapping members 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())) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not " + "allowed.")); m_type = type->getActualType(); } else if (category == Type::Category::FUNCTION) { //@todo would be nice to create a struct type from the arguments @@ -382,10 +389,12 @@ ptr FunctionCall::checkTypeRequirements() FunctionDefinition const& fun = function->getFunction(); vecptr const& parameters = fun.getParameters(); 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) { 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, @@ -395,7 +404,7 @@ ptr FunctionCall::checkTypeRequirements() else m_type = fun.getReturnParameterList()->getParameters().front()->getType(); } else { - throw std::exception(); // type does not support invocation + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); } return m_type; } @@ -428,7 +437,8 @@ ptr Identifier::checkTypeRequirements() VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); if (variable != 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(); return m_type; } @@ -452,7 +462,7 @@ ptr Identifier::checkTypeRequirements() m_type = std::make_shared(std::make_shared(*contractDef)); return m_type; } - throw std::exception(); // declaration reference of unknown/forbidden type + BOOST_ASSERT(false); // declaration reference of unknown/forbidden type return m_type; } diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h new file mode 100644 index 000000000..5e0b1522b --- /dev/null +++ b/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 . +*/ +/** + * @author Christian + * @date 2014 + * Solidity exception hierarchy. + */ + +#pragma once + +#include + +namespace dev { +namespace solidity { + +struct ParserError : virtual Exception {}; +struct TypeError : virtual Exception {}; +struct DeclarationError : virtual Exception {}; + +} } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 33b550eb6..8e769c475 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -23,6 +23,7 @@ #include #include +#include #include namespace dev { @@ -115,7 +116,7 @@ void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnPara virtual bool visit(Identifier& _identifier) override { Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); if (declaration == nullptr) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); _identifier.setReferencedDeclaration(*declaration); return false; } @@ -151,10 +152,11 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl virtual bool visit(UserDefinedTypeName& _typeName) override { Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); if (declaration == nullptr) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); StructDefinition* referencedStruct = dynamic_cast(declaration); + //@todo later, contracts are also valid types 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); return false; } @@ -176,7 +178,7 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl void NameAndTypeResolver::registerDeclaration(Declaration& _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) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index ab72bf7fc..b28154112 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -20,10 +20,11 @@ * Solidity parser. */ -#include "libdevcore/Log.h" -#include "libsolidity/BaseTypes.h" -#include "libsolidity/Parser.h" -#include "libsolidity/Scanner.h" +#include +#include +#include +#include +#include namespace dev { namespace solidity { @@ -530,16 +531,17 @@ ptr Parser::getLiteralAndAdvance() void Parser::throwExpectationError(const std::string& _description) { + //@todo put some of this stuff into ParserError int line, column; std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); - cwarn << "Solidity parser error: " << _description - << "at line " << (line + 1) - << ", column " << (column + 1); - cwarn << m_scanner->getLineAtPosition(getPosition()); - cwarn << std::string(column, ' ') << "^"; - - /// @todo make a proper exception hierarchy - throw std::exception(); + std::stringstream buf; + buf << "Solidity parser error: " << _description + << " at line " << (line + 1) + << ", column " << (column + 1) << "\n" + << m_scanner->getLineAtPosition(getPosition()) << "\n" + << std::string(column, ' ') << "^"; + + BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str())); } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 5a938e461..c9817eb46 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace dev { @@ -60,7 +61,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) " uint256 variable;\n" " uint128 variable;\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } 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" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_CASE(double_variable_declaration) @@ -77,7 +78,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) char const* text = "contract test {\n" " function f() { uint256 x; if (true) { uint256 x; } }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -104,7 +105,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) " uint256 variable;\n" " function f(uint256 arg) { f(notfound); }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 86d09f170..b1f27bcb6 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev { @@ -54,8 +55,7 @@ BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) char const* text = "contract test {\n" " uint256 ;\n" "}\n"; - cwarn << "The next error is expected."; - BOOST_CHECK_THROW(parseText(text), std::exception); + BOOST_CHECK_THROW(parseText(text), ParserError); } BOOST_AUTO_TEST_CASE(empty_function)