ValueFlow: Propagate const variables more aggressively (#3606)
* ValueFlow: Propagate const variables more aggressively * Format * Fix incorrect addition
This commit is contained in:
parent
5fd17ef2c2
commit
d2926bfa96
|
@ -4764,6 +4764,57 @@ static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symbold
|
|||
}
|
||||
}
|
||||
|
||||
static void valueFlowForwardConst(Token* start,
|
||||
const Token* end,
|
||||
const Variable* var,
|
||||
const std::list<ValueFlow::Value>& values,
|
||||
const Settings* const settings)
|
||||
{
|
||||
for (Token* tok = start; tok != end; tok = tok->next()) {
|
||||
if (tok->varId() == var->declarationId()) {
|
||||
for (const ValueFlow::Value& value : values)
|
||||
setTokenValue(tok, value, settings);
|
||||
} else {
|
||||
[&] {
|
||||
// Follow references
|
||||
std::vector<ReferenceToken> refs = followAllReferences(tok);
|
||||
ValueFlow::Value::ValueKind refKind =
|
||||
refs.size() == 1 ? ValueFlow::Value::ValueKind::Known : ValueFlow::Value::ValueKind::Inconclusive;
|
||||
for (const ReferenceToken& ref : refs) {
|
||||
if (ref.token->varId() == var->declarationId()) {
|
||||
for (ValueFlow::Value value : values) {
|
||||
value.valueKind = refKind;
|
||||
value.errorPath.insert(value.errorPath.end(), ref.errors.begin(), ref.errors.end());
|
||||
setTokenValue(tok, value, settings);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Follow symbolic vaues
|
||||
for (const ValueFlow::Value& v : tok->values()) {
|
||||
if (!v.isSymbolicValue())
|
||||
continue;
|
||||
if (!v.tokvalue)
|
||||
continue;
|
||||
if (v.tokvalue->varId() != var->declarationId())
|
||||
continue;
|
||||
for (ValueFlow::Value value : values) {
|
||||
if (v.intvalue != 0) {
|
||||
if (!value.isIntValue())
|
||||
continue;
|
||||
value.intvalue += v.intvalue;
|
||||
}
|
||||
value.valueKind = v.valueKind;
|
||||
value.bound = v.bound;
|
||||
value.errorPath.insert(value.errorPath.end(), v.errorPath.begin(), v.errorPath.end());
|
||||
setTokenValue(tok, value, settings);
|
||||
}
|
||||
}
|
||||
}();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void valueFlowForwardAssign(Token* const tok,
|
||||
const Token* expr,
|
||||
std::vector<const Variable*> vars,
|
||||
|
@ -4826,6 +4877,25 @@ static void valueFlowForwardAssign(Token* const tok,
|
|||
continue;
|
||||
value.tokvalue = tok;
|
||||
}
|
||||
// Const variable
|
||||
if (expr->variable() && expr->variable()->isConst() && !expr->variable()->isReference()) {
|
||||
auto it = std::remove_if(values.begin(), values.end(), [](const ValueFlow::Value& value) {
|
||||
if (!value.isKnown())
|
||||
return false;
|
||||
if (value.isIntValue())
|
||||
return true;
|
||||
if (value.isFloatValue())
|
||||
return true;
|
||||
if (value.isContainerSizeValue())
|
||||
return true;
|
||||
if (value.isIteratorValue())
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
std::list<ValueFlow::Value> constValues;
|
||||
constValues.splice(constValues.end(), values, it, values.end());
|
||||
valueFlowForwardConst(const_cast<Token*>(nextExpression), endOfVarScope, expr->variable(), constValues, settings);
|
||||
}
|
||||
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
|
||||
}
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ private:
|
|||
TEST_CASE(valueFlowForwardLambda);
|
||||
TEST_CASE(valueFlowForwardTryCatch);
|
||||
TEST_CASE(valueFlowForwardInconclusiveImpossible);
|
||||
TEST_CASE(valueFlowForwardConst);
|
||||
|
||||
TEST_CASE(valueFlowFwdAnalysis);
|
||||
|
||||
|
@ -3516,6 +3517,61 @@ private:
|
|||
ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 1));
|
||||
}
|
||||
|
||||
void valueFlowForwardConst()
|
||||
{
|
||||
const char* code;
|
||||
|
||||
code = "int f() {\n"
|
||||
" const int i = 2;\n"
|
||||
" const int x = i+1;\n"
|
||||
" goto end;\n"
|
||||
"end:\n"
|
||||
" return x;\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, 3));
|
||||
|
||||
code = "int f() {\n"
|
||||
" int i = 2;\n"
|
||||
" const int& x = i;\n"
|
||||
" i++;\n"
|
||||
" return x;\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 2));
|
||||
|
||||
code = "int f(int a, int b, int c) {\n"
|
||||
" const int i = 2;\n"
|
||||
" const int x = i+1;\n"
|
||||
" if (a == x) { return 0; }\n"
|
||||
" if (b == x) { return 0; }\n"
|
||||
" if (c == x) { return 0; }\n"
|
||||
" return x;\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 3));
|
||||
|
||||
code = "int f(int a, int b, int c) {\n"
|
||||
" const int i = 2;\n"
|
||||
" const int y = i+1;\n"
|
||||
" const int& x = y;\n"
|
||||
" if (a == x) { return 0; }\n"
|
||||
" if (b == x) { return 0; }\n"
|
||||
" if (c == x) { return 0; }\n"
|
||||
" return x;\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS(true, testValueOfXKnown(code, 8U, 3));
|
||||
|
||||
code = "int f(int a, int b, int c, int x) {\n"
|
||||
" const int i = 2;\n"
|
||||
" const int y = i+1;\n"
|
||||
" if (a == y) { return 0; }\n"
|
||||
" if (b == y) { return 0; }\n"
|
||||
" if (c == y) { return 0; }\n"
|
||||
" if (x == y)\n"
|
||||
" return x;\n"
|
||||
" return 0;\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS(true, testValueOfXKnown(code, 8U, 3));
|
||||
}
|
||||
|
||||
void valueFlowRightShift() {
|
||||
const char *code;
|
||||
/* Set some temporary fixed values to simplify testing */
|
||||
|
|
Loading…
Reference in New Issue