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;
|
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 */
|
/** set ValueFlow value and perform calculations if possible */
|
||||||
static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Settings *settings)
|
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)
|
if (!parent)
|
||||||
return;
|
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%")) {
|
if (parent->str() == "(" && !parent->astOperand2() && Token::Match(parent,"( %name%")) {
|
||||||
const ValueType &valueType = ValueType::parseDecl(parent->next(), settings);
|
const ValueType &valueType = ValueType::parseDecl(parent->next(), settings);
|
||||||
if (valueType.pointer)
|
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 ||
|
if (known || value1.varId == 0U || value2.varId == 0U ||
|
||||||
(value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())) {
|
(value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())) {
|
||||||
ValueFlow::Value result(0);
|
ValueFlow::Value result(0);
|
||||||
result.condition = value1.condition ? value1.condition : value2.condition;
|
combineValueProperties(value1, value2, &result);
|
||||||
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;
|
|
||||||
const float floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue;
|
const float floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue;
|
||||||
const float floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue;
|
const float floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue;
|
||||||
switch (parent->str()[0]) {
|
switch (parent->str()[0]) {
|
||||||
|
|
|
@ -3379,7 +3379,7 @@ private:
|
||||||
ASSERT(tokenValues(code, "s [").empty());
|
ASSERT(tokenValues(code, "s [").empty());
|
||||||
|
|
||||||
code = "void f() {\n"
|
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"
|
" s.size();\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3));
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3));
|
||||||
|
@ -3444,6 +3444,25 @@ private:
|
||||||
"}";
|
"}";
|
||||||
ASSERT(tokenValues(code, "ints [").empty());
|
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