ValueFlow: Better handling of container size values
This commit is contained in:
parent
590bf97247
commit
16cc20a232
|
@ -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<Token *>(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<Token *>(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]) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue