Set valueflow for container elements (#4455)
* Track elements to containers * Format * Handle other access methods * Format
This commit is contained in:
parent
909e9ca359
commit
53820515c9
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue