ValueFlow: Better handling of container size values

This commit is contained in:
Daniel Marjamäki 2018-11-02 18:28:32 +01:00
parent 590bf97247
commit 16cc20a232
2 changed files with 77 additions and 8 deletions

View File

@ -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]) {

View File

@ -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));
}
};