valueFlowForward: improve handling of compound assignments

This commit is contained in:
Daniel Marjamäki 2017-08-23 11:13:47 +02:00
parent 71b0370389
commit 167cfb1ac5
2 changed files with 97 additions and 2 deletions

View File

@ -1672,8 +1672,66 @@ static bool valueFlowForward(Token * const startToken,
}
else if (tok2->varId() == varid) {
// compound assignment, known value in rhs
if (Token::Match(tok2->previous(), "!!* %name% %assign%") &&
tok2->next()->str() != "=" &&
tok2->next()->astOperand2() &&
tok2->next()->astOperand2()->hasKnownIntValue()) {
const ValueFlow::Value &rhsValue = tok2->next()->astOperand2()->values().front();
const std::string &assign = tok2->next()->str();
std::list<ValueFlow::Value>::iterator it;
// Erase values that are not int values..
for (it = values.begin(); it != values.end();) {
if (it->isIntValue()) {
if (assign == "+=")
it->intvalue += rhsValue.intvalue;
else if (assign == "-=")
it->intvalue -= rhsValue.intvalue;
else if (assign == "*=")
it->intvalue *= rhsValue.intvalue;
else if (assign == "/=")
it->intvalue /= rhsValue.intvalue;
else if (assign == "%=")
it->intvalue %= rhsValue.intvalue;
else if (assign == "&=")
it->intvalue &= rhsValue.intvalue;
else if (assign == "|=")
it->intvalue |= rhsValue.intvalue;
else if (assign == "^=")
it->intvalue ^= rhsValue.intvalue;
else {
values.clear();
break;
}
++it;
} else if (it->isFloatValue()) {
if (assign == "+=")
it->floatValue += rhsValue.intvalue;
else if (assign == "-=")
it->floatValue -= rhsValue.intvalue;
else if (assign == "*=")
it->floatValue *= rhsValue.intvalue;
else if (assign == "/=")
it->floatValue /= rhsValue.intvalue;
else {
values.clear();
break;
}
++it;
} else {
it = values.erase(it);
}
}
if (values.empty()) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "coumpound assignment of " + tok2->str());
return false;
}
}
// bailout: assignment
if (Token::Match(tok2->previous(), "!!* %name% %assign%")) {
else if (Token::Match(tok2->previous(), "!!* %name% %assign%")) {
// simplify rhs
for (Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) {
if (tok3->varId() == varid) {

View File

@ -75,8 +75,8 @@ private:
TEST_CASE(valueFlowBeforeConditionTernaryOp);
TEST_CASE(valueFlowAfterAssign);
TEST_CASE(valueFlowAfterCondition);
TEST_CASE(valueFlowForwardCompoundAssign);
TEST_CASE(valueFlowSwitchVariable);
@ -116,6 +116,25 @@ private:
return false;
}
bool testValueOfX(const char code[], unsigned int linenr, float value, float diff) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
std::list<ValueFlow::Value>::const_iterator it;
for (it = tok->values().begin(); it != tok->values().end(); ++it) {
if (it->isFloatValue() && it->floatValue >= value - diff && it->floatValue <= value + diff)
return true;
}
}
}
return false;
}
std::string getErrorPathForX(const char code[], unsigned int linenr) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
@ -1717,6 +1736,24 @@ private:
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
}
void valueFlowForwardCompoundAssign() {
const char *code;
code = "void f() {\n"
" int x = 123;\n"
" x += 43;\n"
" return x;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 4U, 166));
code = "void f() {\n"
" float x = 123.45;\n"
" x += 67;\n"
" return x;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45 + 67, 0.01));
}
void valueFlowBitAnd() {
const char *code;