diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 8885fc65d45..fac53ecb3f2 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -7094,6 +7094,20 @@ static void valueFlowContainerSize(const TokenList& tokenlist, } } + auto forwardMinimumContainerSize = [&](MathLib::bigint size, Token* opTok, const Token* exprTok) -> void { + if (size == 0) + return; + + ValueFlow::Value value(size - 1); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.bound = ValueFlow::Value::Bound::Upper; + value.setImpossible(); + Token* next = nextAfterAstRightmostLeaf(opTok); + if (!next) + next = opTok->next(); + valueFlowForward(next, exprTok, std::move(value), tokenlist, errorLogger, settings); + }; + // after assignment for (const Scope *functionScope : symboldatabase.functionScopes) { for (auto* tok = const_cast(functionScope->bodyStart); tok != functionScope->bodyEnd; tok = tok->next()) { @@ -7161,20 +7175,22 @@ static void valueFlowContainerSize(const TokenList& tokenlist, value.setImpossible(); valueFlowForward(tok->linkAt(2), containerTok, std::move(value), tokenlist, errorLogger, settings); } - } else if (Token::simpleMatch(tok, "+=") && astIsContainer(tok->astOperand1())) { + + } else if (tok->str() == "+=" && astIsContainer(tok->astOperand1())) { const Token* containerTok = tok->astOperand1(); const Token* valueTok = tok->astOperand2(); - MathLib::bigint size = valueFlowGetStrLength(valueTok); - if (size == 0) - continue; - ValueFlow::Value value(size - 1); - value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; - value.bound = ValueFlow::Value::Bound::Upper; - value.setImpossible(); - Token* next = nextAfterAstRightmostLeaf(tok); - if (!next) - next = tok->next(); - valueFlowForward(next, containerTok, std::move(value), tokenlist, errorLogger, settings); + const MathLib::bigint size = valueFlowGetStrLength(valueTok); + forwardMinimumContainerSize(size, tok, containerTok); + + } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "+") && astIsContainerString(tok)) { + const Token* tok2 = tok->astOperand2(); + MathLib::bigint size = 0; + while (Token::simpleMatch(tok2, "+") && tok2->astOperand2()) { + size += valueFlowGetStrLength(tok2->astOperand2()); + tok2 = tok2->astOperand1(); + } + size += valueFlowGetStrLength(tok2); + forwardMinimumContainerSize(size, tok, tok->astOperand1()); } } } diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8ede4292d3c..85a38b7bd33 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -6949,6 +6949,42 @@ class TestValueFlow : public TestFixture { " return a[0];\n" "}\n"; ASSERT(!isKnownContainerSizeValue(tokenValues(code, "a [ 0"), 0).empty()); + + code = "void f(const std::string& a) {\n" // #12994 + " std::string b = a + \"123\";\n" + " if (b.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "b ."), 2)); + + code = "void f(const std::string& a) {\n" + " std::string b = \"123\" + a;\n" + " if (b.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "b ."), 2)); + + code = "void f(const std::string& a, const std::string& b) {\n" + " std::string c = a + b + \"123\";\n" + " if (c.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "c ."), 2)); + + code = "void f(const std::string& a) {\n" + " std::string b = a + \"123\" + \"456\";\n" + " if (b.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "b ."), 5)); + + code = "void f(const std::string& a) {\n" + " std::string b = \"123\" + a + \"456\";\n" + " if (b.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "b ."), 5)); + + code = "void f(const std::string& a, const std::string& b) {\n" + " std::string c = \"123\" + a + b;\n" + " if (c.empty()) {}\n" + "}"; + ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "c ."), 2)); } void valueFlowContainerElement()