diff --git a/lib/astutils.cpp b/lib/astutils.cpp index a9b3e44ec..dd2616d22 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1419,6 +1419,31 @@ bool reaches(const Token * start, const Token * dest, const Library& library, Er return true; } +static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) +{ + for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { + if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) + // TODO: this is a quick bailout + return false; + if (tok->varId() <= 0 || exprVarIds.find(tok->varId()) == exprVarIds.end()) + continue; + const Token *parent = tok; + while (parent->astParent() && !parent->astParent()->isAssignmentOp() && parent->astParent()->tokType() != Token::Type::eIncDecOp) { + if (parent->str() == "," || parent->isUnaryOp("&")) + // TODO: This is a quick bailout + return false; + parent = parent->astParent(); + } + if (parent->astParent()) { + if (parent->astParent()->tokType() == Token::Type::eIncDecOp) + return false; + else if (parent->astParent()->isAssignmentOp() && parent == parent->astParent()->astOperand1()) + return false; + } + } + return true; +} + struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass) { // Parse the given tokens @@ -1515,8 +1540,25 @@ struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const return Result(Result::Type::BAILOUT); if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { - // TODO: only bailout if expr is reassigned in loop - return Result(Result::Type::BAILOUT); + const Token *bodyStart = nullptr; + const Token *conditionStart = nullptr; + if (Token::simpleMatch(tok, "do {")) { + bodyStart = tok->next(); + if (Token::simpleMatch(bodyStart->link(), "} while (")) + conditionStart = bodyStart->link()->tokAt(2); + } else { + conditionStart = tok->next(); + if (Token::simpleMatch(conditionStart->link(), ") {")) + bodyStart = conditionStart->link()->next(); + } + + // Is expr changed in condition? + if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); + + // Is expr changed in loop body? + if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); } if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { @@ -1527,7 +1569,6 @@ struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const if (expr->isName() && Token::Match(tok, "%name% (") && tok->str().find("<") != std::string::npos && tok->str().find(expr->str()) != std::string::npos) return Result(Result::Type::BAILOUT); - if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { const Token *parent = tok; bool other = false; diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 251f08c4a..940aa116e 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -2567,6 +2567,19 @@ private: values = tokenValues(code, "<"); ASSERT_EQUALS(true, values.empty()); + code = "void f() {\n" + " S s;\n" + " s.x = 37;\n" + " int y = 10;\n" + " while (s.x < y)\n" // s.x has a known value + " y--;\n" + "}"; + values = tokenValues(code, ". x <"); + ASSERT(values.size() == 1 && + values.front().isKnown() && + values.front().isIntValue() && + values.front().intvalue == 37); + code = "void f() {\n" " Hints hints;\n" " hints.x = 1;\n"