|
22 | 22 | #include <libsolutil/Algorithms.h> |
23 | 23 | #include <boost/range/algorithm/sort.hpp> |
24 | 24 |
|
| 25 | +#include <functional> |
| 26 | + |
25 | 27 | using namespace std; |
| 28 | +using namespace std::placeholders; |
26 | 29 | using namespace solidity::langutil; |
27 | 30 | using namespace solidity::frontend; |
28 | 31 |
|
29 | | -bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot) |
| 32 | + |
| 33 | +bool ControlFlowAnalyzer::run() |
30 | 34 | { |
31 | | - _astRoot.accept(*this); |
| 35 | + for (auto& [pair, flow]: m_cfg.allFunctionFlows()) |
| 36 | + analyze(*pair.function, pair.contract, *flow); |
| 37 | + |
32 | 38 | return Error::containsOnlyWarnings(m_errorReporter.errors()); |
33 | 39 | } |
34 | 40 |
|
35 | | -bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) |
| 41 | +void ControlFlowAnalyzer::analyze(FunctionDefinition const& _function, ContractDefinition const* _contract, FunctionFlow const& _flow) |
36 | 42 | { |
37 | | - if (_function.isImplemented()) |
38 | | - { |
39 | | - auto const& functionFlow = m_cfg.functionFlow(_function); |
40 | | - checkUninitializedAccess(functionFlow.entry, functionFlow.exit, _function.body().statements().empty()); |
41 | | - checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert, functionFlow.transactionReturn); |
42 | | - } |
43 | | - return false; |
| 43 | + if (!_function.isImplemented()) |
| 44 | + return; |
| 45 | + |
| 46 | + optional<string> mostDerivedContractName; |
| 47 | + |
| 48 | + // The name of the most derived contract only required if it differs from |
| 49 | + // the functions contract |
| 50 | + if (_contract && _contract != _function.annotation().contract) |
| 51 | + mostDerivedContractName = _contract->name(); |
| 52 | + |
| 53 | + checkUninitializedAccess( |
| 54 | + _flow.entry, |
| 55 | + _flow.exit, |
| 56 | + _function.body().statements().empty(), |
| 57 | + mostDerivedContractName |
| 58 | + ); |
| 59 | + checkUnreachable(_flow.entry, _flow.exit, _flow.revert, _flow.transactionReturn); |
44 | 60 | } |
45 | 61 |
|
46 | | -void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody) const |
| 62 | + |
| 63 | +void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody, optional<string> _contractName) |
47 | 64 | { |
48 | 65 | struct NodeInfo |
49 | 66 | { |
@@ -156,16 +173,27 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod |
156 | 173 | " without prior assignment, which would lead to undefined behaviour." |
157 | 174 | ); |
158 | 175 | else if (!_emptyBody && varDecl.name().empty()) |
| 176 | + { |
| 177 | + if (!m_unassignedReturnVarsAlreadyWarnedFor.emplace(&varDecl).second) |
| 178 | + continue; |
| 179 | + |
159 | 180 | m_errorReporter.warning( |
160 | 181 | 6321_error, |
161 | 182 | varDecl.location(), |
162 | | - "Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable." |
| 183 | + "Unnamed return variable can remain unassigned" + |
| 184 | + ( |
| 185 | + _contractName.has_value() ? |
| 186 | + " when the function is called when \"" + _contractName.value() + "\" is the most derived contract." : |
| 187 | + "." |
| 188 | + ) + |
| 189 | + " Add an explicit return with value to all non-reverting code paths or name the variable." |
163 | 190 | ); |
| 191 | + } |
164 | 192 | } |
165 | 193 | } |
166 | 194 | } |
167 | 195 |
|
168 | | -void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const |
| 196 | +void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) |
169 | 197 | { |
170 | 198 | // collect all nodes reachable from the entry point |
171 | 199 | std::set<CFGNode const*> reachable = util::BreadthFirstSearch<CFGNode const*>{{_entry}}.run( |
@@ -193,6 +221,8 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* |
193 | 221 | // Extend the location, as long as the next location overlaps (unreachable is sorted). |
194 | 222 | for (; it != unreachable.end() && it->start <= location.end; ++it) |
195 | 223 | location.end = std::max(location.end, it->end); |
196 | | - m_errorReporter.warning(5740_error, location, "Unreachable code."); |
| 224 | + |
| 225 | + if (m_unreachableLocationsAlreadyWarnedFor.emplace(location).second) |
| 226 | + m_errorReporter.warning(5740_error, location, "Unreachable code."); |
197 | 227 | } |
198 | 228 | } |
0 commit comments