Set valueflow for container elements (#4455)

* Track elements to containers

* Format

* Handle other access methods

* Format
This commit is contained in:
Paul Fultz II 2022-09-10 02:26:44 -05:00 committed by GitHub
parent 909e9ca359
commit 53820515c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 20 deletions

View File

@ -1423,14 +1423,26 @@ static void valueFlowArrayBool(TokenList *tokenlist)
static void valueFlowArrayElement(TokenList* tokenlist, const Settings* settings)
{
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
if (!Token::simpleMatch(tok, "["))
continue;
if (!tok->isBinaryOp())
continue;
if (tok->hasKnownIntValue())
continue;
const Token* indexTok = tok->astOperand2();
const Token* arrayTok = tok->astOperand1();
const Token* indexTok = nullptr;
const Token* arrayTok = nullptr;
if (Token::simpleMatch(tok, "[") && tok->isBinaryOp()) {
indexTok = tok->astOperand2();
arrayTok = tok->astOperand1();
} else if (Token::Match(tok->tokAt(-2), ". %name% (") && astIsContainer(tok->tokAt(-2)->astOperand1())) {
arrayTok = tok->tokAt(-2)->astOperand1();
const Library::Container* container = getLibraryContainer(arrayTok);
if (!container || container->stdAssociativeLike)
continue;
Library::Container::Yield yield = container->getYield(tok->strAt(-1));
if (yield != Library::Container::Yield::AT_INDEX)
continue;
indexTok = tok->astOperand2();
}
if (!indexTok || !arrayTok)
continue;
for (const ValueFlow::Value& arrayValue : arrayTok->values()) {
if (!arrayValue.isTokValue())
@ -5291,7 +5303,8 @@ static void valueFlowForwardConst(Token* start,
const Token* end,
const Variable* var,
const ContainerOfValue& values,
const Settings* const settings)
const Settings* const settings,
int = 0)
{
for (Token* tok = start; tok != end; tok = tok->next()) {
if (tok->varId() == var->declarationId()) {
@ -5337,6 +5350,15 @@ static void valueFlowForwardConst(Token* start,
}
}
static void valueFlowForwardConst(Token* start,
const Token* end,
const Variable* var,
const std::initializer_list<ValueFlow::Value>& values,
const Settings* const settings)
{
valueFlowForwardConst(start, end, var, values, settings, 0);
}
static void valueFlowForwardAssign(Token* const tok,
const Token* expr,
std::vector<const Variable*> vars,
@ -8091,6 +8113,20 @@ static std::vector<ValueFlow::Value> getContainerSizeFromConstructor(const Token
return getContainerSizeFromConstructorArgs(args, valueType->container, known);
}
static void valueFlowContainerSetTokValue(TokenList* tokenlist, const Token* tok, Token* initList)
{
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::TOK;
value.tokvalue = initList;
value.setKnown();
Token* start = initList->link() ? initList->link() : initList->next();
if (tok->variable() && tok->variable()->isConst()) {
valueFlowForwardConst(start, tok->variable()->scope()->bodyEnd, tok->variable(), {value}, tokenlist->getSettings());
} else {
valueFlowForward(start, tok, value, tokenlist);
}
}
static void valueFlowContainerSize(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* /*errorLogger*/,
@ -8146,7 +8182,8 @@ static void valueFlowContainerSize(TokenList* tokenlist,
values.back().setKnown();
if (!staticSize) {
if (Token::simpleMatch(var->nameToken()->next(), "{")) {
const Token* initList = var->nameToken()->next();
Token* initList = var->nameToken()->next();
valueFlowContainerSetTokValue(tokenlist, var->nameToken(), initList);
values = getInitListSize(initList, var->valueType(), settings, known);
} else if (Token::simpleMatch(var->nameToken()->next(), "(")) {
const Token* constructorArgs = var->nameToken()->next();
@ -8166,24 +8203,27 @@ static void valueFlowContainerSize(TokenList* tokenlist,
// after assignment
for (const Scope *functionScope : symboldatabase->functionScopes) {
for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
for (Token* tok = const_cast<Token*>(functionScope->bodyStart); tok != functionScope->bodyEnd; tok = tok->next()) {
if (Token::Match(tok, "%name%|;|{|} %var% = %str% ;")) {
const Token *containerTok = tok->next();
Token* containerTok = tok->next();
if (containerTok->exprId() == 0)
continue;
if (containerTok->valueType() && containerTok->valueType()->container && containerTok->valueType()->container->stdStringLike) {
if (containerTok->valueType() && containerTok->valueType()->container &&
containerTok->valueType()->container->stdStringLike) {
valueFlowContainerSetTokValue(tokenlist, containerTok, containerTok->tokAt(2));
ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2)));
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
valueFlowForward(containerTok->next(), containerTok, value, tokenlist);
}
} else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) {
const Token* containerTok = tok->next();
Token* containerTok = tok->next();
if (containerTok->exprId() == 0)
continue;
if (astIsContainer(containerTok) && containerTok->valueType()->container->size_templateArgNo < 0) {
std::vector<ValueFlow::Value> values =
getInitListSize(tok->tokAt(3), containerTok->valueType(), settings);
valueFlowContainerSetTokValue(tokenlist, containerTok, tok->tokAt(3));
for (const ValueFlow::Value& value : values)
valueFlowForward(containerTok->next(), containerTok, value, tokenlist);
}

View File

@ -136,6 +136,7 @@ private:
TEST_CASE(valueFlowConditionExpressions);
TEST_CASE(valueFlowContainerSize);
TEST_CASE(valueFlowContainerElement);
TEST_CASE(valueFlowDynamicBufferSize);
@ -534,9 +535,11 @@ private:
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
}
static std::list<ValueFlow::Value> removeSymbolic(std::list<ValueFlow::Value> values)
static std::list<ValueFlow::Value> removeSymbolicTok(std::list<ValueFlow::Value> values)
{
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
values.remove_if([](const ValueFlow::Value& v) {
return v.isSymbolicValue() || v.isTokValue();
});
return values;
}
@ -3898,7 +3901,7 @@ private:
" foo.x = 1;\n"
" x = 0 + foo.x;\n" // <- foo.x is 1
"}";
values = removeSymbolic(tokenValues(code, "+"));
values = removeSymbolicTok(tokenValues(code, "+"));
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(true, values.front().isKnown());
ASSERT_EQUALS(true, values.front().isIntValue());
@ -3936,7 +3939,7 @@ private:
" hints.x = 2;\n"
" x = 0 + foo.x;\n" // <- foo.x is possible 1, possible 2
"}";
values = removeSymbolic(tokenValues(code, "+"));
values = removeSymbolicTok(tokenValues(code, "+"));
TODO_ASSERT_EQUALS(2U, 0U, values.size()); // should be 2
// FP: Condition '*b>0' is always true
@ -5542,6 +5545,7 @@ private:
MathLib::bigint i,
bool unique = true) {
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
if (!unique)
values.remove_if(&isNotPossible);
if (values.size() != 1)
@ -5559,6 +5563,7 @@ private:
MathLib::bigint i,
bool unique = true) {
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
if (!unique)
values.remove_if(&isNotImpossible);
if (values.size() != 1)
@ -5576,6 +5581,7 @@ private:
MathLib::bigint i,
bool unique = true) {
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
if (!unique)
values.remove_if(&isNotInconclusive);
if (values.size() != 1)
@ -5591,6 +5597,7 @@ private:
static std::string isKnownContainerSizeValue(std::list<ValueFlow::Value> values, MathLib::bigint i, bool unique = true) {
values.remove_if(std::mem_fn(&ValueFlow::Value::isSymbolicValue));
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
if (!unique)
values.remove_if(&isNotKnown);
if (values.size() != 1)
@ -5792,7 +5799,7 @@ private:
" std::string s { p };\n" // size of s is unknown
" s.front();\n"
"}";
ASSERT(tokenValues(code, "s . front").empty());
ASSERT(removeSymbolicTok(tokenValues(code, "s . front")).empty());
code = "void f() {\n"
" std::string s = { 'a', 'b', 'c' };\n" // size of s is 3
@ -6314,6 +6321,34 @@ private:
ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
}
void valueFlowContainerElement()
{
const char* code;
LOAD_LIB_2(settings.library, "std.cfg");
code = "int f() {\n"
" std::vector<int> v = {1, 2, 3, 4};\n"
" int x = v[1];\n"
" return x;\n"
"}\n";
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 2));
code = "int f() {\n"
" std::vector<int> v = {1, 2, 3, 4};\n"
" int x = v.at(1);\n"
" return x;\n"
"}\n";
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 2));
code = "int f() {\n"
" std::string s = \"hello\";\n"
" int x = s[1];\n"
" return x;\n"
"}\n";
ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 'e'));
}
void valueFlowDynamicBufferSize() {
const char *code;
@ -6364,7 +6399,7 @@ private:
code = "short f(short x) {\n"
" return x + 0;\n"
"}";
values = removeSymbolic(tokenValues(code, "+", &s));
values = removeSymbolicTok(tokenValues(code, "+", &s));
ASSERT_EQUALS(2, values.size());
ASSERT_EQUALS(-0x8000, values.front().intvalue);
ASSERT_EQUALS(0x7fff, values.back().intvalue);
@ -6388,7 +6423,7 @@ private:
code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n"
" return x + 0;\n"
"}";
values = removeSymbolic(tokenValues(code, "+", &s));
values = removeSymbolicTok(tokenValues(code, "+", &s));
ASSERT_EQUALS(2, values.size());
ASSERT_EQUALS(0, values.front().intvalue);
ASSERT_EQUALS(100, values.back().intvalue);
@ -6396,7 +6431,7 @@ private:
code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n"
" return x + 0;\n"
"}";
values = removeSymbolic(tokenValues(code, "+", &s));
values = removeSymbolicTok(tokenValues(code, "+", &s));
values.remove_if([](const ValueFlow::Value& v) {
return v.isImpossible();
});