diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 80f038482..def5e1026 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -4450,6 +4450,56 @@ static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldataba } } +static void valueFlowSymbolicIdentity(TokenList* tokenlist) +{ + for (Token* tok = tokenlist->front(); tok; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + if (!Token::Match(tok, "*|/|<<|>>|^|+|-|%or%")) + continue; + if (!tok->astOperand1()) + continue; + if (!tok->astOperand2()) + continue; + if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) + continue; + const ValueFlow::Value* constant = nullptr; + const Token* vartok = nullptr; + if (tok->astOperand1()->hasKnownIntValue()) { + constant = &tok->astOperand1()->values().front(); + vartok = tok->astOperand2(); + } + if (tok->astOperand2()->hasKnownIntValue()) { + constant = &tok->astOperand2()->values().front(); + vartok = tok->astOperand1(); + } + if (!constant) + continue; + if (!vartok) + continue; + if (vartok->exprId() == 0) + continue; + if (Token::Match(tok, "<<|>>|/") && !astIsLHS(vartok)) + continue; + if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) + continue; + if (Token::Match(tok, "*|/") && constant->intvalue != 1) + continue; + std::vector values = {makeSymbolic(vartok)}; + std::unordered_set ids = {vartok->exprId()}; + std::copy_if( + vartok->values().begin(), vartok->values().end(), std::back_inserter(values), [&](const ValueFlow::Value& v) { + if (!v.isSymbolicValue()) + return false; + if (!v.tokvalue) + return false; + return ids.insert(v.tokvalue->exprId()).second; + }); + for (const ValueFlow::Value& v : values) + setTokenValue(tok, v, tokenlist->getSettings()); + } +} + static void valueFlowSymbolicAbs(TokenList* tokenlist, SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { @@ -7362,6 +7412,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, while (n > 0 && values < getTotalValues(tokenlist)) { values = getTotalValues(tokenlist); valueFlowImpossibleValues(tokenlist, settings); + valueFlowSymbolicIdentity(tokenlist); valueFlowSymbolicAbs(tokenlist, symboldatabase); valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); valueFlowSymbolicInfer(tokenlist, symboldatabase); diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8e2354ae1..009638b5a 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -144,6 +144,7 @@ private: TEST_CASE(valueFlowMod); TEST_CASE(valueFlowNotNull); TEST_CASE(valueFlowSymbolic); + TEST_CASE(valueFlowSymbolicIdentity); TEST_CASE(valueFlowSmartPointer); } @@ -508,6 +509,12 @@ private: return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value(); } + std::list removeSymbolic(std::list values) + { + values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue)); + return values; + } + void valueFlowNumber() { ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue); ASSERT_EQUALS_DOUBLE(192.0, valueOfTok("x=0x0.3p10;", "0x0.3p10").floatValue, 1e-5); // 3 * 16^-1 * 2^10 = 192 @@ -3449,7 +3456,7 @@ private: " foo.x = 1;\n" " x = 0 + foo.x;\n" // <- foo.x is 1 "}"; - values = tokenValues(code, "+"); + values = removeSymbolic(tokenValues(code, "+")); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(true, values.front().isKnown()); ASSERT_EQUALS(true, values.front().isIntValue()); @@ -3487,7 +3494,7 @@ private: " hints.x = 2;\n" " x = 0 + foo.x;\n" // <- foo.x is possible 1, possible 2 "}"; - values = tokenValues(code, "+"); + values = removeSymbolic(tokenValues(code, "+")); TODO_ASSERT_EQUALS(2U, 0U, values.size()); // should be 2 // FP: Condition '*b>0' is always true @@ -3926,23 +3933,23 @@ private: " return x+y;\n" "}\n" "void f2() {\n" - " x = 1 * add(10+1,4);\n" + " x = 2 * add(10+1,4);\n" "}"; - ASSERT_EQUALS(15, valueOfTok(code, "*").intvalue); + ASSERT_EQUALS(30, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int one() { return 1; }\n" - "void f() { x = 1 * one(); }"; - ASSERT_EQUALS(1, valueOfTok(code, "*").intvalue); + "void f() { x = 2 * one(); }"; + ASSERT_EQUALS(2, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int add(int x, int y) {\n" " return x+y;\n" "}\n" "void f2() {\n" - " x = 1 * add(1,add(2,3));\n" + " x = 2 * add(1,add(2,3));\n" "}"; - ASSERT_EQUALS(6, valueOfTok(code, "*").intvalue); + ASSERT_EQUALS(12, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int f(int i, X x) {\n" @@ -5673,7 +5680,7 @@ private: code = "short f(short x) {\n" " return x + 0;\n" "}"; - values = tokenValues(code, "+", &s); + values = removeSymbolic(tokenValues(code, "+", &s)); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(-0x8000, values.front().intvalue); ASSERT_EQUALS(0x7fff, values.back().intvalue); @@ -5697,7 +5704,7 @@ private: code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n" " return x + 0;\n" "}"; - values = tokenValues(code, "+", &s); + values = removeSymbolic(tokenValues(code, "+", &s)); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(0, values.front().intvalue); ASSERT_EQUALS(100, values.back().intvalue); @@ -5705,7 +5712,7 @@ private: code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n" " return x + 0;\n" "}"; - values = tokenValues(code, "+", &s); + values = removeSymbolic(tokenValues(code, "+", &s)); values.remove_if([](const ValueFlow::Value& v) { return v.isImpossible(); }); @@ -6468,6 +6475,71 @@ private: ASSERT_EQUALS(false, testValueOfXImpossible(code, 11U, 0)); } + void valueFlowSymbolicIdentity() + { + const char* code; + + code = "void f(int a) {\n" + " int x = a*1;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a/1;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a+0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a-0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a^0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a|0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a>>0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = a<<0;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = 0>>a;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, "a", 0)); + + code = "void f(int a) {\n" + " int x = 0<