Fixed #8681 (ValueFlow: Container size)
This commit is contained in:
parent
8032f64c15
commit
81f54f7094
|
@ -1718,8 +1718,42 @@ void CheckStl::readingEmptyStlContainer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckStl::readingEmptyStlContainerError(const Token *tok)
|
|
||||||
|
void CheckStl::readingEmptyStlContainer2()
|
||||||
|
{
|
||||||
|
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||||
|
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!tok->isName() || !tok->valueType())
|
||||||
|
continue;
|
||||||
|
const Library::Container *container = tok->valueType()->container;
|
||||||
|
if (!container)
|
||||||
|
continue;
|
||||||
|
const ValueFlow::Value *value = tok->getContainerSizeValue(0);
|
||||||
|
if (!value)
|
||||||
|
continue;
|
||||||
|
if (value->isInconclusive() && !mSettings->inconclusive)
|
||||||
|
continue;
|
||||||
|
if (!value->errorSeverity() && !mSettings->isEnabled(Settings::WARNING))
|
||||||
|
continue;
|
||||||
|
if (Token::Match(tok, "%name% . %name% (")) {
|
||||||
|
if (container->getYield(tok->strAt(2)) == Library::Container::Yield::ITEM)
|
||||||
|
readingEmptyStlContainerError(tok,value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckStl::readingEmptyStlContainerError(const Token *tok, const ValueFlow::Value *value)
|
||||||
{
|
{
|
||||||
const std::string varname = tok ? tok->str() : std::string("var");
|
const std::string varname = tok ? tok->str() : std::string("var");
|
||||||
reportError(tok, Severity::style, "reademptycontainer", "$symbol:" + varname +"\nReading from empty STL container '$symbol'", CWE398, true);
|
|
||||||
|
std::string errmsg;
|
||||||
|
if (value && value->condition)
|
||||||
|
errmsg = "Reading from container '$symbol'. " + ValueFlow::eitherTheConditionIsRedundant(value->condition) + " or '$symbol' can be empty.";
|
||||||
|
else
|
||||||
|
errmsg = "Reading from empty STL container '$symbol'";
|
||||||
|
|
||||||
|
const ErrorPath errorPath = getErrorPath(tok, value, "Reading from empty container");
|
||||||
|
|
||||||
|
reportError(errorPath, value ? (value->errorSeverity() ? Severity::error : Severity::warning) : Severity::style, "reademptycontainer", "$symbol:" + varname +"\n" + errmsg, CWE398, !value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ public:
|
||||||
checkStl.redundantCondition();
|
checkStl.redundantCondition();
|
||||||
checkStl.missingComparison();
|
checkStl.missingComparison();
|
||||||
checkStl.readingEmptyStlContainer();
|
checkStl.readingEmptyStlContainer();
|
||||||
|
checkStl.readingEmptyStlContainer2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,6 +170,9 @@ public:
|
||||||
/** @brief Reading from empty stl container */
|
/** @brief Reading from empty stl container */
|
||||||
void readingEmptyStlContainer();
|
void readingEmptyStlContainer();
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Reading from empty stl container (using valueflow) */
|
||||||
|
void readingEmptyStlContainer2();
|
||||||
private:
|
private:
|
||||||
void readingEmptyStlContainer_parseUsage(const Token* tok, const Library::Container* container, std::map<unsigned int, const Library::Container*>& empty, bool noerror);
|
void readingEmptyStlContainer_parseUsage(const Token* tok, const Library::Container* container, std::map<unsigned int, const Library::Container*>& empty, bool noerror);
|
||||||
|
|
||||||
|
@ -205,7 +209,7 @@ private:
|
||||||
|
|
||||||
void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName);
|
void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName);
|
||||||
|
|
||||||
void readingEmptyStlContainerError(const Token* tok);
|
void readingEmptyStlContainerError(const Token* tok, const ValueFlow::Value *value=nullptr);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override {
|
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override {
|
||||||
CheckStl c(nullptr, settings, errorLogger);
|
CheckStl c(nullptr, settings, errorLogger);
|
||||||
|
|
|
@ -1374,62 +1374,68 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
if (tok->mValues->size() > 1U)
|
if (tok->mValues->size() > 1U)
|
||||||
out << '{';
|
out << '{';
|
||||||
}
|
}
|
||||||
for (std::list<ValueFlow::Value>::const_iterator it=tok->mValues->begin(); it!=tok->mValues->end(); ++it) {
|
for (const ValueFlow::Value &value : *tok->mValues) {
|
||||||
if (xml) {
|
if (xml) {
|
||||||
out << " <value ";
|
out << " <value ";
|
||||||
switch (it->valueType) {
|
switch (value.valueType) {
|
||||||
case ValueFlow::Value::INT:
|
case ValueFlow::Value::INT:
|
||||||
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
||||||
out << "intvalue=\"" << (MathLib::biguint)it->intvalue << '\"';
|
out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"';
|
||||||
else
|
else
|
||||||
out << "intvalue=\"" << it->intvalue << '\"';
|
out << "intvalue=\"" << value.intvalue << '\"';
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::TOK:
|
case ValueFlow::Value::TOK:
|
||||||
out << "tokvalue=\"" << it->tokvalue << '\"';
|
out << "tokvalue=\"" << value.tokvalue << '\"';
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::FLOAT:
|
case ValueFlow::Value::FLOAT:
|
||||||
out << "floatvalue=\"" << it->floatValue << '\"';
|
out << "floatvalue=\"" << value.floatValue << '\"';
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::MOVED:
|
case ValueFlow::Value::MOVED:
|
||||||
out << "movedvalue=\"" << ValueFlow::Value::toString(it->moveKind) << '\"';
|
out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"';
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::UNINIT:
|
case ValueFlow::Value::UNINIT:
|
||||||
out << "uninit=\"1\"";
|
out << "uninit=\"1\"";
|
||||||
break;
|
break;
|
||||||
|
case ValueFlow::Value::CONTAINER_SIZE:
|
||||||
|
out << "container-size=\"" << value.intvalue << '\"';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (it->condition)
|
if (value.condition)
|
||||||
out << " condition-line=\"" << it->condition->linenr() << '\"';
|
out << " condition-line=\"" << value.condition->linenr() << '\"';
|
||||||
if (it->isKnown())
|
if (value.isKnown())
|
||||||
out << " known=\"true\"";
|
out << " known=\"true\"";
|
||||||
else if (it->isPossible())
|
else if (value.isPossible())
|
||||||
out << " possible=\"true\"";
|
out << " possible=\"true\"";
|
||||||
else if (it->isInconclusive())
|
else if (value.isInconclusive())
|
||||||
out << " inconclusive=\"true\"";
|
out << " inconclusive=\"true\"";
|
||||||
out << "/>" << std::endl;
|
out << "/>" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (it != tok->mValues->begin())
|
if (&value != &tok->mValues->front())
|
||||||
out << ",";
|
out << ",";
|
||||||
switch (it->valueType) {
|
switch (value.valueType) {
|
||||||
case ValueFlow::Value::INT:
|
case ValueFlow::Value::INT:
|
||||||
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
||||||
out << (MathLib::biguint)it->intvalue;
|
out << (MathLib::biguint)value.intvalue;
|
||||||
else
|
else
|
||||||
out << it->intvalue;
|
out << value.intvalue;
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::TOK:
|
case ValueFlow::Value::TOK:
|
||||||
out << it->tokvalue->str();
|
out << value.tokvalue->str();
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::FLOAT:
|
case ValueFlow::Value::FLOAT:
|
||||||
out << it->floatValue;
|
out << value.floatValue;
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::MOVED:
|
case ValueFlow::Value::MOVED:
|
||||||
out << ValueFlow::Value::toString(it->moveKind);
|
out << ValueFlow::Value::toString(value.moveKind);
|
||||||
break;
|
break;
|
||||||
case ValueFlow::Value::UNINIT:
|
case ValueFlow::Value::UNINIT:
|
||||||
out << "Uninit";
|
out << "Uninit";
|
||||||
break;
|
break;
|
||||||
|
case ValueFlow::Value::CONTAINER_SIZE:
|
||||||
|
out << "size=" << value.intvalue;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
lib/token.h
10
lib/token.h
|
@ -875,6 +875,16 @@ public:
|
||||||
|
|
||||||
const ValueFlow::Value * getInvalidValue(const Token *ftok, unsigned int argnr, const Settings *settings) const;
|
const ValueFlow::Value * getInvalidValue(const Token *ftok, unsigned int argnr, const Settings *settings) const;
|
||||||
|
|
||||||
|
const ValueFlow::Value * getContainerSizeValue(const MathLib::bigint val) const {
|
||||||
|
if (!mValues)
|
||||||
|
return nullptr;
|
||||||
|
for (const ValueFlow::Value &value : *mValues) {
|
||||||
|
if (value.isContainerSizeValue() && value.intvalue == val)
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const Token *getValueTokenMaxStrLength() const;
|
const Token *getValueTokenMaxStrLength() const;
|
||||||
const Token *getValueTokenMinStrSize() const;
|
const Token *getValueTokenMinStrSize() const;
|
||||||
|
|
||||||
|
|
|
@ -3413,6 +3413,38 @@ static void valueFlowUninit(TokenList *tokenlist, SymbolDatabase * /*symbolDatab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void valueFlowContainerReverse(const Token *tok, unsigned int containerId, const ValueFlow::Value &value, const Settings *settings)
|
||||||
|
{
|
||||||
|
while (nullptr != (tok = tok->previous())) {
|
||||||
|
if (Token::Match(tok, "[{}]"))
|
||||||
|
break;
|
||||||
|
if (tok->varId() != containerId)
|
||||||
|
continue;
|
||||||
|
if (!tok->valueType() || !tok->valueType()->container)
|
||||||
|
continue;
|
||||||
|
setTokenValue(const_cast<Token *>(tok), value, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowContainerSize(TokenList * /*tokenlist*/, SymbolDatabase* symboldatabase, ErrorLogger * /*errorLogger*/, const Settings *settings)
|
||||||
|
{
|
||||||
|
for (const Scope &scope : symboldatabase->scopeList) {
|
||||||
|
if (scope.type != Scope::ScopeType::eIf) // TODO: while
|
||||||
|
continue;
|
||||||
|
for (const Token *tok = scope.classDef; tok && tok->str() != "{"; tok = tok->next()) {
|
||||||
|
if (!tok->isName() || !tok->valueType() || tok->valueType()->type != ValueType::CONTAINER)
|
||||||
|
continue;
|
||||||
|
if (!Token::Match(tok, "%name% . %name% ("))
|
||||||
|
continue;
|
||||||
|
if (tok->valueType()->container->getYield(tok->strAt(2)) != Library::Container::Yield::EMPTY)
|
||||||
|
continue;
|
||||||
|
ValueFlow::Value value(tok->tokAt(3), 0LL);
|
||||||
|
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
|
valueFlowContainerReverse(scope.classDef, tok->varId(), value, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ValueFlow::Value::Value(const Token *c, long long val)
|
ValueFlow::Value::Value(const Token *c, long long val)
|
||||||
: valueType(INT),
|
: valueType(INT),
|
||||||
intvalue(val),
|
intvalue(val),
|
||||||
|
@ -3442,6 +3474,8 @@ std::string ValueFlow::Value::infoString() const
|
||||||
return "<Moved>";
|
return "<Moved>";
|
||||||
case UNINIT:
|
case UNINIT:
|
||||||
return "<Uninit>";
|
return "<Uninit>";
|
||||||
|
case CONTAINER_SIZE:
|
||||||
|
return "size=" + MathLib::toString(intvalue);
|
||||||
};
|
};
|
||||||
throw InternalError(nullptr, "Invalid ValueFlow Value type");
|
throw InternalError(nullptr, "Invalid ValueFlow Value type");
|
||||||
}
|
}
|
||||||
|
@ -3479,6 +3513,8 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
valueFlowSubFunction(tokenlist, errorLogger, settings);
|
valueFlowSubFunction(tokenlist, errorLogger, settings);
|
||||||
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
|
if (tokenlist->isCPP())
|
||||||
|
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,9 @@ namespace ValueFlow {
|
||||||
break;
|
break;
|
||||||
case UNINIT:
|
case UNINIT:
|
||||||
break;
|
break;
|
||||||
|
case CONTAINER_SIZE:
|
||||||
|
if (intvalue != rhs.intvalue)
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return varvalue == rhs.varvalue &&
|
return varvalue == rhs.varvalue &&
|
||||||
|
@ -77,7 +80,7 @@ namespace ValueFlow {
|
||||||
|
|
||||||
std::string infoString() const;
|
std::string infoString() const;
|
||||||
|
|
||||||
enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT } valueType;
|
enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE } valueType;
|
||||||
bool isIntValue() const {
|
bool isIntValue() const {
|
||||||
return valueType == INT;
|
return valueType == INT;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +96,9 @@ namespace ValueFlow {
|
||||||
bool isUninitValue() const {
|
bool isUninitValue() const {
|
||||||
return valueType == UNINIT;
|
return valueType == UNINIT;
|
||||||
}
|
}
|
||||||
|
bool isContainerSizeValue() const {
|
||||||
|
return valueType == CONTAINER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/** int value */
|
/** int value */
|
||||||
long long intvalue;
|
long long intvalue;
|
||||||
|
|
|
@ -143,6 +143,7 @@ private:
|
||||||
TEST_CASE(dereference_auto);
|
TEST_CASE(dereference_auto);
|
||||||
|
|
||||||
TEST_CASE(readingEmptyStlContainer);
|
TEST_CASE(readingEmptyStlContainer);
|
||||||
|
TEST_CASE(readingEmptyStlContainer2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) {
|
void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) {
|
||||||
|
@ -3254,6 +3255,15 @@ private:
|
||||||
"}", true);
|
"}", true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void readingEmptyStlContainer2() {
|
||||||
|
check("void f(std::vector<int> v) {\n"
|
||||||
|
" v.front();\n"
|
||||||
|
" if (v.empty());\n"
|
||||||
|
"}\n",true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Reading from container 'v'. Either the condition 'v.empty()' is redundant or 'v' can be empty.\n", errout.str());
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestStl)
|
REGISTER_TEST(TestStl)
|
||||||
|
|
|
@ -103,6 +103,8 @@ private:
|
||||||
TEST_CASE(valueFlowInlineAssembly);
|
TEST_CASE(valueFlowInlineAssembly);
|
||||||
|
|
||||||
TEST_CASE(valueFlowUninit);
|
TEST_CASE(valueFlowUninit);
|
||||||
|
|
||||||
|
TEST_CASE(valueFlowContainerSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool testValueOfX(const char code[], unsigned int linenr, int value) {
|
bool testValueOfX(const char code[], unsigned int linenr, int value) {
|
||||||
|
@ -3202,6 +3204,31 @@ private:
|
||||||
ASSERT_EQUALS(true, values.front().isPossible() || values.back().isPossible());
|
ASSERT_EQUALS(true, values.front().isPossible() || values.back().isPossible());
|
||||||
ASSERT_EQUALS(true, values.front().intvalue == 0 || values.back().intvalue == 0);
|
ASSERT_EQUALS(true, values.front().intvalue == 0 || values.back().intvalue == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void valueFlowContainerSize() {
|
||||||
|
const char *code;
|
||||||
|
std::list<ValueFlow::Value> values;
|
||||||
|
|
||||||
|
LOAD_LIB_2(settings.library, "std.cfg");
|
||||||
|
|
||||||
|
// valueFlowContainerReverse
|
||||||
|
code = "void f(const std::list<int> &ints) {\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
" if (ints.empty()) {}\n"
|
||||||
|
"}";
|
||||||
|
values = tokenValues(code, "ints . front");
|
||||||
|
ASSERT_EQUALS(1, values.size());
|
||||||
|
ASSERT_EQUALS(true, values.empty() ? true : values.front().isContainerSizeValue());
|
||||||
|
ASSERT_EQUALS(0, values.empty() ? 0 : values.front().intvalue);
|
||||||
|
|
||||||
|
code = "void f(std::list<int> ints) {\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
" ints.pop_back();\n"
|
||||||
|
" if (ints.empty()) {}\n"
|
||||||
|
"}";
|
||||||
|
values = tokenValues(code, "ints . front");
|
||||||
|
ASSERT_EQUALS(true, values.empty());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestValueFlow)
|
REGISTER_TEST(TestValueFlow)
|
||||||
|
|
Loading…
Reference in New Issue