ValueFlow: Refactor the for-loop handling. Use abstract interpretation.

This commit is contained in:
Daniel Marjamäki 2014-03-15 11:29:33 +01:00
parent ce1aa1e011
commit 894a65b0b1
3 changed files with 164 additions and 96 deletions

View File

@ -609,53 +609,135 @@ static void valueFlowAfterAssign(TokenList *tokenlist, ErrorLogger *errorLogger,
}
}
static void execute(const Token *expr,
std::map<unsigned int, MathLib::bigint> * const programMemory,
MathLib::bigint *result,
bool *error)
{
if (!expr)
*error = true;
else if (expr->isNumber())
*result = MathLib::toLongNumber(expr->str());
else if (expr->varId() > 0) {
const std::map<unsigned int, MathLib::bigint>::const_iterator var = programMemory->find(expr->varId());
if (var == programMemory->end())
*error = true;
else
*result = var->second;
}
else if (expr->isComparisonOp()) {
MathLib::bigint result1, result2;
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "<")
*result = result1 < result2;
else if (expr->str() == "<=")
*result = result1 <= result2;
else if (expr->str() == ">")
*result = result1 > result2;
else if (expr->str() == ">=")
*result = result1 >= result2;
else if (expr->str() == "==")
*result = result1 == result2;
else if (expr->str() == "!=")
*result = result1 != result2;
}
else if (expr->str() == "=") {
execute(expr->astOperand2(), programMemory, result, error);
if (expr->astOperand1()->varId())
(*programMemory)[expr->astOperand1()->varId()] = *result;
else
*error = true;
}
else if (expr->str() == "++") {
if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U)
*error = true;
else {
std::map<unsigned int, MathLib::bigint>::iterator var = programMemory->find(expr->astOperand1()->varId());
if (var == programMemory->end())
*error = true;
else {
*result = var->second + 1;
var->second = *result;
}
}
}
else if (expr->str() == "&&") {
execute(expr->astOperand1(), programMemory, result, error);
if (*error || *result == 0)
*result = 0;
else {
execute(expr->astOperand2(), programMemory, result, error);
// If there is an error, assume the result is not important
if (*error) {
*error = false;
*result = 1;
}
}
}
else if (expr->str() == "||") {
execute(expr->astOperand1(), programMemory, result, error);
if (*result == 0 && *error == false)
execute(expr->astOperand2(), programMemory, result, error);
}
else
*error = true;
}
static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!Token::simpleMatch(tok, "for ("))
if (!Token::simpleMatch(tok, "for (") ||
!Token::simpleMatch(tok->next()->astOperand2(), ";") ||
!Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";"))
continue;
tok = tok->tokAt(2);
if (!Token::Match(tok,"%type%| %var% = %num% ;")) { // TODO: don't use %num%
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok, "For loop not handled");
const Token *firstExpression = tok->next()->astOperand2()->astOperand1();
const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1();
const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2();
tok = tok->linkAt(1);
std::map<unsigned int, MathLib::bigint> programMemory;
MathLib::bigint result;
bool error = false;
execute(firstExpression, &programMemory, &result, &error);
if (error)
continue;
}
Token * const vartok = tok->tokAt(Token::Match(tok, "%var% =") ? 0 : 1);
const MathLib::bigint num1 = MathLib::toLongNumber(vartok->strAt(2));
if (vartok->varId() == 0U)
continue;
tok = vartok->tokAt(4);
const Token *num2tok = nullptr;
if (Token::Match(tok, "%varid% <|<=|!=", vartok->varId())) {
tok = tok->next();
num2tok = tok->astOperand2();
if (num2tok && num2tok->str() == "(" && !num2tok->astOperand2())
num2tok = num2tok->astOperand1();
if (!Token::Match(num2tok, "%num%"))
num2tok = 0;
}
if (!num2tok) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok, "For loop not handled");
continue;
}
const MathLib::bigint num2 = MathLib::toLongNumber(num2tok ? num2tok->str() : "0") - ((tok->str()=="<=") ? 0 : 1);
while (tok && tok->str() != ";")
tok = tok->next();
if (!num2tok || !Token::Match(tok, "; %varid% ++ ) {", vartok->varId())) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok, "For loop not handled");
execute(secondExpression, &programMemory, &result, &error);
if (error)
continue;
const std::map<unsigned int, MathLib::bigint> startMemory(programMemory);
std::map<unsigned int, MathLib::bigint> endMemory;
unsigned int maxcount = 10000;
while (result != 0 && !error && --maxcount) {
endMemory = programMemory;
execute(thirdExpression, &programMemory, &result, &error);
if (!error)
execute(secondExpression, &programMemory, &result, &error);
}
Token * const bodyStart = tok->tokAt(4);
Token * const bodyStart = tok->next();
const Token * const bodyEnd = bodyStart->link();
for (std::map<unsigned int, MathLib::bigint>::const_iterator it = startMemory.begin(); it != startMemory.end(); ++it) {
const unsigned int varid = it->first;
const MathLib::bigint num1 = it->second;
const MathLib::bigint num2 = endMemory[varid];
// Is variable modified inside for loop
bool modified = false;
for (const Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) {
if (Token::Match(tok2, "%varid% =", vartok->varId())) {
if (Token::Match(tok2, "%varid% =", varid)) {
modified = true;
break;
}
@ -664,7 +746,7 @@ static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, con
continue;
for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) {
if (tok2->varId() == vartok->varId()) {
if (tok2->varId() == varid) {
const Token * parent = tok2->astParent();
while (parent) {
const Token * const p = parent;
@ -674,7 +756,7 @@ static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, con
}
if (parent) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable " + vartok->str() + " stopping on ?");
bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on ?");
continue;
}
@ -689,12 +771,13 @@ static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, con
if (tok2->str() == "{") {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable " + vartok->str() + " stopping on {");
bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on {");
break;
}
}
}
}
}
static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{

View File

@ -130,7 +130,6 @@ private:
TEST_CASE(array_index_negative2); // ticket #3063
TEST_CASE(array_index_for_decr);
TEST_CASE(array_index_varnames); // FP: struct member. #1576
TEST_CASE(array_index_for_break); // FP: for,break
TEST_CASE(array_index_for_continue); // for,continue
TEST_CASE(array_index_for); // FN: for,if
TEST_CASE(array_index_for_neq); // #2211: Using != in condition
@ -458,7 +457,8 @@ private:
" a[i] = 0;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n"
"[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout.str());
}
}
@ -1045,7 +1045,8 @@ private:
" for (int i = 3; 0 <= i; i--)\n"
" a[i] = i;\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: a\n"
"[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout.str());
check("void f()\n"
"{\n"
@ -1766,7 +1767,8 @@ private:
" data[i] = 0;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n"
"[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout.str());
check("void f()\n"
"{\n"
@ -1844,19 +1846,6 @@ private:
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n", errout.str());
}
void array_index_for_break() {
check("void f() {\n"
" int a[2];\n"
" for (int i = 0; i <= 2; ++i) {\n"
" a[i] = 0;\n"
" if (i==1) {\n"
" break;\n"
" }\n"
" }\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void array_index_for_continue() {
// #3913
check("void f() {\n"
@ -1924,7 +1913,9 @@ private:
" a[i] = 0;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n"
"[test.cpp:4]: (error) Array 'a[5]' accessed at index 9, which is out of bounds.\n",
errout.str());
}
void array_index_for_question() {
@ -2274,7 +2265,8 @@ private:
" for (i = 0; i <= 10; ++i)\n"
" a[i] = 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: a\n"
"[test.cpp:7]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str());
}
@ -2285,7 +2277,8 @@ private:
" for (int i = 0; i < 8; ++i)\n"
" p[i] = 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: p\n", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: p\n"
"[test.cpp:5]: (error) Array 'p[2]' accessed at index 7, which is out of bounds.\n", errout.str());
// No false positive
check("void foo(int x, int y)\n"

View File

@ -617,14 +617,6 @@ private:
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
code = "void f() {\n"
" for (int x = 0; x < ((short)10); x++)\n"
" a[x] = 0;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
code = "void f() {\n"
" for (int x = 0; x < 10; x++)\n"
" x<4 ?\n"