2020-02-13 16:27:06 +01:00
|
|
|
#include "forwardanalyzer.h"
|
|
|
|
#include "astutils.h"
|
|
|
|
#include "settings.h"
|
|
|
|
#include "symboldatabase.h"
|
2020-03-20 10:37:16 +01:00
|
|
|
#include "token.h"
|
2020-04-13 13:44:48 +02:00
|
|
|
#include "valueptr.h"
|
2020-02-13 16:27:06 +01:00
|
|
|
|
2020-05-10 20:32:59 +02:00
|
|
|
#include <algorithm>
|
2020-02-15 07:57:43 +01:00
|
|
|
#include <functional>
|
|
|
|
|
2020-02-13 16:27:06 +01:00
|
|
|
struct ForwardTraversal {
|
|
|
|
enum class Progress { Continue, Break, Skip };
|
2020-09-11 00:06:49 +02:00
|
|
|
ForwardTraversal(const ValuePtr<ForwardAnalyzer>& analyzer, const Settings* settings)
|
2020-09-11 08:51:12 +02:00
|
|
|
: analyzer(analyzer), settings(settings), actions(ForwardAnalyzer::Action::None), analyzeOnly(false)
|
2020-09-11 00:06:49 +02:00
|
|
|
{}
|
2020-02-13 16:27:06 +01:00
|
|
|
ValuePtr<ForwardAnalyzer> analyzer;
|
|
|
|
const Settings* settings;
|
2020-09-10 04:29:26 +02:00
|
|
|
ForwardAnalyzer::Action actions;
|
2020-09-11 00:06:49 +02:00
|
|
|
bool analyzeOnly;
|
|
|
|
|
2020-09-11 00:07:13 +02:00
|
|
|
bool stopUpdates() {
|
2020-09-11 00:06:49 +02:00
|
|
|
analyzeOnly = true;
|
|
|
|
return actions.isModified();
|
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
std::pair<bool, bool> evalCond(const Token* tok) {
|
2020-02-13 16:27:06 +01:00
|
|
|
std::vector<int> result = analyzer->evaluate(tok);
|
2020-02-13 17:04:05 +01:00
|
|
|
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) {
|
|
|
|
return x;
|
|
|
|
});
|
|
|
|
bool checkElse = std::any_of(result.begin(), result.end(), [](int x) {
|
|
|
|
return !x;
|
|
|
|
});
|
2020-02-13 16:27:06 +01:00
|
|
|
return std::make_pair(checkThen, checkElse);
|
|
|
|
}
|
|
|
|
|
2020-02-15 09:14:14 +01:00
|
|
|
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
|
|
|
Progress traverseTok(T* tok, std::function<Progress(T*)> f, bool traverseUnknown, T** out = nullptr) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (Token::Match(tok, "asm|goto|continue|setjmp|longjmp"))
|
|
|
|
return Progress::Break;
|
|
|
|
else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) {
|
2020-02-15 07:57:43 +01:00
|
|
|
traverseRecursive(tok->astOperand1(), f, traverseUnknown);
|
|
|
|
traverseRecursive(tok->astOperand2(), f, traverseUnknown);
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
} else if (isUnevaluated(tok)) {
|
|
|
|
if (out)
|
|
|
|
*out = tok->link();
|
|
|
|
return Progress::Skip;
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (tok->astOperand1() && tok->astOperand2() && Token::Match(tok, "?|&&|%oror%")) {
|
2020-02-15 07:57:43 +01:00
|
|
|
if (traverseConditional(tok, f, traverseUnknown) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
if (out)
|
|
|
|
*out = nextAfterAstRightmostLeaf(tok);
|
|
|
|
return Progress::Skip;
|
|
|
|
// Skip lambdas
|
2020-02-15 07:57:43 +01:00
|
|
|
} else if (T* lambdaEndToken = findLambdaEndToken(tok)) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (checkScope(lambdaEndToken).isModified())
|
|
|
|
return Progress::Break;
|
|
|
|
if (out)
|
2020-06-13 10:26:54 +02:00
|
|
|
*out = lambdaEndToken->next();
|
2020-03-22 10:12:53 +01:00
|
|
|
// Skip class scope
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (tok->str() == "{" && tok->scope() && tok->scope()->isClassOrStruct()) {
|
2020-03-22 10:12:53 +01:00
|
|
|
if (out)
|
|
|
|
*out = tok->link();
|
2020-02-13 16:27:06 +01:00
|
|
|
} else {
|
2020-02-15 07:57:43 +01:00
|
|
|
if (f(tok) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
}
|
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
2020-02-15 09:14:14 +01:00
|
|
|
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
2020-04-09 12:11:33 +02:00
|
|
|
Progress traverseRecursive(T* tok, std::function<Progress(T*)> f, bool traverseUnknown, unsigned int recursion=0) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!tok)
|
|
|
|
return Progress::Continue;
|
2020-04-09 12:11:33 +02:00
|
|
|
if (recursion > 10000)
|
|
|
|
return Progress::Skip;
|
2020-09-03 07:17:36 +02:00
|
|
|
T* firstOp = tok->astOperand1();
|
|
|
|
T* secondOp = tok->astOperand2();
|
|
|
|
// Evaluate RHS of assignment before LHS
|
|
|
|
if (tok->isAssignmentOp())
|
|
|
|
std::swap(firstOp, secondOp);
|
|
|
|
if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
2020-09-03 07:17:36 +02:00
|
|
|
Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown);
|
2020-02-13 16:27:06 +01:00
|
|
|
if (p == Progress::Break)
|
|
|
|
return Progress::Break;
|
2020-09-03 07:17:36 +02:00
|
|
|
if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
template<class T, class F, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
|
|
|
Progress traverseConditional(T* tok, F f, bool traverseUnknown) {
|
2020-04-09 14:38:10 +02:00
|
|
|
if (Token::Match(tok, "?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) {
|
2020-02-15 07:57:43 +01:00
|
|
|
T* condTok = tok->astOperand1();
|
|
|
|
T* childTok = tok->astOperand2();
|
2020-02-13 16:27:06 +01:00
|
|
|
bool checkThen, checkElse;
|
|
|
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
|
|
|
if (!checkThen && !checkElse) {
|
|
|
|
// Stop if the value is conditional
|
2020-09-11 00:06:49 +02:00
|
|
|
if (!traverseUnknown && analyzer->isConditional() && stopUpdates())
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
checkThen = true;
|
|
|
|
checkElse = true;
|
|
|
|
}
|
2020-05-19 21:03:28 +02:00
|
|
|
if (childTok->str() == ":") {
|
2020-02-15 07:57:43 +01:00
|
|
|
if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
2020-02-15 07:57:43 +01:00
|
|
|
if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
} else {
|
2020-05-19 21:03:28 +02:00
|
|
|
if (!checkThen && tok->str() == "&&")
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Continue;
|
2020-05-19 21:03:28 +02:00
|
|
|
if (!checkElse && tok->str() == "||")
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Continue;
|
2020-02-15 07:57:43 +01:00
|
|
|
if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
Progress update(Token* tok) {
|
|
|
|
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
2020-09-10 04:29:26 +02:00
|
|
|
actions |= action;
|
2020-09-11 00:06:49 +02:00
|
|
|
if (!action.isNone() && !analyzeOnly)
|
2020-02-15 07:57:43 +01:00
|
|
|
analyzer->update(tok, action);
|
2020-05-22 22:57:20 +02:00
|
|
|
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
|
|
|
return Progress::Break;
|
2020-02-15 07:57:43 +01:00
|
|
|
if (action.isInvalid())
|
|
|
|
return Progress::Break;
|
2020-06-08 21:17:12 +02:00
|
|
|
if (action.isWrite() && !action.isRead())
|
|
|
|
// Analysis of this write will continue separately
|
|
|
|
return Progress::Break;
|
2020-02-15 07:57:43 +01:00
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Progress updateTok(Token* tok, Token** out = nullptr) {
|
2020-02-15 09:14:14 +01:00
|
|
|
std::function<Progress(Token*)> f = [this](Token* tok2) {
|
|
|
|
return update(tok2);
|
|
|
|
};
|
|
|
|
return traverseTok(tok, f, false, out);
|
2020-02-15 07:57:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Progress updateRecursive(Token* tok) {
|
2020-02-15 09:14:14 +01:00
|
|
|
std::function<Progress(Token*)> f = [this](Token* tok2) {
|
|
|
|
return update(tok2);
|
|
|
|
};
|
|
|
|
return traverseRecursive(tok, f, false);
|
2020-02-15 07:57:43 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 09:20:49 +01:00
|
|
|
template <class T>
|
|
|
|
T* findRange(T* start, const Token* end, std::function<bool(ForwardAnalyzer::Action)> pred) {
|
2020-02-13 16:27:06 +01:00
|
|
|
for (T* tok = start; tok && tok != end; tok = tok->next()) {
|
|
|
|
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
|
|
|
if (pred(action))
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
ForwardAnalyzer::Action analyzeRecursive(const Token* start) {
|
|
|
|
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
2020-02-15 09:14:14 +01:00
|
|
|
std::function<Progress(const Token *)> f = [&](const Token* tok) {
|
2020-02-15 07:57:43 +01:00
|
|
|
result = analyzer->analyze(tok);
|
|
|
|
if (result.isModified() || result.isInconclusive())
|
|
|
|
return Progress::Break;
|
|
|
|
return Progress::Continue;
|
|
|
|
};
|
|
|
|
traverseRecursive(start, f, true);
|
|
|
|
return result;
|
2020-02-13 16:27:06 +01:00
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
ForwardAnalyzer::Action analyzeRange(const Token* start, const Token* end) {
|
2020-02-13 16:27:06 +01:00
|
|
|
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
|
|
|
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
|
|
|
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
|
|
|
if (action.isModified() || action.isInconclusive())
|
|
|
|
return action;
|
|
|
|
result = action;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
void forkScope(Token* endBlock, bool isModified = false) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (analyzer->updateScope(endBlock, isModified)) {
|
|
|
|
ForwardTraversal ft = *this;
|
|
|
|
ft.updateRange(endBlock->link(), endBlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static bool hasGoto(const Token* endBlock) {
|
|
|
|
return Token::findsimplematch(endBlock->link(), "goto", endBlock);
|
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
bool isEscapeScope(const Token* endBlock, bool unknown = false) {
|
2020-02-13 16:27:06 +01:00
|
|
|
const Token* ftok = nullptr;
|
|
|
|
bool r = isReturnScope(endBlock, &settings->library, &ftok);
|
|
|
|
if (!r && ftok)
|
|
|
|
return unknown;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class Status {
|
|
|
|
None,
|
|
|
|
Escaped,
|
|
|
|
Modified,
|
|
|
|
Inconclusive,
|
|
|
|
};
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
ForwardAnalyzer::Action analyzeScope(const Token* endBlock) {
|
|
|
|
return analyzeRange(endBlock->link(), endBlock);
|
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
ForwardAnalyzer::Action checkScope(Token* endBlock) {
|
2020-02-13 16:27:06 +01:00
|
|
|
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
|
|
|
forkScope(endBlock, a.isModified());
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
ForwardAnalyzer::Action checkScope(const Token* endBlock) {
|
2020-02-13 16:27:06 +01:00
|
|
|
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
2020-02-15 07:57:43 +01:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
Progress updateLoop(Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) {
|
2020-08-26 07:02:15 +02:00
|
|
|
const bool isDoWhile = precedes(endBlock, condTok);
|
2020-02-15 07:57:43 +01:00
|
|
|
ForwardAnalyzer::Action bodyAnalysis = analyzeScope(endBlock);
|
|
|
|
ForwardAnalyzer::Action allAnalysis = bodyAnalysis;
|
2020-08-24 13:10:36 +02:00
|
|
|
if (condTok)
|
|
|
|
allAnalysis |= analyzeRecursive(condTok);
|
2020-02-15 07:57:43 +01:00
|
|
|
if (initTok)
|
|
|
|
allAnalysis |= analyzeRecursive(initTok);
|
|
|
|
if (stepTok)
|
|
|
|
allAnalysis |= analyzeRecursive(stepTok);
|
2020-09-10 04:29:26 +02:00
|
|
|
actions |= allAnalysis;
|
2020-02-15 07:57:43 +01:00
|
|
|
if (allAnalysis.isInconclusive()) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!analyzer->lowerToInconclusive())
|
|
|
|
return Progress::Break;
|
2020-02-15 07:57:43 +01:00
|
|
|
} else if (allAnalysis.isModified()) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
|
|
|
}
|
|
|
|
// Traverse condition after lowering
|
2020-08-26 07:02:15 +02:00
|
|
|
if (condTok && (!isDoWhile || !bodyAnalysis.isModified())) {
|
2020-06-05 18:06:03 +02:00
|
|
|
if (updateRecursive(condTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
|
|
|
|
bool checkThen, checkElse;
|
|
|
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
|
|
|
if (checkElse)
|
|
|
|
// condition is false, we don't enter the loop
|
|
|
|
return Progress::Break;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:57:43 +01:00
|
|
|
forkScope(endBlock, allAnalysis.isModified());
|
|
|
|
if (bodyAnalysis.isModified()) {
|
2020-02-13 16:27:06 +01:00
|
|
|
Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&ForwardAnalyzer::Action::isModified));
|
|
|
|
const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock);
|
|
|
|
if (!Token::Match(nextStatement, ";|} break ;"))
|
|
|
|
return Progress::Break;
|
2020-02-15 07:57:43 +01:00
|
|
|
} else {
|
|
|
|
if (stepTok && updateRecursive(stepTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
2020-02-13 16:27:06 +01:00
|
|
|
}
|
2020-02-17 18:28:58 +01:00
|
|
|
// TODO: Should we traverse the body?
|
2020-02-13 16:27:06 +01:00
|
|
|
// updateRange(endBlock->link(), endBlock);
|
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
Progress updateRange(Token* start, const Token* end) {
|
2020-02-13 16:27:06 +01:00
|
|
|
for (Token* tok = start; tok && tok != end; tok = tok->next()) {
|
|
|
|
Token* next = nullptr;
|
|
|
|
|
2020-05-18 08:00:01 +02:00
|
|
|
if (tok->link()) {
|
|
|
|
// Skip casts..
|
|
|
|
if (tok->str() == "(" && !tok->astOperand2() && tok->isCast()) {
|
|
|
|
tok = tok->link();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Skip template arguments..
|
|
|
|
if (tok->str() == "<") {
|
|
|
|
tok = tok->link();
|
|
|
|
continue;
|
|
|
|
}
|
2020-03-10 10:35:49 +01:00
|
|
|
}
|
2020-03-08 11:49:14 +01:00
|
|
|
|
2020-02-13 16:27:06 +01:00
|
|
|
// Evaluate RHS of assignment before LHS
|
|
|
|
if (Token* assignTok = assignExpr(tok)) {
|
2020-09-03 07:17:36 +02:00
|
|
|
if (updateRecursive(assignTok) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
tok = nextAfterAstRightmostLeaf(assignTok);
|
|
|
|
if (!tok)
|
|
|
|
return Progress::Break;
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (tok->str() == "break") {
|
2020-02-13 16:27:06 +01:00
|
|
|
const Scope* scope = findBreakScope(tok->scope());
|
|
|
|
if (!scope)
|
|
|
|
return Progress::Break;
|
|
|
|
tok = skipTo(tok, scope->bodyEnd, end);
|
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
2020-02-17 18:28:58 +01:00
|
|
|
// TODO: Don't break, instead move to the outer scope
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!tok)
|
|
|
|
return Progress::Break;
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (Token::Match(tok, "%name% :") || tok->str() == "case") {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (tok->link() && tok->str() == "}") {
|
2020-06-13 10:26:54 +02:00
|
|
|
const Scope* scope = tok->scope();
|
2020-06-14 10:06:54 +02:00
|
|
|
if (!scope)
|
2020-06-13 10:26:54 +02:00
|
|
|
return Progress::Break;
|
2020-05-18 08:00:01 +02:00
|
|
|
if (Token::Match(tok->link()->previous(), ")|else {")) {
|
|
|
|
const bool inElse = Token::simpleMatch(tok->link()->previous(), "else {");
|
2020-09-03 07:17:36 +02:00
|
|
|
Token* condTok = getCondTokFromEnd(tok);
|
2020-05-18 08:00:01 +02:00
|
|
|
if (!condTok)
|
|
|
|
return Progress::Break;
|
|
|
|
if (!condTok->hasKnownIntValue()) {
|
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
2020-06-09 08:16:53 +02:00
|
|
|
} else if (condTok->values().front().intvalue == inElse) {
|
2020-05-18 08:00:01 +02:00
|
|
|
return Progress::Break;
|
|
|
|
}
|
2020-09-03 07:17:36 +02:00
|
|
|
// Handle for loop
|
|
|
|
Token* stepTok = getStepTokFromEnd(tok);
|
|
|
|
bool checkThen, checkElse;
|
|
|
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
|
|
|
if (stepTok && !checkElse) {
|
|
|
|
if (updateRecursive(stepTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
if (updateRecursive(condTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
}
|
2020-05-18 08:00:01 +02:00
|
|
|
analyzer->assume(condTok, !inElse, tok);
|
|
|
|
if (Token::simpleMatch(tok, "} else {"))
|
|
|
|
tok = tok->linkAt(2);
|
2020-06-13 10:26:54 +02:00
|
|
|
} else if (scope->type == Scope::eTry) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
2020-06-13 10:26:54 +02:00
|
|
|
} else if (scope->type == Scope::eLambda) {
|
|
|
|
return Progress::Break;
|
2020-07-24 08:12:36 +02:00
|
|
|
} else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) {
|
|
|
|
if (updateLoop(tok, tok->tokAt(2)->astOperand2()) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
tok = tok->linkAt(2);
|
2020-05-18 08:00:01 +02:00
|
|
|
} else if (Token::simpleMatch(tok->next(), "else {")) {
|
2020-02-13 16:27:06 +01:00
|
|
|
tok = tok->linkAt(2);
|
2020-05-18 08:00:01 +02:00
|
|
|
}
|
|
|
|
} else if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) {
|
2020-02-13 16:27:06 +01:00
|
|
|
Token* endCond = tok->next()->link();
|
|
|
|
Token* endBlock = endCond->next()->link();
|
|
|
|
Token* condTok = getCondTok(tok);
|
|
|
|
Token* initTok = getInitTok(tok);
|
|
|
|
if (!condTok)
|
|
|
|
return Progress::Break;
|
|
|
|
if (initTok && updateRecursive(initTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
if (Token::Match(tok, "for|while (")) {
|
2020-08-22 09:16:26 +02:00
|
|
|
// For-range loop
|
|
|
|
if (Token::simpleMatch(condTok, ":")) {
|
|
|
|
Token* conTok = condTok->astOperand2();
|
|
|
|
if (conTok && updateRecursive(conTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
if (updateLoop(endBlock, condTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
} else {
|
|
|
|
Token* stepTok = getStepTok(tok);
|
|
|
|
if (updateLoop(endBlock, condTok, initTok, stepTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
|
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
tok = endBlock;
|
|
|
|
} else {
|
|
|
|
// Traverse condition
|
|
|
|
if (updateRecursive(condTok) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
// Check if condition is true or false
|
|
|
|
bool checkThen, checkElse;
|
|
|
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
|
|
|
ForwardAnalyzer::Action thenAction = ForwardAnalyzer::Action::None;
|
|
|
|
ForwardAnalyzer::Action elseAction = ForwardAnalyzer::Action::None;
|
2020-02-16 16:02:22 +01:00
|
|
|
bool hasElse = Token::simpleMatch(endBlock, "} else {");
|
2020-02-13 16:27:06 +01:00
|
|
|
bool bail = false;
|
|
|
|
|
|
|
|
// Traverse then block
|
|
|
|
bool returnThen = isEscapeScope(endBlock, true);
|
|
|
|
bool returnElse = false;
|
|
|
|
if (checkThen) {
|
|
|
|
if (updateRange(endCond->next(), endBlock) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
} else if (!checkElse) {
|
|
|
|
thenAction = checkScope(endBlock);
|
|
|
|
if (hasGoto(endBlock))
|
|
|
|
bail = true;
|
|
|
|
}
|
|
|
|
// Traverse else block
|
2020-02-16 16:02:22 +01:00
|
|
|
if (hasElse) {
|
2020-02-13 16:27:06 +01:00
|
|
|
returnElse = isEscapeScope(endBlock->linkAt(2), true);
|
|
|
|
if (checkElse) {
|
|
|
|
Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2));
|
|
|
|
if (result == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
} else if (!checkThen) {
|
|
|
|
elseAction = checkScope(endBlock->linkAt(2));
|
|
|
|
if (hasGoto(endBlock))
|
|
|
|
bail = true;
|
|
|
|
}
|
|
|
|
tok = endBlock->linkAt(2);
|
|
|
|
} else {
|
|
|
|
tok = endBlock;
|
|
|
|
}
|
2020-09-11 00:06:49 +02:00
|
|
|
actions |= (thenAction | elseAction);
|
2020-02-13 16:27:06 +01:00
|
|
|
if (bail)
|
|
|
|
return Progress::Break;
|
|
|
|
if (returnThen && returnElse)
|
|
|
|
return Progress::Break;
|
|
|
|
else if (thenAction.isModified() && elseAction.isModified())
|
|
|
|
return Progress::Break;
|
|
|
|
else if ((returnThen || returnElse) && (thenAction.isModified() || elseAction.isModified()))
|
|
|
|
return Progress::Break;
|
|
|
|
// Conditional return
|
|
|
|
if (returnThen && !hasElse) {
|
|
|
|
if (checkThen) {
|
|
|
|
return Progress::Break;
|
|
|
|
} else {
|
2020-09-11 00:06:49 +02:00
|
|
|
if (analyzer->isConditional() && stopUpdates())
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
|
|
|
analyzer->assume(condTok, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (thenAction.isInconclusive() || elseAction.isInconclusive()) {
|
|
|
|
if (!analyzer->lowerToInconclusive())
|
|
|
|
return Progress::Break;
|
|
|
|
} else if (thenAction.isModified() || elseAction.isModified()) {
|
2020-09-11 00:06:49 +02:00
|
|
|
if (!hasElse && analyzer->isConditional() && stopUpdates())
|
2020-09-11 08:51:12 +02:00
|
|
|
return Progress::Break;
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!analyzer->lowerToPossible())
|
|
|
|
return Progress::Break;
|
|
|
|
analyzer->assume(condTok, elseAction.isModified());
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 19:20:03 +02:00
|
|
|
} else if (Token::simpleMatch(tok, "try {")) {
|
|
|
|
Token* endBlock = tok->next()->link();
|
|
|
|
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
|
|
|
if (updateRange(tok->next(), endBlock) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
if (a.isModified())
|
|
|
|
analyzer->lowerToPossible();
|
|
|
|
tok = endBlock;
|
2020-02-13 16:27:06 +01:00
|
|
|
} else if (Token::simpleMatch(tok, "do {")) {
|
|
|
|
Token* endBlock = tok->next()->link();
|
2020-08-24 13:10:36 +02:00
|
|
|
Token* condTok = Token::simpleMatch(endBlock, "} while (") ? endBlock->tokAt(2)->astOperand2() : nullptr;
|
|
|
|
if (updateLoop(endBlock, condTok) == Progress::Break)
|
2020-02-13 16:27:06 +01:00
|
|
|
return Progress::Break;
|
2020-08-24 13:10:36 +02:00
|
|
|
if (condTok)
|
|
|
|
tok = endBlock->linkAt(2)->next();
|
|
|
|
else
|
|
|
|
tok = endBlock;
|
2020-02-13 16:27:06 +01:00
|
|
|
} else if (Token::Match(tok, "assert|ASSERT (")) {
|
|
|
|
const Token* condTok = tok->next()->astOperand2();
|
|
|
|
bool checkThen, checkElse;
|
|
|
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
|
|
|
if (checkElse)
|
|
|
|
return Progress::Break;
|
|
|
|
if (!checkThen)
|
2020-02-19 07:55:04 +01:00
|
|
|
analyzer->assume(condTok, true, tok);
|
2020-02-13 16:27:06 +01:00
|
|
|
} else if (Token::simpleMatch(tok, "switch (")) {
|
|
|
|
if (updateRecursive(tok->next()->astOperand2()) == Progress::Break)
|
|
|
|
return Progress::Break;
|
|
|
|
return Progress::Break;
|
|
|
|
} else {
|
|
|
|
if (updateTok(tok, &next) == Progress::Break)
|
|
|
|
return Progress::Break;
|
2020-05-21 08:47:48 +02:00
|
|
|
if (next) {
|
|
|
|
if (precedes(next, end))
|
|
|
|
tok = next->previous();
|
|
|
|
else
|
|
|
|
return Progress::Break;
|
|
|
|
}
|
2020-02-13 16:27:06 +01:00
|
|
|
}
|
|
|
|
// Prevent infinite recursion
|
|
|
|
if (tok->next() == start)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return Progress::Continue;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static bool isUnevaluated(const Token* tok) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (Token::Match(tok->previous(), "sizeof|decltype ("))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static Token* assignExpr(Token* tok) {
|
2020-02-13 16:27:06 +01:00
|
|
|
while (tok->astParent() && astIsLHS(tok)) {
|
2020-05-19 21:03:28 +02:00
|
|
|
if (tok->astParent()->isAssignmentOp())
|
2020-02-13 16:27:06 +01:00
|
|
|
return tok->astParent();
|
|
|
|
tok = tok->astParent();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static const Scope* findBreakScope(const Scope* scope) {
|
2020-02-13 16:27:06 +01:00
|
|
|
while (scope && scope->type != Scope::eWhile && scope->type != Scope::eFor && scope->type != Scope::eSwitch)
|
|
|
|
scope = scope->nestedIn;
|
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static Token* skipTo(Token* tok, const Token* dest, const Token* end = nullptr) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (end && dest->index() > end->index())
|
|
|
|
return nullptr;
|
|
|
|
int i = dest->index() - tok->index();
|
|
|
|
if (i > 0)
|
|
|
|
return tok->tokAt(dest->index() - tok->index());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:04:05 +01:00
|
|
|
static bool isConditional(const Token* tok) {
|
2020-02-13 16:27:06 +01:00
|
|
|
const Token* parent = tok->astParent();
|
|
|
|
while (parent && !Token::Match(parent, "%oror%|&&|:")) {
|
|
|
|
tok = parent;
|
|
|
|
parent = parent->astParent();
|
|
|
|
}
|
|
|
|
return parent && (parent->str() == ":" || parent->astOperand2() == tok);
|
|
|
|
}
|
|
|
|
|
2020-02-15 08:08:55 +01:00
|
|
|
static Token* getInitTok(Token* tok) {
|
2020-02-13 16:27:06 +01:00
|
|
|
if (!tok)
|
|
|
|
return nullptr;
|
|
|
|
if (Token::Match(tok, "%name% ("))
|
|
|
|
return getInitTok(tok->next());
|
2020-05-19 21:03:28 +02:00
|
|
|
if (tok->str() != "(")
|
2020-02-13 16:27:06 +01:00
|
|
|
return nullptr;
|
|
|
|
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
|
|
|
return nullptr;
|
|
|
|
if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";"))
|
|
|
|
return nullptr;
|
|
|
|
return tok->astOperand2()->astOperand1();
|
|
|
|
}
|
|
|
|
|
2020-02-15 08:08:55 +01:00
|
|
|
static Token* getStepTok(Token* tok) {
|
2020-02-15 07:57:43 +01:00
|
|
|
if (!tok)
|
|
|
|
return nullptr;
|
|
|
|
if (Token::Match(tok, "%name% ("))
|
|
|
|
return getStepTok(tok->next());
|
2020-05-19 21:03:28 +02:00
|
|
|
if (tok->str() != "(")
|
2020-02-15 07:57:43 +01:00
|
|
|
return nullptr;
|
|
|
|
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
|
|
|
return nullptr;
|
|
|
|
if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";"))
|
|
|
|
return nullptr;
|
|
|
|
return tok->astOperand2()->astOperand2()->astOperand2();
|
|
|
|
}
|
|
|
|
|
2020-09-03 07:17:36 +02:00
|
|
|
static Token* getStepTokFromEnd(Token* tok) {
|
|
|
|
if (!Token::simpleMatch(tok, "}"))
|
|
|
|
return nullptr;
|
|
|
|
Token* end = tok->link()->previous();
|
|
|
|
if (!Token::simpleMatch(end, ")"))
|
|
|
|
return nullptr;
|
|
|
|
return getStepTok(end->link());
|
|
|
|
}
|
|
|
|
|
2020-02-13 16:27:06 +01:00
|
|
|
};
|
|
|
|
|
2020-09-10 04:32:07 +02:00
|
|
|
ForwardAnalyzer::Action valueFlowGenericForward(Token* start,
|
2020-09-11 08:51:12 +02:00
|
|
|
const Token* end,
|
|
|
|
const ValuePtr<ForwardAnalyzer>& fa,
|
|
|
|
const Settings* settings)
|
2020-02-13 16:27:06 +01:00
|
|
|
{
|
|
|
|
ForwardTraversal ft{fa, settings};
|
|
|
|
ft.updateRange(start, end);
|
2020-09-10 04:29:26 +02:00
|
|
|
return ft.actions;
|
2020-02-13 16:27:06 +01:00
|
|
|
}
|