Propagate symbolic values for identity operators (#3497)
This commit is contained in:
parent
b97426a886
commit
6b9ef1fc58
|
@ -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<ValueFlow::Value> values = {makeSymbolic(vartok)};
|
||||||
|
std::unordered_set<nonneg int> 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)
|
static void valueFlowSymbolicAbs(TokenList* tokenlist, SymbolDatabase* symboldatabase)
|
||||||
{
|
{
|
||||||
for (const Scope* scope : symboldatabase->functionScopes) {
|
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||||
|
@ -7362,6 +7412,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
while (n > 0 && values < getTotalValues(tokenlist)) {
|
while (n > 0 && values < getTotalValues(tokenlist)) {
|
||||||
values = getTotalValues(tokenlist);
|
values = getTotalValues(tokenlist);
|
||||||
valueFlowImpossibleValues(tokenlist, settings);
|
valueFlowImpossibleValues(tokenlist, settings);
|
||||||
|
valueFlowSymbolicIdentity(tokenlist);
|
||||||
valueFlowSymbolicAbs(tokenlist, symboldatabase);
|
valueFlowSymbolicAbs(tokenlist, symboldatabase);
|
||||||
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowSymbolicInfer(tokenlist, symboldatabase);
|
valueFlowSymbolicInfer(tokenlist, symboldatabase);
|
||||||
|
|
|
@ -144,6 +144,7 @@ private:
|
||||||
TEST_CASE(valueFlowMod);
|
TEST_CASE(valueFlowMod);
|
||||||
TEST_CASE(valueFlowNotNull);
|
TEST_CASE(valueFlowNotNull);
|
||||||
TEST_CASE(valueFlowSymbolic);
|
TEST_CASE(valueFlowSymbolic);
|
||||||
|
TEST_CASE(valueFlowSymbolicIdentity);
|
||||||
TEST_CASE(valueFlowSmartPointer);
|
TEST_CASE(valueFlowSmartPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,6 +509,12 @@ private:
|
||||||
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
|
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<ValueFlow::Value> removeSymbolic(std::list<ValueFlow::Value> values)
|
||||||
|
{
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
void valueFlowNumber() {
|
void valueFlowNumber() {
|
||||||
ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue);
|
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
|
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"
|
" foo.x = 1;\n"
|
||||||
" x = 0 + foo.x;\n" // <- foo.x is 1
|
" x = 0 + foo.x;\n" // <- foo.x is 1
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "+");
|
values = removeSymbolic(tokenValues(code, "+"));
|
||||||
ASSERT_EQUALS(1U, values.size());
|
ASSERT_EQUALS(1U, values.size());
|
||||||
ASSERT_EQUALS(true, values.front().isKnown());
|
ASSERT_EQUALS(true, values.front().isKnown());
|
||||||
ASSERT_EQUALS(true, values.front().isIntValue());
|
ASSERT_EQUALS(true, values.front().isIntValue());
|
||||||
|
@ -3487,7 +3494,7 @@ private:
|
||||||
" hints.x = 2;\n"
|
" hints.x = 2;\n"
|
||||||
" x = 0 + foo.x;\n" // <- foo.x is possible 1, possible 2
|
" 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
|
TODO_ASSERT_EQUALS(2U, 0U, values.size()); // should be 2
|
||||||
|
|
||||||
// FP: Condition '*b>0' is always true
|
// FP: Condition '*b>0' is always true
|
||||||
|
@ -3926,23 +3933,23 @@ private:
|
||||||
" return x+y;\n"
|
" return x+y;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"void f2() {\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());
|
ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
|
||||||
|
|
||||||
code = "int one() { return 1; }\n"
|
code = "int one() { return 1; }\n"
|
||||||
"void f() { x = 1 * one(); }";
|
"void f() { x = 2 * one(); }";
|
||||||
ASSERT_EQUALS(1, valueOfTok(code, "*").intvalue);
|
ASSERT_EQUALS(2, valueOfTok(code, "*").intvalue);
|
||||||
ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
|
ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
|
||||||
|
|
||||||
code = "int add(int x, int y) {\n"
|
code = "int add(int x, int y) {\n"
|
||||||
" return x+y;\n"
|
" return x+y;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"void f2() {\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());
|
ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
|
||||||
|
|
||||||
code = "int f(int i, X x) {\n"
|
code = "int f(int i, X x) {\n"
|
||||||
|
@ -5673,7 +5680,7 @@ private:
|
||||||
code = "short f(short x) {\n"
|
code = "short f(short x) {\n"
|
||||||
" return x + 0;\n"
|
" return x + 0;\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "+", &s);
|
values = removeSymbolic(tokenValues(code, "+", &s));
|
||||||
ASSERT_EQUALS(2, values.size());
|
ASSERT_EQUALS(2, values.size());
|
||||||
ASSERT_EQUALS(-0x8000, values.front().intvalue);
|
ASSERT_EQUALS(-0x8000, values.front().intvalue);
|
||||||
ASSERT_EQUALS(0x7fff, values.back().intvalue);
|
ASSERT_EQUALS(0x7fff, values.back().intvalue);
|
||||||
|
@ -5697,7 +5704,7 @@ private:
|
||||||
code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n"
|
code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n"
|
||||||
" return x + 0;\n"
|
" return x + 0;\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "+", &s);
|
values = removeSymbolic(tokenValues(code, "+", &s));
|
||||||
ASSERT_EQUALS(2, values.size());
|
ASSERT_EQUALS(2, values.size());
|
||||||
ASSERT_EQUALS(0, values.front().intvalue);
|
ASSERT_EQUALS(0, values.front().intvalue);
|
||||||
ASSERT_EQUALS(100, values.back().intvalue);
|
ASSERT_EQUALS(100, values.back().intvalue);
|
||||||
|
@ -5705,7 +5712,7 @@ private:
|
||||||
code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n"
|
code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n"
|
||||||
" return x + 0;\n"
|
" return x + 0;\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "+", &s);
|
values = removeSymbolic(tokenValues(code, "+", &s));
|
||||||
values.remove_if([](const ValueFlow::Value& v) {
|
values.remove_if([](const ValueFlow::Value& v) {
|
||||||
return v.isImpossible();
|
return v.isImpossible();
|
||||||
});
|
});
|
||||||
|
@ -6468,6 +6475,71 @@ private:
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 11U, 0));
|
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<<a;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, "a", 0));
|
||||||
|
}
|
||||||
|
|
||||||
void valueFlowSmartPointer()
|
void valueFlowSmartPointer()
|
||||||
{
|
{
|
||||||
const char* code;
|
const char* code;
|
||||||
|
|
Loading…
Reference in New Issue