diff options
author | Daniel Kirchner <daniel@ekpyron.org> | 2019-01-09 02:33:46 +0800 |
---|---|---|
committer | Daniel Kirchner <daniel@ekpyron.org> | 2019-01-10 17:36:50 +0800 |
commit | 0dfd4a726eb7e6fa8a5e886a0d80bb5bf3d9b7dc (patch) | |
tree | 8457ac70f7e9921d9825e939f2ff23ffc1259a46 | |
parent | 63319cfdcd7668a75caaacd0d8f3a83a62c31525 (diff) | |
download | dexon-solidity-0dfd4a726eb7e6fa8a5e886a0d80bb5bf3d9b7dc.tar.gz dexon-solidity-0dfd4a726eb7e6fa8a5e886a0d80bb5bf3d9b7dc.tar.zst dexon-solidity-0dfd4a726eb7e6fa8a5e886a0d80bb5bf3d9b7dc.zip |
Warn about unreachable code.
27 files changed, 258 insertions, 12 deletions
diff --git a/Changelog.md b/Changelog.md index 23e394c6..bb7b191f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Control Flow Graph: Warn about unreachable code. Bugfixes: diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h index 7fe2472d..34a4dfe5 100644 --- a/libdevcore/Algorithms.h +++ b/libdevcore/Algorithms.h @@ -75,4 +75,47 @@ private: V const* m_firstCycleVertex = nullptr; }; +/** + * Generic breadth first search. + * + * Example: Gather all (recursive) children in a graph starting at (and including) ``root``: + * + * Node const* root = ...; + * std::set<Node> allNodes = BreadthFirstSearch<Node>{{root}}.run([](Node const& _node, auto&& _addChild) { + * // Potentially process ``_node``. + * for (Node const& _child: _node.children()) + * // Potentially filter the children to be visited. + * _addChild(_child); + * }).visited; + * + * Note that the order of the traversal is *non-deterministic* (the children are stored in a std::set of pointers). + */ +template<typename V> +struct BreadthFirstSearch +{ + /// Runs the breadth first search. The verticesToTraverse member of the struct needs to be initialized. + /// @param _forEachChild is a callable of the form [...](V const& _node, auto&& _addChild) { ... } + /// that is called for each visited node and is supposed to call _addChild(childNode) for every child + /// node of _node. + template<typename ForEachChild> + BreadthFirstSearch& run(ForEachChild&& _forEachChild) + { + while (!verticesToTraverse.empty()) + { + V const* v = *verticesToTraverse.begin(); + verticesToTraverse.erase(verticesToTraverse.begin()); + visited.insert(v); + + _forEachChild(*v, [this](V const& _vertex) { + if (!visited.count(&_vertex)) + verticesToTraverse.insert(&_vertex); + }); + } + return *this; + } + + std::set<V const*> verticesToTraverse; + std::set<V const*> visited{}; +}; + } diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 840891c2..c461909f 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -49,6 +49,26 @@ struct SourceLocation bool isEmpty() const { return start == -1 && end == -1; } + /// @returns the smallest SourceLocation that contains both @param _a and @param _b. + /// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one + /// is unset, the source of the other will be used for the result, even if that is unset as well). + /// Invalid start and end positions (with value of -1) are ignored (if start or end are -1 for both @param _a and + /// @param _b, then start resp. end of the result will be -1 as well). + static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b) + { + if (!_a.source) + _a.source = _b.source; + + if (_a.start < 0) + _a.start = _b.start; + else if (_b.start >= 0 && _b.start < _a.start) + _a.start = _b.start; + if (_b.end > _a.end) + _a.end = _b.end; + + return _a; + } + int start = -1; int end = -1; std::shared_ptr<CharStream> source; diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 3adf6318..b801547f 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -18,6 +18,7 @@ #include <libsolidity/analysis/ControlFlowAnalyzer.h> #include <liblangutil/SourceLocation.h> +#include <libdevcore/Algorithms.h> #include <boost/range/algorithm/sort.hpp> using namespace std; @@ -36,6 +37,7 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) { auto const& functionFlow = m_cfg.functionFlow(_function); checkUninitializedAccess(functionFlow.entry, functionFlow.exit); + checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert); } return false; } @@ -145,3 +147,35 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod } } } + +void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const +{ + // collect all nodes reachable from the entry point + std::set<CFGNode const*> reachable = BreadthFirstSearch<CFGNode>{{_entry}}.run( + [](CFGNode const& _node, auto&& _addChild) { + for (CFGNode const* exit: _node.exits) + _addChild(*exit); + } + ).visited; + + // traverse all paths backwards from exit and revert + // and extract (valid) source locations of unreachable nodes into sorted set + std::set<SourceLocation> unreachable; + BreadthFirstSearch<CFGNode>{{_exit, _revert}}.run( + [&](CFGNode const& _node, auto&& _addChild) { + if (!reachable.count(&_node) && !_node.location.isEmpty()) + unreachable.insert(_node.location); + for (CFGNode const* entry: _node.entries) + _addChild(*entry); + } + ); + + for (auto it = unreachable.begin(); it != unreachable.end();) + { + SourceLocation location = *it++; + // Extend the location, as long as the next location overlaps (unreachable is sorted). + for (; it != unreachable.end() && it->start <= location.end; ++it) + location.end = std::max(location.end, it->end); + m_errorReporter.warning(location, "Unreachable code."); + } +} diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index 7761817a..e1585740 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -38,6 +38,9 @@ public: private: /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit. void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const; + /// Checks for unreachable code, i.e. code ending in @param _exit or @param _revert + /// that can not be reached from @param _entry. + void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const; CFG const& m_cfg; langutil::ErrorReporter& m_errorReporter; diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 3dab8b16..66664245 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -18,6 +18,7 @@ #include <libsolidity/analysis/ControlFlowBuilder.h> using namespace dev; +using namespace langutil; using namespace solidity; using namespace std; @@ -53,6 +54,7 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation) case Token::Or: case Token::And: { + visitNode(_operation); appendControlFlow(_operation.leftExpression()); auto nodes = splitFlow<2>(); @@ -62,14 +64,14 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation) return false; } default: - break; + return ASTConstVisitor::visit(_operation); } - return ASTConstVisitor::visit(_operation); } bool ControlFlowBuilder::visit(Conditional const& _conditional) { solAssert(!!m_currentNode, ""); + visitNode(_conditional); _conditional.condition().accept(*this); @@ -86,6 +88,7 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional) bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) { solAssert(!!m_currentNode, ""); + visitNode(_ifStatement); _ifStatement.condition().accept(*this); @@ -106,6 +109,7 @@ bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) bool ControlFlowBuilder::visit(ForStatement const& _forStatement) { solAssert(!!m_currentNode, ""); + visitNode(_forStatement); if (_forStatement.initializationExpression()) _forStatement.initializationExpression()->accept(*this); @@ -139,6 +143,7 @@ bool ControlFlowBuilder::visit(ForStatement const& _forStatement) bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) { solAssert(!!m_currentNode, ""); + visitNode(_whileStatement); if (_whileStatement.isDoWhile()) { @@ -183,28 +188,31 @@ bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) return false; } -bool ControlFlowBuilder::visit(Break const&) +bool ControlFlowBuilder::visit(Break const& _break) { solAssert(!!m_currentNode, ""); solAssert(!!m_breakJump, ""); + visitNode(_break); connect(m_currentNode, m_breakJump); m_currentNode = newLabel(); return false; } -bool ControlFlowBuilder::visit(Continue const&) +bool ControlFlowBuilder::visit(Continue const& _continue) { solAssert(!!m_currentNode, ""); solAssert(!!m_continueJump, ""); + visitNode(_continue); connect(m_currentNode, m_continueJump); m_currentNode = newLabel(); return false; } -bool ControlFlowBuilder::visit(Throw const&) +bool ControlFlowBuilder::visit(Throw const& _throw) { solAssert(!!m_currentNode, ""); solAssert(!!m_revertNode, ""); + visitNode(_throw); connect(m_currentNode, m_revertNode); m_currentNode = newLabel(); return false; @@ -232,6 +240,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) { case FunctionType::Kind::Revert: solAssert(!!m_revertNode, ""); + visitNode(_functionCall); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_revertNode); @@ -241,6 +250,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) case FunctionType::Kind::Assert: { solAssert(!!m_revertNode, ""); + visitNode(_functionCall); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_revertNode); @@ -314,6 +324,7 @@ bool ControlFlowBuilder::visit(Return const& _return) { solAssert(!!m_currentNode, ""); solAssert(!!m_returnNode, ""); + visitNode(_return); if (_return.expression()) { appendControlFlow(*_return.expression()); @@ -327,11 +338,12 @@ bool ControlFlowBuilder::visit(Return const& _return) } connect(m_currentNode, m_returnNode); m_currentNode = newLabel(); - return true; + return false; } -bool ControlFlowBuilder::visit(FunctionTypeName const&) +bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName) { + visitNode(_functionTypeName); // Do not visit the parameters and return values of a function type name. // We do not want to consider them as variable declarations for the control flow graph. return false; @@ -340,6 +352,7 @@ bool ControlFlowBuilder::visit(FunctionTypeName const&) bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) { solAssert(!!m_currentNode, ""); + visitNode(_inlineAssembly); for (auto const& ref: _inlineAssembly.annotation().externalReferences) { if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration)) @@ -355,6 +368,7 @@ bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) { solAssert(!!m_currentNode, ""); + visitNode(_variableDeclaration); m_currentNode->variableOccurrences.emplace_back( _variableDeclaration, @@ -382,6 +396,7 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { solAssert(!!m_currentNode, ""); + visitNode(_variableDeclarationStatement); for (auto const& var: _variableDeclarationStatement.declarations()) if (var) @@ -417,6 +432,7 @@ bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDecl bool ControlFlowBuilder::visit(Identifier const& _identifier) { solAssert(!!m_currentNode, ""); + visitNode(_identifier); if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) m_currentNode->variableOccurrences.emplace_back( @@ -430,7 +446,12 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier) return true; } - +bool ControlFlowBuilder::visitNode(ASTNode const& _node) +{ + solAssert(!!m_currentNode, ""); + m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, _node.location()); + return true; +} void ControlFlowBuilder::appendControlFlow(ASTNode const& _node) { diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index f196e5fc..b1748395 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -66,6 +66,11 @@ private: bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(Identifier const& _identifier) override; +protected: + bool visitNode(ASTNode const&) override; + +private: + /// Appends the control flow of @a _node to the current control flow. void appendControlFlow(ASTNode const& _node); @@ -77,9 +82,6 @@ private: /// Creates an arc from @a _from to @a _to. static void connect(CFGNode* _from, CFGNode* _to); - -private: - /// Splits the control flow starting at the current node into n paths. /// m_currentNode is set to nullptr and has to be set manually or /// using mergeFlow later. diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index cc0113d8..a55b96e8 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -20,6 +20,7 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ASTVisitor.h> #include <liblangutil/ErrorReporter.h> +#include <liblangutil/SourceLocation.h> #include <map> #include <memory> @@ -98,6 +99,8 @@ struct CFGNode /// Variable occurrences in the node. std::vector<VariableOccurrence> variableOccurrences; + // Source location of this control flow block. + langutil::SourceLocation location; }; /** Describes the control flow of a function. */ diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol index b868d61d..c7e3eacd 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol @@ -46,7 +46,11 @@ contract C { } // ---- // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (146-151): Unreachable code. +// Warning: (169-174): Unreachable code. // TypeError: (223-234): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (316-321): Unreachable code. // TypeError: (440-451): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (654-665): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (871-882): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (933-938): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol index 55c5edd3..5a113668 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol @@ -29,3 +29,4 @@ contract C { } } // ---- +// Warning: (567-572): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol index 96767402..da7f1a90 100644 --- a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol +++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol @@ -5,4 +5,6 @@ contract C { revert(); b; } -}
\ No newline at end of file +} +// ---- +// Warning: (125-126): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol index b941ad34..9ebd0321 100644 --- a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol +++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol @@ -8,3 +8,4 @@ contract C { } } // ---- +// Warning: (112-135): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol new file mode 100644 index 00000000..17119ca6 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + return; + // unreachable comment + } +} diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol new file mode 100644 index 00000000..e00398bc --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + if (false) { + return; // unreachable, but not yet detected + } + return; + } +} diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol new file mode 100644 index 00000000..363c53e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + do { + uint a = 42; a; + continue; + return; // this is unreachable + } while(false); + return; // this is still reachable + } +} +// ---- +// Warning: (119-126): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol new file mode 100644 index 00000000..9b755347 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint) { + return 0; + return 0; + } +} +// ---- +// Warning: (85-93): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol new file mode 100644 index 00000000..a6457e4f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + revert(); + } +} +// ---- +// Warning: (70-78): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol new file mode 100644 index 00000000..496addb2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + for (uint a = 0; a < 1; a++) { + break; + uint b = 42; b; + } + return; + } +} +// ---- +// Warning: (76-79): Unreachable code. +// Warning: (114-128): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol new file mode 100644 index 00000000..3513b17d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol @@ -0,0 +1,12 @@ +contract C { + function f(bool c) public pure { + if (c) { + return; + } else { + return; + } + return; // unreachable + } +} +// ---- +// Warning: (142-149): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol new file mode 100644 index 00000000..9bb6a41c --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + uint a = 0; a; + } +} +// ---- +// Warning: (70-83): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol new file mode 100644 index 00000000..4c80c5ca --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + for(int i = 0; i < 3; i++) { f(); } + } +} +// ---- +// Warning: (70-105): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol new file mode 100644 index 00000000..2d1ddd4f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + uint a = 0; + while (a < 100) { + a++; + break; + a--; + } + } +} +// ---- +// Warning: (138-141): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol new file mode 100644 index 00000000..55f98f67 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + while(true) { + continue; + return; + } + return; // this is unreachable as well, but currently undetected (needs to consider constant condition "true") + } +} +// ---- +// Warning: (100-107): Unreachable code. diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol index fce669dd..9fafd706 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol @@ -7,6 +7,8 @@ contract test { } } // ---- +// Warning: (103-106): Unreachable code. +// Warning: (144-152): Unreachable code. // Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (122-131): Unused local variable. // Warning: (20-169): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol index 4adf0948..c36f9c58 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol @@ -7,6 +7,7 @@ contract test { } } // ---- +// Warning: (144-152): Unreachable code. // Warning: (37-46): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (122-131): Unused local variable. // Warning: (24-177): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol index c22ae42f..c81f611b 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol @@ -6,6 +6,8 @@ contract test { } } // ---- +// Warning: (89-92): Unreachable code. +// Warning: (130-138): Unreachable code. // Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (108-117): Unused local variable. // Warning: (20-155): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/while_loop.sol b/test/libsolidity/syntaxTests/parsing/while_loop.sol index dbb00a69..cdac929f 100644 --- a/test/libsolidity/syntaxTests/parsing/while_loop.sol +++ b/test/libsolidity/syntaxTests/parsing/while_loop.sol @@ -5,3 +5,4 @@ contract test { } } // ---- +// Warning: (105-113): Unreachable code. |