42 #include <type_traits>
47 struct ForwardTraversal {
48 enum class Progress { Continue, Break, Skip };
51 : analyzer(analyzer), tokenList(tokenList), errorLogger(errorLogger), settings(settings)
59 bool analyzeTerminate{};
61 std::vector<Token*> loopEnds;
67 return Progress::Break;
71 explicit Branch(
Token* tok =
nullptr) : endBlock(tok) {}
72 Token* endBlock =
nullptr;
76 bool escapeUnknown =
false;
78 bool isEscape()
const {
79 return escape || escapeUnknown;
81 bool isConclusiveEscape()
const {
82 return escape && !escapeUnknown;
84 bool isModified()
const {
85 return action.
isModified() && !isConclusiveEscape();
87 bool isInconclusive()
const {
100 std::pair<bool, bool> evalCond(
const Token* tok,
const Token* ctx =
nullptr)
const {
102 return std::make_pair(
false,
false);
103 std::vector<MathLib::bigint> result = analyzer->evaluate(tok, ctx);
105 const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](
int x) {
108 const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](
int x) {
111 return std::make_pair(checkThen, checkElse);
114 bool isConditionTrue(
const Token* tok,
const Token* ctx =
nullptr)
const {
115 return evalCond(tok, ctx).first;
118 template<
class T,
class F,
REQUIRES(
"T must be a Token class", std::is_convertible<T*, const Token*> )>
119 Progress traverseTok(T* tok,
const F &f,
bool traverseUnknown, T** out =
nullptr) {
124 traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown);
128 if (loopEnds.empty())
132 *out = loopEnds.back();
134 traverseRecursive(tok->astOperand2(), f, traverseUnknown);
135 traverseRecursive(tok->astOperand1(), f, traverseUnknown);
139 traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown);
144 return Progress::Skip;
145 }
else if (tok->astOperand1() && tok->astOperand2() &&
Token::Match(tok,
"?|&&|%oror%")) {
146 if (traverseConditional(tok, f, traverseUnknown) == Progress::Break)
150 return Progress::Skip;
153 if (checkScope(lambdaEndToken).isModified())
156 *out = lambdaEndToken->next();
158 }
else if (tok->str() ==
"{" && tok->scope() && tok->scope()->isClassOrStruct()) {
162 if (f(tok) == Progress::Break)
165 return Progress::Continue;
168 template<
class T,
class F,
REQUIRES(
"T must be a Token class", std::is_convertible<T*, const Token*> )>
169 Progress traverseRecursive(T* tok,
const F &f,
bool traverseUnknown,
unsigned int recursion=0) {
171 return Progress::Continue;
172 if (recursion > 10000)
173 return Progress::Skip;
174 T* firstOp = tok->astOperand1();
175 T* secondOp = tok->astOperand2();
181 std::swap(firstOp, secondOp);
182 if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break)
184 const Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown);
185 if (p == Progress::Break)
187 if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break)
189 if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break)
191 return Progress::Continue;
194 template<
class T,
class F,
REQUIRES(
"T must be a Token class", std::is_convertible<T*, const Token*> )>
195 Progress traverseConditional(T* tok, F f,
bool traverseUnknown) {
196 if (
Token::Match(tok,
"?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) {
197 T* condTok = tok->astOperand1();
198 T* childTok = tok->astOperand2();
199 bool checkThen, checkElse;
200 std::tie(checkThen, checkElse) = evalCond(condTok);
201 if (!checkThen && !checkElse) {
202 if (!traverseUnknown && analyzer->stopOnCondition(condTok) && stopUpdates()) {
203 return Progress::Continue;
208 if (childTok->str() ==
":") {
209 if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break)
211 if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break)
214 if (!checkThen && tok->str() ==
"&&")
215 return Progress::Continue;
216 if (!checkElse && tok->str() ==
"||")
217 return Progress::Continue;
218 if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break)
222 return Progress::Continue;
225 Progress update(
Token* tok) {
228 if (!action.
isNone() && !analyzeOnly)
237 return Progress::Continue;
240 Progress updateTok(
Token* tok,
Token** out =
nullptr) {
241 auto f = [
this](
Token* tok2) {
244 return traverseTok(tok, f,
false, out);
247 Progress updateRecursive(
Token* tok) {
248 auto f = [
this](
Token* tok2) {
251 return traverseRecursive(tok, f,
false);
256 auto f = [&](
const Token* tok) {
260 return Progress::Continue;
262 traverseRecursive(start, f,
true);
268 for (
const Token* tok = start; tok && tok != end; tok = tok->
next()) {
277 ForwardTraversal fork(
bool analyze =
false)
const {
278 ForwardTraversal ft = *
this;
280 ft.analyzeOnly =
true;
281 ft.analyzeTerminate =
true;
287 std::vector<ForwardTraversal> tryForkScope(
Token* endBlock,
bool isModified =
false)
const {
288 if (analyzer->updateScope(endBlock, isModified)) {
289 ForwardTraversal ft = fork();
290 return {std::move(ft)};
292 return std::vector<ForwardTraversal> {};
295 std::vector<ForwardTraversal> tryForkUpdateScope(
Token* endBlock,
bool isModified =
false)
const {
296 std::vector<ForwardTraversal> result = tryForkScope(endBlock, isModified);
297 for (ForwardTraversal& ft : result)
298 ft.updateScope(endBlock);
302 static bool hasGoto(
const Token* endBlock) {
306 static bool hasJump(
const Token* endBlock) {
310 bool hasInnerReturnScope(
const Token* start,
const Token* end)
const {
311 for (
const Token* tok=start; tok != end; tok = tok->
previous()) {
313 const Token* ftok =
nullptr;
323 const Token* ftok =
nullptr;
336 return analyzeRange(endBlock->
link(), endBlock);
350 bool checkBranch(Branch& branch)
const {
353 std::vector<ForwardTraversal> ft1 = tryForkUpdateScope(branch.endBlock, a.
isModified());
354 const bool bail = hasGoto(branch.endBlock);
358 if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) {
359 ForwardTraversal ft2 = fork(
true);
360 ft2.updateScope(branch.endBlock);
362 branch.escape =
true;
363 branch.escapeUnknown =
false;
368 branch.escape =
true;
369 branch.escapeUnknown =
false;
376 bool reentersLoop(
Token* endBlock,
const Token* condTok,
const Token* stepTok)
const {
381 bool stepChangesCond =
false;
384 if (exprToks.first !=
nullptr && exprToks.second !=
nullptr)
390 const bool condChanged =
394 const bool changed = stepChangesCond || bodyChangesCond || condChanged;
397 ForwardTraversal ft = fork(
true);
398 ft.updateScope(endBlock);
399 return ft.isConditionTrue(condTok) && bodyChangesCond;
402 Progress updateInnerLoop(
Token* endBlock,
Token* stepTok,
Token* condTok) {
403 loopEnds.push_back(endBlock);
407 if (endBlock && updateScope(endBlock) == Progress::Break)
409 if (stepTok && updateRecursive(stepTok) == Progress::Break)
411 if (condTok && !
Token::simpleMatch(condTok,
":") && updateRecursive(condTok) == Progress::Break)
413 return Progress::Continue;
416 Progress updateLoop(
const Token* endToken,
419 Token* initTok =
nullptr,
420 Token* stepTok =
nullptr,
422 if (initTok && updateRecursive(initTok) == Progress::Break)
424 const bool isDoWhile =
precedes(endBlock, condTok);
425 bool checkThen =
true;
426 bool checkElse =
false;
428 std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->
previous() :
nullptr);
430 if (checkElse && exit) {
431 if (hasJump(endBlock)) {
432 if (!analyzer->lowerToPossible())
434 if (analyzer->isConditional() && stopUpdates())
437 return Progress::Continue;
443 condAnalysis = analyzeRecursive(condTok);
444 allAnalysis |= condAnalysis;
447 allAnalysis |= analyzeRecursive(stepTok);
448 actions |= allAnalysis;
450 if (checkElse && isDoWhile &&
453 if (updateScope(endBlock) == Progress::Break)
455 return updateRecursive(condTok);
458 if (!analyzer->lowerToInconclusive())
461 if (!analyzer->lowerToPossible())
467 if (updateRecursive(condTok) == Progress::Break)
470 if (!checkThen && !checkElse && !isDoWhile && analyzer->stopOnCondition(condTok) && stopUpdates())
474 return Progress::Continue;
475 if (checkThen || isDoWhile) {
479 if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
482 if (allAnalysis.
isModified() && reentersLoop(endBlock, condTok, stepTok))
487 std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.
isModified());
488 bool forkContinue =
true;
489 for (ForwardTraversal& ft : ftv) {
492 if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
493 forkContinue =
false;
496 if (allAnalysis.
isModified() || !forkContinue) {
500 if (analyzer->isConditional() && stopUpdates())
502 analyzer->assume(condTok,
false);
505 for (ForwardTraversal& ft : ftv) {
506 if (!ft.actions.isIncremental())
507 ft.updateRange(endBlock, endToken);
513 if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
514 return Progress::Break;
518 return Progress::Continue;
521 Progress updateLoopExit(
const Token* endToken,
524 Token* initTok =
nullptr,
525 Token* stepTok =
nullptr) {
526 return updateLoop(endToken, endBlock, condTok, initTok, stepTok,
true);
529 Progress updateScope(
Token* endBlock,
int depth = 20)
533 assert(endBlock->
link());
534 Token* ctx = endBlock->
link()->previous();
536 ctx = ctx->
link()->previous();
538 analyzer->updateState(ctx);
539 return updateRange(endBlock->
link(), endBlock, depth);
542 Progress updateRange(
Token* start,
const Token* end,
int depth = 20) {
547 Token* next =
nullptr;
548 if (tok->
index() <= i)
559 if (tok->
str() ==
"<") {
567 if (updateRecursive(assignTok) == Progress::Break)
586 if (updateLoop(end, endBlock, condTok,
nullptr, stepTok) == Progress::Break)
589 }
else if (tok->
str() ==
"break") {
593 tok = skipTo(tok, scopeEndToken, end);
596 if (!analyzer->lowerToPossible())
602 if (!analyzer->lowerToPossible())
604 }
else if (tok->
link() && tok->
str() ==
"}") {
615 if (!condTok->hasKnownIntValue() || inLoop) {
616 if (!analyzer->lowerToPossible())
618 }
else if (condTok->values().front().intvalue == inElse) {
623 Token* stepTok = getStepTokFromEnd(tok);
624 bool checkThen, checkElse;
625 std::tie(checkThen, checkElse) = evalCond(condTok);
626 if (stepTok && !checkElse) {
627 if (updateRecursive(stepTok) == Progress::Break)
629 if (updateRecursive(condTok) == Progress::Break)
632 std::tie(checkThen, checkElse) = evalCond(condTok);
635 if (updateLoopExit(end, tok, condTok,
nullptr, stepTok) == Progress::Break)
644 if (!analyzer->lowerToPossible())
653 reportError(
Severity::information,
"normalCheckLevelMaxBranches",
"Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches.");
660 if (initTok && updateRecursive(initTok) == Progress::Break)
666 if (conTok && updateRecursive(conTok) == Progress::Break)
668 bool isEmpty =
false;
669 std::vector<MathLib::bigint> result =
674 isEmpty = result.front() != 0;
675 if (!isEmpty && updateLoop(end, endBlock, condTok) == Progress::Break)
680 if (updateLoop(end, endBlock, condTok,
nullptr, stepTok) == Progress::Break)
686 if (updateRecursive(condTok) == Progress::Break)
688 Branch thenBranch{endBlock};
689 Branch elseBranch{endBlock->
tokAt(2) ? endBlock->
linkAt(2) :
nullptr};
691 std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok);
692 if (!thenBranch.check && !elseBranch.check && analyzer->stopOnCondition(condTok) && stopUpdates())
698 thenBranch.escape =
isEscapeScope(endBlock, thenBranch.escapeUnknown);
699 if (thenBranch.check) {
700 thenBranch.active =
true;
701 if (updateScope(endBlock, depth - 1) == Progress::Break)
703 }
else if (!elseBranch.check) {
704 thenBranch.active =
true;
705 if (checkBranch(thenBranch))
711 if (elseBranch.check) {
712 elseBranch.active =
true;
713 const Progress result = updateScope(endBlock->
linkAt(2), depth - 1);
714 if (result == Progress::Break)
716 }
else if (!thenBranch.check) {
717 elseBranch.active =
true;
718 if (checkBranch(elseBranch))
721 tok = endBlock->
linkAt(2);
725 if (thenBranch.active)
726 actions |= thenBranch.action;
727 if (elseBranch.active)
728 actions |= elseBranch.action;
731 if (thenBranch.isDead() && elseBranch.isDead()) {
732 if (thenBranch.isModified() && elseBranch.isModified())
734 if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape())
739 if (thenBranch.active && thenBranch.isEscape() && !hasElse) {
740 if (!thenBranch.isConclusiveEscape()) {
741 if (!analyzer->lowerToInconclusive())
743 }
else if (thenBranch.check) {
746 if (analyzer->isConditional() && stopUpdates())
748 analyzer->assume(condTok,
false);
751 if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) {
752 if (!analyzer->lowerToInconclusive())
754 }
else if (thenBranch.isModified() || elseBranch.isModified()) {
755 if (!hasElse && analyzer->isConditional() && stopUpdates())
757 if (!analyzer->lowerToPossible())
759 analyzer->assume(condTok, elseBranch.isModified());
764 ForwardTraversal tryTraversal = fork();
765 tryTraversal.updateScope(endBlock, depth - 1);
766 bool bail = tryTraversal.actions.isModified();
768 actions = tryTraversal.actions;
769 terminate = tryTraversal.terminate;
777 endBlock = endCatch->
linkAt(1);
778 ForwardTraversal ft = fork();
779 ft.updateScope(endBlock, depth - 1);
788 if (updateLoop(end, endBlock, condTok) == Progress::Break)
796 bool checkThen, checkElse;
797 std::tie(checkThen, checkElse) = evalCond(condTok);
806 }
else if (
Token* callTok = callExpr(tok)) {
808 if (start != callTok && tok != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break)
812 updateRange(callTok->next(), callTok->link(), depth - 1) == Progress::Break)
814 if (updateTok(callTok) == Progress::Break)
816 tok = callTok->
link();
820 if (updateTok(tok, &next) == Progress::Break)
826 return Progress::Continue;
830 if (tok->
next() == start)
833 return Progress::Continue;
836 void reportError(
Severity severity,
const std::string&
id,
const std::string& msg) {
904 ForwardTraversal ft{a, tokenList, errorLogger, settings};
906 ft.analyzer->updateState(start);
907 ft.updateRange(start, end);
917 ForwardTraversal ft{a, tokenList, errorLogger, settings};
918 (void)ft.updateRecursive(start);
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
const Token * findExpressionChanged(const Token *expr, const Token *start, const Token *end, const Settings &settings, int depth)
bool astIsLHS(const Token *tok)
bool isEscapeFunction(const Token *ftok, const Library *library)
static bool isFunctionCall(const Token *tok)
Token * getInitTok(Token *tok)
const Token * findNextTokenFromBreak(const Token *breakToken)
For a "break" token, locate the next token to execute.
Token * getCondTokFromEnd(Token *endBlock)
Token * getCondTok(Token *tok)
const Token * findLambdaEndToken(const Token *first)
find lambda function end token
const Token * nextAfterAstRightmostLeaf(const Token *tok)
Token * getStepTok(Token *tok)
bool isReturnScope(const Token *const endToken, const Library &library, const Token **unknownFunc, bool functionScope)
Is scope a return scope (scope will unconditionally return)
bool isUnevaluated(const Token *tok)
bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth)
const Token * findAstNode(const Token *ast, const TFunc &pred)
This is an interface, which the class responsible of error logging should implement.
virtual void reportErr(const ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
File name and line number.
Wrapper for error messages, provided by reportErr()
This is just a container for general settings so that we don't need to pass individual values to func...
static bool terminated()
termination requested?
const std::string & getSourceFilePath() const
The token list that the TokenList generates is a linked-list of this class.
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
bool hasKnownIntValue() const
bool isControlFlowKeyword() const
std::pair< const Token *, const Token * > findExpressionStartEndTokens() const
static const Token * findsimplematch(const Token *const startTok, const char(&pattern)[count])
const Token * tokAt(int index) const
void astOperand2(Token *tok)
void scope(const Scope *s)
Associate this token with given scope.
void link(Token *linkToToken)
Create link to given token.
const Token * linkAt(int index) const
void variable(const Variable *v)
Associate this token with given variable.
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
void astParent(Token *tok)
#define REQUIRES(msg,...)
Analyzer::Result valueFlowGenericForward(Token *start, const Token *end, const ValuePtr< Analyzer > &a, const TokenList &tokenList, ErrorLogger &errorLogger, const Settings &settings)
Severity
enum class for severity.
@ information
Checking information.
static const Token * assignExpr(const Token *tok)
bool isInconclusive() const
bool isIdempotent() const
bool isIncremental() const
Simple container to be thrown when internal error is detected.
bool contains(const Range &r, const T &x)
static bool isEscapeScope(const Token *tok, const Settings &settings, bool unknown=false)