diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index e09580a00..2616c47d5 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -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& values, + const Settings* const settings) +{ + valueFlowForwardConst(start, end, var, values, settings, 0); +} + static void valueFlowForwardAssign(Token* const tok, const Token* expr, std::vector vars, @@ -8091,6 +8113,20 @@ static std::vector 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(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 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); } diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 70ad741c8..5a5abacc3 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -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 removeSymbolic(std::list values) + static std::list removeSymbolicTok(std::list 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 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 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 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(); });