diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 8a3500772..fb13b9bca 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -345,6 +345,20 @@ static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign return value; } +static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result) +{ + if (value1.isKnown() && value2.isKnown()) + result->setKnown(); + else if (value1.isInconclusive() || value2.isInconclusive()) + result->setInconclusive(); + else + result->setPossible(); + result->condition = value1.condition ? value1.condition : value2.condition; + result->varId = (value1.varId != 0U) ? value1.varId : value2.varId; + result->varvalue = (result->varId == value1.varId) ? value1.varvalue : value2.varvalue; + result->errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; +} + /** set ValueFlow value and perform calculations if possible */ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Settings *settings) { @@ -359,6 +373,48 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti if (!parent) return; + if (value.isContainerSizeValue()) { + // .empty, .size, +"abc", +'a' + if (Token::Match(parent, "+")) { + for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { + for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { + ValueFlow::Value result; + result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + if (value1.isContainerSizeValue() && value2.isContainerSizeValue()) + result.intvalue = value1.intvalue + value2.intvalue; + else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString) + result.intvalue = value1.intvalue + Token::getStrLength(value2.tokvalue); + else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString) + result.intvalue = Token::getStrLength(value1.tokvalue) + value2.intvalue; + else + continue; + + combineValueProperties(value1, value2, &result); + + setTokenValue(parent, result, settings); + } + } + } + + + else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) && parent->astOperand1() && parent->astOperand1()->valueType()) { + const Library::Container *c = parent->astOperand1()->valueType()->container; + const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD; + if (yields == Library::Container::Yield::SIZE) { + ValueFlow::Value v(value); + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(const_cast(parent->astParent()), v, settings); + } else if (yields == Library::Container::Yield::EMPTY) { + ValueFlow::Value v(value); + v.intvalue = !v.intvalue; + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(const_cast(parent->astParent()), v, settings); + } + } + + return; + } + if (parent->str() == "(" && !parent->astOperand2() && Token::Match(parent,"( %name%")) { const ValueType &valueType = ValueType::parseDecl(parent->next(), settings); if (valueType.pointer) @@ -471,13 +527,7 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti if (known || value1.varId == 0U || value2.varId == 0U || (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())) { ValueFlow::Value result(0); - result.condition = value1.condition ? value1.condition : value2.condition; - result.setInconclusive(value1.isInconclusive() | value2.isInconclusive()); - result.varId = (value1.varId != 0U) ? value1.varId : value2.varId; - result.varvalue = (result.varId == value1.varId) ? value1.varvalue : value2.varvalue; - result.errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; - if (value1.valueKind == value2.valueKind) - result.valueKind = value1.valueKind; + combineValueProperties(value1, value2, &result); const float floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue; const float floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue; switch (parent->str()[0]) { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 152b24908..ad63b1c3f 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -3379,7 +3379,7 @@ private: ASSERT(tokenValues(code, "s [").empty()); code = "void f() {\n" - " std::string s=\"abc\";\n" // size of s is 3 + " std::string s = \"abc\";\n" // size of s is 3 " s.size();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3)); @@ -3444,6 +3444,25 @@ private: "}"; ASSERT(tokenValues(code, "ints [").empty()); + // container size => yields + code = "void f() {\n" + " std::string s = \"abcd\";\n" + " s.size();\n" + "}"; + ASSERT_EQUALS(4, tokenValues(code, "( ) ;").front().intvalue); + + code = "void f() {\n" + " std::string s;\n" + " s.empty();\n" + "}"; + ASSERT_EQUALS(1, tokenValues(code, "( ) ;").front().intvalue); + + // Calculations + code = "void f() {\n" + " std::string s = \"abcd\";\n" + " x = s + s;\n" + "}"; + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8)); } };