Fixed #7965 (valueFlowForward: Improved handling of correlated variables)

This commit is contained in:
Daniel Marjamäki 2017-08-27 19:50:44 +02:00
parent 713f607168
commit 02a1b7cd2d
2 changed files with 65 additions and 7 deletions

View File

@ -154,17 +154,20 @@ static ProgramMemory getProgramMemory(const Token *tok, unsigned int varid, cons
if (Token::Match(tok2, "[;{}] %varid% = %var% ;", varid)) { if (Token::Match(tok2, "[;{}] %varid% = %var% ;", varid)) {
const Token *vartok = tok2->tokAt(3); const Token *vartok = tok2->tokAt(3);
programMemory.setValue(vartok->varId(), value); programMemory.setValue(vartok->varId(), value);
} } else if (Token::Match(tok2, "[;{}] %var% =") ||
if (Token::Match(tok2, "[;{}] %var% =")) { Token::Match(tok2, "[;{}] const| %type% %var% (")) {
const Token *vartok = tok2->next(); const Token *vartok = tok2->next();
while (vartok->next()->isName())
vartok = vartok->next();
if (!programMemory.hasValue(vartok->varId())) { if (!programMemory.hasValue(vartok->varId())) {
MathLib::bigint result = 0; MathLib::bigint result = 0;
bool error = false; bool error = false;
execute(tok2->tokAt(2)->astOperand2(), &programMemory, &result, &error); execute(vartok->next()->astOperand2(), &programMemory, &result, &error);
if (!error) if (!error)
programMemory.setIntValue(vartok->varId(), result); programMemory.setIntValue(vartok->varId(), result);
} }
} }
if (tok2->str() == "{") { if (tok2->str() == "{") {
if (indentlevel <= 0) if (indentlevel <= 0)
break; break;
@ -1296,6 +1299,9 @@ static bool valueFlowForward(Token * const startToken,
bool returnStatement = false; // current statement is a return, stop analysis at the ";" bool returnStatement = false; // current statement is a return, stop analysis at the ";"
bool read = false; // is variable value read? bool read = false; // is variable value read?
if (values.empty())
return true;
for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) {
if (indentlevel >= 0 && tok2->str() == "{") if (indentlevel >= 0 && tok2->str() == "{")
++indentlevel; ++indentlevel;
@ -1414,17 +1420,32 @@ static bool valueFlowForward(Token * const startToken,
} }
const Token * const condTok = tok2->next()->astOperand2(); const Token * const condTok = tok2->next()->astOperand2();
const bool condAlwaysTrue = (condTok && condTok->values().size() == 1U && condTok->values().front().isKnown() && condTok->values().front().intvalue != 0); const bool condAlwaysTrue = (condTok && condTok->hasKnownIntValue() && condTok->values().front().intvalue != 0);
const bool condAlwaysFalse = (condTok && condTok->hasKnownIntValue() && condTok->values().front().intvalue == 0);
// Should scope be skipped because variable value is checked? // Should scope be skipped because variable value is checked?
std::list<ValueFlow::Value> truevalues; std::list<ValueFlow::Value> truevalues;
std::list<ValueFlow::Value> falsevalues;
for (std::list<ValueFlow::Value>::const_iterator it = values.begin(); it != values.end(); ++it) { for (std::list<ValueFlow::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
if (condAlwaysTrue) if (condAlwaysTrue) {
truevalues.push_back(*it); truevalues.push_back(*it);
else if (subFunction && conditionIsTrue(condTok, getProgramMemory(tok2, varid, *it))) continue;
}
if (condAlwaysFalse) {
falsevalues.push_back(*it);
continue;
}
const ProgramMemory &programMemory = getProgramMemory(tok2, varid, *it);
if (subFunction && conditionIsTrue(condTok, programMemory))
truevalues.push_back(*it); truevalues.push_back(*it);
else if (!subFunction && !conditionIsFalse(condTok, getProgramMemory(tok2, varid, *it))) else if (!subFunction && !conditionIsFalse(condTok, programMemory))
truevalues.push_back(*it); truevalues.push_back(*it);
if (condAlwaysFalse)
falsevalues.push_back(*it);
else if (conditionIsFalse(condTok, programMemory))
falsevalues.push_back(*it);
else if (!subFunction && !conditionIsTrue(condTok, programMemory))
falsevalues.push_back(*it);
} }
if (truevalues.size() != values.size() || condAlwaysTrue) { if (truevalues.size() != values.size() || condAlwaysTrue) {
// '{' // '{'
@ -1458,6 +1479,30 @@ static bool valueFlowForward(Token * const startToken,
removeValues(values, truevalues); removeValues(values, truevalues);
} }
if (Token::simpleMatch(tok2, "} else {")) {
Token * const startTokenElse = tok2->tokAt(2);
valueFlowForward(startTokenElse->next(),
startTokenElse->link(),
var,
varid,
falsevalues,
constValue,
subFunction,
tokenlist,
errorLogger,
settings);
// goto '}'
tok2 = startTokenElse->link();
if (isReturnScope(tok2)) {
if (condAlwaysFalse)
return false;
removeValues(values, falsevalues);
}
}
continue; continue;
} }

View File

@ -77,6 +77,7 @@ private:
TEST_CASE(valueFlowAfterAssign); TEST_CASE(valueFlowAfterAssign);
TEST_CASE(valueFlowAfterCondition); TEST_CASE(valueFlowAfterCondition);
TEST_CASE(valueFlowForwardCompoundAssign); TEST_CASE(valueFlowForwardCompoundAssign);
TEST_CASE(valueFlowForwardCorrelatedVariables);
TEST_CASE(valueFlowSwitchVariable); TEST_CASE(valueFlowSwitchVariable);
@ -1771,6 +1772,18 @@ private:
ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45 + 67, 0.01)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45 + 67, 0.01));
} }
void valueFlowForwardCorrelatedVariables() {
const char *code;
code = "void f(int x = 0) {\n"
" bool zero(x==0);\n"
" if (zero) a = x;\n" // <- x is 0
" else b = x;\n" // <- x is not 0
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
}
void valueFlowBitAnd() { void valueFlowBitAnd() {
const char *code; const char *code;