Fixed #3796 (new check: redundant initialization with empty string)
This commit is contained in:
parent
470f1579c1
commit
f8a4fb91fe
|
@ -1553,3 +1553,90 @@ void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::st
|
||||||
"derefInvalidIterator", "Possible dereference of an invalid iterator: " + iterName + "\n" +
|
"derefInvalidIterator", "Possible dereference of an invalid iterator: " + iterName + "\n" +
|
||||||
"Make sure to check that the iterator is valid before dereferencing it - not after.");
|
"Make sure to check that the iterator is valid before dereferencing it - not after.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CheckStl::readingEmptyStlContainer()
|
||||||
|
{
|
||||||
|
if (!_settings->isEnabled("style"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_settings->inconclusive) // check only if inconclusive true;
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::map<unsigned int, bool> empty; //map to keep track of whether the StlContainer has been given value atleast once.
|
||||||
|
|
||||||
|
static const char *STL_CONTAINERS[] = {"map", "vector"};
|
||||||
|
|
||||||
|
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
std::size_t varListSize = symbolDatabase->getVariableListSize();
|
||||||
|
for (std::size_t i = 1; i < varListSize; ++i) {
|
||||||
|
// to include stl containers declared in file in used map
|
||||||
|
const Variable* var = symbolDatabase->getVariableFromVarId(i);
|
||||||
|
if (var && var->isLocal() && var->isStlType(STL_CONTAINERS))
|
||||||
|
empty[var->declarationId()] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||||
|
bool stl1_exist = false;
|
||||||
|
bool stl2_exist = false;
|
||||||
|
bool stl2_isEmpty = true;
|
||||||
|
|
||||||
|
if (tok->variable() && tok->variable()->isStlType(STL_CONTAINERS)) {
|
||||||
|
if (empty.find(tok->varId()) != empty.end())
|
||||||
|
stl1_exist = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// to check for various conditions for the way stl containers and variables can be used
|
||||||
|
if (Token::Match(tok, "%var% =|[")) {
|
||||||
|
const Token *tok2;
|
||||||
|
|
||||||
|
if (Token::Match(tok->next(), "="))
|
||||||
|
tok2 = tok->tokAt(2);
|
||||||
|
|
||||||
|
// to check cases like Cmap[1]; or i = Cmap[1] -- the right wld evaluate true when token reaches it.
|
||||||
|
else if (Token::Match(tok->next()," [") && Token::Match(tok->linkAt(1),"] ="))
|
||||||
|
tok2 = tok->next()->link()->tokAt(2);
|
||||||
|
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// rhs variable
|
||||||
|
if (tok2->variable() && tok2->variable()->isStlType(STL_CONTAINERS)) {
|
||||||
|
if (empty.find(tok2->varId()) != empty.end()) {
|
||||||
|
stl2_exist = true;
|
||||||
|
stl2_isEmpty = empty[tok2->varId()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if both var are Stl Container...
|
||||||
|
if (stl1_exist && stl2_exist) {
|
||||||
|
if (stl2_isEmpty)
|
||||||
|
readingEmptyStlContainerError(tok2);
|
||||||
|
else
|
||||||
|
empty[tok->varId()] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if only second var is Stl Container
|
||||||
|
else if (stl2_exist && stl2_isEmpty)
|
||||||
|
readingEmptyStlContainerError(tok);
|
||||||
|
|
||||||
|
// if only first var is stl container
|
||||||
|
else if (stl1_exist)
|
||||||
|
empty[tok->varId()] = false;
|
||||||
|
} else if (stl1_exist && Token::Match(tok, "%var% [ %any% ] = %any%")) {
|
||||||
|
empty[tok->varId()] = false;
|
||||||
|
} else if (stl1_exist && Token::Match(tok, "%var% . %type% (")) {
|
||||||
|
//static const char STL_CONTAINER_INSERT_FUNCTIONS[] = "%var% . insert|push_back|push_front|assign|set|reset|emplace_after|insert_after|push (";
|
||||||
|
empty[tok->varId()] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckStl::readingEmptyStlContainerError(const Token *tok)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::style, "reademptycontainer", "Reading from empty STL container", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
checkStl.size();
|
checkStl.size();
|
||||||
checkStl.redundantCondition();
|
checkStl.redundantCondition();
|
||||||
checkStl.missingComparison();
|
checkStl.missingComparison();
|
||||||
|
checkStl.readingEmptyStlContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,6 +147,9 @@ public:
|
||||||
*/
|
*/
|
||||||
void dereferenceErasedError(const Token* erased, const Token* deref, const std::string &itername);
|
void dereferenceErasedError(const Token* erased, const Token* deref, const std::string &itername);
|
||||||
|
|
||||||
|
/** @brief Reading from empty stl container */
|
||||||
|
void readingEmptyStlContainer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,6 +188,8 @@ 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
CheckStl c(0, settings, errorLogger);
|
CheckStl c(0, settings, errorLogger);
|
||||||
c.invalidIteratorError(0, "iterator");
|
c.invalidIteratorError(0, "iterator");
|
||||||
|
@ -211,6 +217,7 @@ private:
|
||||||
c.uselessCallsEmptyError(0);
|
c.uselessCallsEmptyError(0);
|
||||||
c.uselessCallsRemoveError(0, "remove");
|
c.uselessCallsRemoveError(0, "remove");
|
||||||
c.dereferenceInvalidIteratorError(0, "i");
|
c.dereferenceInvalidIteratorError(0, "i");
|
||||||
|
c.readingEmptyStlContainerError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
@ -230,7 +237,8 @@ private:
|
||||||
"* common mistakes when using string::c_str()\n"
|
"* common mistakes when using string::c_str()\n"
|
||||||
"* using auto pointer (auto_ptr)\n"
|
"* using auto pointer (auto_ptr)\n"
|
||||||
"* useless calls of string and STL functions\n"
|
"* useless calls of string and STL functions\n"
|
||||||
"* dereferencing an invalid iterator\n";
|
"* dereferencing an invalid iterator\n"
|
||||||
|
"* reading from empty STL container\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -128,6 +128,7 @@ private:
|
||||||
TEST_CASE(dereferenceInvalidIterator);
|
TEST_CASE(dereferenceInvalidIterator);
|
||||||
TEST_CASE(dereference_auto);
|
TEST_CASE(dereference_auto);
|
||||||
|
|
||||||
|
TEST_CASE(readingEmptyStlContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], const bool inconclusive=false) {
|
void check(const char code[], const bool inconclusive=false) {
|
||||||
|
@ -2394,6 +2395,72 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void readingEmptyStlContainer() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::map<int, std::string> CMap;\n"
|
||||||
|
" std::string strValue = CMap[1]; \n"
|
||||||
|
" std::cout << strValue << CMap.size() << std::endl;\n"
|
||||||
|
"}\n",true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::map<int,std::string> CMap;\n"
|
||||||
|
" std::string strValue = CMap[1];"
|
||||||
|
"}\n",true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::map<int,std::string> CMap;\n"
|
||||||
|
" CMap[1] = \"123\";\n"
|
||||||
|
" std::string strValue = CMap[1];"
|
||||||
|
"}\n",true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<std::string> f() {\n"
|
||||||
|
" try {\n"
|
||||||
|
" std::vector<std::string> Vector;\n"
|
||||||
|
" std::vector<std::string> v2 = Vector;\n"
|
||||||
|
" std::string strValue = v2[1]; \n"
|
||||||
|
" }\n"
|
||||||
|
" return Vector;\n"
|
||||||
|
"}\n",true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Reading from empty STL container\n"
|
||||||
|
"[test.cpp:5]: (style, inconclusive) Reading from empty STL container\n", errout.str());
|
||||||
|
|
||||||
|
check("f() {\n"
|
||||||
|
" try {\n"
|
||||||
|
" std::vector<std::string> Vector;\n"
|
||||||
|
" Vector.push_back(\"123\");\n"
|
||||||
|
" std::vector<std::string> v2 = Vector;\n"
|
||||||
|
" std::string strValue = v2[0]; \n"
|
||||||
|
" }\n"
|
||||||
|
" return Vector;\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::map<std::string,std::string> mymap;\n"
|
||||||
|
" mymap[\"Bakery\"] = \"Barbara\";\n"
|
||||||
|
" std:string bakery_name = mymap[\"Bakery\"];\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::vector<int> v;\n"
|
||||||
|
" v.insert(1);\n"
|
||||||
|
" int i = v[0];\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::map<int, std::string> CMap;\n"
|
||||||
|
" std::string strValue = CMap[1];\n"
|
||||||
|
" std::string strValue2 = CMap[1];\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container\n"
|
||||||
|
"[test.cpp:4]: (style, inconclusive) Reading from empty STL container\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestStl)
|
REGISTER_TEST(TestStl)
|
||||||
|
|
Loading…
Reference in New Issue