diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 3778bbff1..143b28ed4 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -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::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) { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8b2b1fc1c..a563edf8a 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -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::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;