ValueFlow: Improve bailout for structs etc in loops

This commit is contained in:
Daniel Marjamäki 2019-07-29 15:51:48 +02:00
parent 6b662c7353
commit 607b3daca8
2 changed files with 57 additions and 3 deletions

View File

@ -1419,6 +1419,31 @@ bool reaches(const Token * start, const Token * dest, const Library& library, Er
return true; return true;
} }
static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set<int> &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<int> &exprVarIds, bool local, bool inInnerClass) struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set<int> &exprVarIds, bool local, bool inInnerClass)
{ {
// Parse the given tokens // Parse the given tokens
@ -1515,8 +1540,25 @@ struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const
return Result(Result::Type::BAILOUT); return Result(Result::Type::BAILOUT);
if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) {
// TODO: only bailout if expr is reassigned in loop const Token *bodyStart = nullptr;
return Result(Result::Type::BAILOUT); 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), ") {")) { 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) 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); return Result(Result::Type::BAILOUT);
if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { if (exprVarIds.find(tok->varId()) != exprVarIds.end()) {
const Token *parent = tok; const Token *parent = tok;
bool other = false; bool other = false;

View File

@ -2567,6 +2567,19 @@ private:
values = tokenValues(code, "<"); values = tokenValues(code, "<");
ASSERT_EQUALS(true, values.empty()); 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" code = "void f() {\n"
" Hints hints;\n" " Hints hints;\n"
" hints.x = 1;\n" " hints.x = 1;\n"