mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #356 from guanqun/break-not-in-loop
check whether break/continue is in the loop
This commit is contained in:
commit
67c855c583
80
libsolidity/analysis/SyntaxChecker.cpp
Normal file
80
libsolidity/analysis/SyntaxChecker.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
|
||||||
|
bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit)
|
||||||
|
{
|
||||||
|
_sourceUnit.accept(*this);
|
||||||
|
return m_errors.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description)
|
||||||
|
{
|
||||||
|
auto err = make_shared<Error>(Error::Type::SyntaxError);
|
||||||
|
*err <<
|
||||||
|
errinfo_sourceLocation(_location) <<
|
||||||
|
errinfo_comment(_description);
|
||||||
|
|
||||||
|
m_errors.push_back(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyntaxChecker::visit(WhileStatement const&)
|
||||||
|
{
|
||||||
|
m_inLoopDepth++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyntaxChecker::endVisit(WhileStatement const&)
|
||||||
|
{
|
||||||
|
m_inLoopDepth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyntaxChecker::visit(ForStatement const&)
|
||||||
|
{
|
||||||
|
m_inLoopDepth++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyntaxChecker::endVisit(ForStatement const&)
|
||||||
|
{
|
||||||
|
m_inLoopDepth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyntaxChecker::visit(Continue const& _continueStatement)
|
||||||
|
{
|
||||||
|
if (m_inLoopDepth <= 0)
|
||||||
|
// we're not in a for/while loop, report syntax error
|
||||||
|
syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyntaxChecker::visit(Break const& _breakStatement)
|
||||||
|
{
|
||||||
|
if (m_inLoopDepth <= 0)
|
||||||
|
// we're not in a for/while loop, report syntax error
|
||||||
|
syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
61
libsolidity/analysis/SyntaxChecker.h
Normal file
61
libsolidity/analysis/SyntaxChecker.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
|
#include <libsolidity/ast/Types.h>
|
||||||
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module that performs syntax analysis on the AST:
|
||||||
|
* - whether continue/break is in a for/while loop.
|
||||||
|
*/
|
||||||
|
class SyntaxChecker: private ASTConstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @param _errors the reference to the list of errors and warnings to add them found during type checking.
|
||||||
|
SyntaxChecker(ErrorList& _errors): m_errors(_errors) {}
|
||||||
|
|
||||||
|
bool checkSyntax(SourceUnit const& _sourceUnit);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Adds a new error to the list of errors.
|
||||||
|
void syntaxError(SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
|
virtual bool visit(WhileStatement const& _whileStatement) override;
|
||||||
|
virtual void endVisit(WhileStatement const& _whileStatement) override;
|
||||||
|
virtual bool visit(ForStatement const& _forStatement) override;
|
||||||
|
virtual void endVisit(ForStatement const& _forStatement) override;
|
||||||
|
|
||||||
|
virtual bool visit(Continue const& _continueStatement) override;
|
||||||
|
virtual bool visit(Break const& _breakStatement) override;
|
||||||
|
|
||||||
|
ErrorList& m_errors;
|
||||||
|
|
||||||
|
int m_inLoopDepth = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@
|
|||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||||
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <libsolidity/codegen/Compiler.h>
|
#include <libsolidity/codegen/Compiler.h>
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
#include <libsolidity/interface/InterfaceHandler.h>
|
#include <libsolidity/interface/InterfaceHandler.h>
|
||||||
@ -133,6 +134,11 @@ bool CompilerStack::parse()
|
|||||||
resolveImports();
|
resolveImports();
|
||||||
|
|
||||||
bool noErrors = true;
|
bool noErrors = true;
|
||||||
|
SyntaxChecker syntaxChecker(m_errors);
|
||||||
|
for (Source const* source: m_sourceOrder)
|
||||||
|
if (!syntaxChecker.checkSyntax(*source->ast))
|
||||||
|
noErrors = false;
|
||||||
|
|
||||||
DocStringAnalyser docStringAnalyser(m_errors);
|
DocStringAnalyser docStringAnalyser(m_errors);
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
if (!docStringAnalyser.analyseDocStrings(*source->ast))
|
if (!docStringAnalyser.analyseDocStrings(*source->ast))
|
||||||
|
@ -39,6 +39,9 @@ Error::Error(Type _type): m_type(_type)
|
|||||||
case Type::ParserError:
|
case Type::ParserError:
|
||||||
m_typeName = "Parser Error";
|
m_typeName = "Parser Error";
|
||||||
break;
|
break;
|
||||||
|
case Type::SyntaxError:
|
||||||
|
m_typeName = "Syntax Error";
|
||||||
|
break;
|
||||||
case Type::TypeError:
|
case Type::TypeError:
|
||||||
m_typeName = "Type Error";
|
m_typeName = "Type Error";
|
||||||
break;
|
break;
|
||||||
|
@ -47,6 +47,7 @@ public:
|
|||||||
DocstringParsingError,
|
DocstringParsingError,
|
||||||
ParserError,
|
ParserError,
|
||||||
TypeError,
|
TypeError,
|
||||||
|
SyntaxError,
|
||||||
Why3TranslatorError,
|
Why3TranslatorError,
|
||||||
Warning
|
Warning
|
||||||
};
|
};
|
||||||
|
@ -166,18 +166,6 @@ BOOST_AUTO_TEST_CASE(while_loop)
|
|||||||
testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
|
testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(break_outside_loop)
|
|
||||||
{
|
|
||||||
// break and continue outside loops should be simply ignored
|
|
||||||
char const* sourceCode = "contract test {\n"
|
|
||||||
" function f(uint x) returns(uint y) {\n"
|
|
||||||
" break; continue; return 2;\n"
|
|
||||||
" }\n"
|
|
||||||
"}\n";
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
testSolidityAgainstCpp("f(uint256)", [](u256 const&) -> u256 { return 2; }, u256(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(nested_loops)
|
BOOST_AUTO_TEST_CASE(nested_loops)
|
||||||
{
|
{
|
||||||
// tests that break and continue statements in nested loops jump to the correct place
|
// tests that break and continue statements in nested loops jump to the correct place
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
#include <libsolidity/parsing/Parser.h>
|
#include <libsolidity/parsing/Parser.h>
|
||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
#include <libsolidity/analysis/GlobalContext.h>
|
#include <libsolidity/analysis/GlobalContext.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
@ -57,6 +58,10 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false)
|
|||||||
if(!sourceUnit)
|
if(!sourceUnit)
|
||||||
return make_pair(sourceUnit, nullptr);
|
return make_pair(sourceUnit, nullptr);
|
||||||
|
|
||||||
|
SyntaxChecker syntaxChecker(errors);
|
||||||
|
if (!syntaxChecker.checkSyntax(*sourceUnit))
|
||||||
|
return make_pair(sourceUnit, std::make_shared<Error::Type const>(errors[0]->type()));
|
||||||
|
|
||||||
std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>();
|
std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>();
|
||||||
NameAndTypeResolver resolver(globalContext->declarations(), errors);
|
NameAndTypeResolver resolver(globalContext->declarations(), errors);
|
||||||
solAssert(Error::containsOnlyWarnings(errors), "");
|
solAssert(Error::containsOnlyWarnings(errors), "");
|
||||||
@ -2914,6 +2919,47 @@ BOOST_AUTO_TEST_CASE(lvalues_as_inline_array)
|
|||||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(break_not_in_loop)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
if (true)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(continue_not_in_loop)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
if (true)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(continue_not_in_loop_2)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user