Exception safety: Removed the noisy checks and keep the useful checks
This commit is contained in:
parent
b2a775f3e0
commit
3a8e7b4bf0
|
@ -73,216 +73,6 @@ void CheckExceptionSafety::destructors()
|
|||
}
|
||||
|
||||
|
||||
void CheckExceptionSafety::unsafeNew()
|
||||
{
|
||||
if (!_settings->isEnabled("exceptNew"))
|
||||
return;
|
||||
|
||||
// Inspect initializer lists..
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (tok->str() != ")")
|
||||
continue;
|
||||
tok = tok->next();
|
||||
if (!tok)
|
||||
break;
|
||||
if (tok->str() != ":")
|
||||
continue;
|
||||
|
||||
// multiple "new" in an initializer list..
|
||||
std::string varname;
|
||||
for (tok = tok->next(); tok; tok = tok->next())
|
||||
{
|
||||
if (!Token::Match(tok, "%var% ("))
|
||||
break;
|
||||
tok = tok->next();
|
||||
if (Token::Match(tok->next(), "new %type%"))
|
||||
{
|
||||
if (!varname.empty())
|
||||
{
|
||||
unsafeNewError(tok->previous(), varname);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
// TODO: check if class is autodeallocated+might throw exception in constructor
|
||||
if (!_settings->isAutoDealloc(tok->strAt(2)))
|
||||
{
|
||||
varname = tok->strAt(-1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
tok = tok->link();
|
||||
tok = tok ? tok->next() : 0;
|
||||
if (!tok)
|
||||
break;
|
||||
if (tok->str() != ",")
|
||||
break;
|
||||
}
|
||||
if (!tok)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Inspect constructors..
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
// match this pattern.. "C :: C ( .. ) {"
|
||||
if (!tok->next() || tok->next()->str() != "::")
|
||||
continue;
|
||||
if (!Token::Match(tok, "%var% :: %var% ("))
|
||||
continue;
|
||||
if (tok->str() != tok->strAt(2))
|
||||
continue;
|
||||
if (!Token::simpleMatch(tok->tokAt(3)->link(), ") {"))
|
||||
continue;
|
||||
|
||||
// inspect the constructor..
|
||||
std::string varname;
|
||||
for (tok = tok->tokAt(3)->link()->tokAt(2); tok; tok = tok->next())
|
||||
{
|
||||
if (tok->str() == "{" || tok->str() == "}")
|
||||
break;
|
||||
// some variable declaration..
|
||||
if (Token::Match(tok->previous(), "[{;] %type% * %var% ;"))
|
||||
break;
|
||||
// allocating with new..
|
||||
if (Token::Match(tok, "%var% = new %type%"))
|
||||
{
|
||||
if (!varname.empty())
|
||||
{
|
||||
unsafeNewError(tok, varname);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
TODO: check is class is autodeallocated + might throw exceptions in the constructor
|
||||
if (!_settings->isAutoDealloc(tok->strAt(3)))
|
||||
varname = tok->str();
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// allocating multiple local variables..
|
||||
std::set<unsigned int> localVars;
|
||||
std::string varname;
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (tok->str() == "{" || tok->str() == "}")
|
||||
{
|
||||
localVars.clear();
|
||||
varname = "";
|
||||
}
|
||||
|
||||
if (Token::Match(tok, "[;{}] %type% * %var% ;") &&
|
||||
tok->tokAt(1)->isStandardType())
|
||||
{
|
||||
tok = tok->tokAt(3);
|
||||
if (tok->varId())
|
||||
localVars.insert(tok->varId());
|
||||
}
|
||||
|
||||
if (Token::Match(tok, "[;{}] %var% = new %type%"))
|
||||
{
|
||||
if (!varname.empty())
|
||||
{
|
||||
unsafeNewError(tok->next(), varname);
|
||||
break;
|
||||
}
|
||||
if (tok->next()->varId() && localVars.find(tok->next()->varId()) != localVars.end())
|
||||
varname = tok->strAt(1);
|
||||
}
|
||||
|
||||
else if (Token::Match(tok, "[;{}] %var% ("))
|
||||
{
|
||||
// False negatives: we don't handle switch cases properly so we just bail out.
|
||||
if (tok->strAt(1) == "switch")
|
||||
break;
|
||||
|
||||
for (tok = tok->next(); tok && !Token::Match(tok, "[;{}]"); tok = tok->next())
|
||||
{
|
||||
if (tok->str() == varname)
|
||||
{
|
||||
varname = "";
|
||||
}
|
||||
}
|
||||
if (!tok)
|
||||
break;
|
||||
}
|
||||
|
||||
else if (tok->str() == "delete")
|
||||
{
|
||||
if (Token::simpleMatch(tok->next(), varname.c_str()) ||
|
||||
Token::simpleMatch(tok->next(), ("[ ] " + varname).c_str()))
|
||||
{
|
||||
varname = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckExceptionSafety::realloc()
|
||||
{
|
||||
if (!_settings->isEnabled("exceptRealloc"))
|
||||
return;
|
||||
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (Token::simpleMatch(tok, "try {"))
|
||||
{
|
||||
tok = tok->next()->link();
|
||||
if (!tok)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Token::Match(tok, "[{};] delete"))
|
||||
continue;
|
||||
|
||||
tok = tok->tokAt(2);
|
||||
if (Token::simpleMatch(tok, "[ ]"))
|
||||
tok = tok->tokAt(2);
|
||||
if (!tok)
|
||||
break;
|
||||
|
||||
// reallocating..
|
||||
if (!Token::Match(tok, "%var% ; %var% = new %type%"))
|
||||
continue;
|
||||
|
||||
// variable id of deallocated pointer..
|
||||
const unsigned int varid(tok->varId());
|
||||
if (varid == 0)
|
||||
continue;
|
||||
|
||||
// variable id of allocated pointer must match..
|
||||
if (varid != tok->tokAt(2)->varId())
|
||||
continue;
|
||||
|
||||
// is is a class member variable..
|
||||
const Token *tok1 = Token::findmatch(_tokenizer->tokens(), "%varid%", varid);
|
||||
while (0 != (tok1 = tok1 ? tok1->previous() : 0))
|
||||
{
|
||||
if (tok1->str() == "}")
|
||||
tok1 = tok1->link();
|
||||
else if (tok1->str() == "{")
|
||||
{
|
||||
if (tok1->previous() && tok1->previous()->isName())
|
||||
{
|
||||
while (0 != (tok1 = tok1 ? tok1->previous() : 0))
|
||||
{
|
||||
if (!tok1->isName() && tok1->str() != ":" && tok1->str() != ",")
|
||||
break;
|
||||
if (tok1->str() == "class")
|
||||
{
|
||||
reallocError(tok->tokAt(2), tok->str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckExceptionSafety::deallocThrow()
|
||||
|
|
|
@ -55,8 +55,6 @@ public:
|
|||
{
|
||||
CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger);
|
||||
checkExceptionSafety.destructors();
|
||||
checkExceptionSafety.unsafeNew();
|
||||
checkExceptionSafety.realloc();
|
||||
checkExceptionSafety.deallocThrow();
|
||||
}
|
||||
|
||||
|
@ -64,12 +62,6 @@ public:
|
|||
/** Don't throw exceptions in destructors */
|
||||
void destructors();
|
||||
|
||||
/** unsafe use of "new" */
|
||||
void unsafeNew();
|
||||
|
||||
/** Unsafe realloc */
|
||||
void realloc();
|
||||
|
||||
/** deallocating memory and then throw */
|
||||
void deallocThrow();
|
||||
|
||||
|
@ -80,18 +72,6 @@ private:
|
|||
reportError(tok, Severity::style, "exceptThrowInDestructor", "Throwing exception in destructor");
|
||||
}
|
||||
|
||||
/** Unsafe use of new */
|
||||
void unsafeNewError(const Token * const tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "exceptNew", "Upon exception there is memory leak: " + varname);
|
||||
}
|
||||
|
||||
/** Unsafe reallocation */
|
||||
void reallocError(const Token * const tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "exceptRealloc", "Upon exception " + varname + " becomes a dead pointer");
|
||||
}
|
||||
|
||||
void deallocThrowError(const Token * const tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "exceptDeallocThrow", "Throwing exception in invalid state, " + varname + " points at deallocated memory");
|
||||
|
@ -101,8 +81,6 @@ private:
|
|||
void getErrorMessages()
|
||||
{
|
||||
destructorsError(0);
|
||||
unsafeNewError(0, "p");
|
||||
reallocError(0, "p");
|
||||
deallocThrowError(0, "p");
|
||||
}
|
||||
|
||||
|
@ -117,8 +95,6 @@ private:
|
|||
{
|
||||
return "Checking exception safety\n"
|
||||
"* Throwing exceptions in destructors\n"
|
||||
"* Unsafe use of 'new'\n"
|
||||
"* Unsafe reallocation\n"
|
||||
"* Throwing exception during invalid state";
|
||||
}
|
||||
};
|
||||
|
|
|
@ -602,8 +602,6 @@ bool CppCheck::parseFromArgs(int argc, const char* const argv[])
|
|||
" Example: -DDEBUG=1 -D__cplusplus\n"
|
||||
" --enable=id Enable additional checks. The available ids are:\n"
|
||||
" * all - enable all checks\n"
|
||||
" * exceptNew - exception safety when using new\n"
|
||||
" * exceptRealloc - exception safety when reallocating\n"
|
||||
" * style - Check coding style\n"
|
||||
" * unusedFunctions - check for unused functions\n"
|
||||
" Several ids can be given if you separate them with commas\n"
|
||||
|
|
|
@ -188,8 +188,6 @@ std::string Settings::addEnabled(const std::string &str)
|
|||
handled = _checkCodingStyle = true;
|
||||
|
||||
std::set<std::string> id;
|
||||
id.insert("exceptNew");
|
||||
id.insert("exceptRealloc");
|
||||
id.insert("unusedFunctions");
|
||||
|
||||
if (str == "all")
|
||||
|
|
|
@ -35,9 +35,6 @@ private:
|
|||
void run()
|
||||
{
|
||||
TEST_CASE(destructors);
|
||||
TEST_CASE(newnew);
|
||||
TEST_CASE(switchnewnew);
|
||||
TEST_CASE(realloc);
|
||||
TEST_CASE(deallocThrow);
|
||||
}
|
||||
|
||||
|
@ -68,118 +65,6 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:3]: (style) Throwing exception in destructor\n", errout.str());
|
||||
}
|
||||
|
||||
void newnew()
|
||||
{
|
||||
check("C::C() : a(new A), b(new B) { }");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:1]: (style) Upon exception there is memory leak: a\n", errout.str());
|
||||
|
||||
check("C::C()\n"
|
||||
"{\n"
|
||||
" a = new A;\n"
|
||||
" b = new B;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:4]: (style) Upon exception there is memory leak: a\n", errout.str());
|
||||
|
||||
check("void a()\n"
|
||||
"{\n"
|
||||
" A *a1 = new A;\n"
|
||||
" A *a2 = new A;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:4]: (style) Upon exception there is memory leak: a1\n", errout.str());
|
||||
|
||||
check("void a()\n"
|
||||
"{\n"
|
||||
" A *a1 = new A;\n"
|
||||
" A *a2 = new (std::nothrow) A;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void a()\n"
|
||||
"{\n"
|
||||
" A *a1 = new A;\n"
|
||||
" delete a1;\n"
|
||||
" A *a2 = new A;\n"
|
||||
" delete a2;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void a()\n"
|
||||
"{\n"
|
||||
" A *a = new A;\n"
|
||||
" B *b = new B;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// passing pointer to unknown function.. the pointer may be added to some list etc..
|
||||
check("void f()\n"
|
||||
"{\n"
|
||||
" A *a1 = new A;\n"
|
||||
" add(a1);\n"
|
||||
" A *a2 = new A;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void switchnewnew()
|
||||
{
|
||||
check("int *f(int x)\n"
|
||||
"{\n"
|
||||
" int *p = 0;\n"
|
||||
" switch(x)\n"
|
||||
" {\n"
|
||||
" case 1:\n"
|
||||
" p = new int(10);\n"
|
||||
" break;\n"
|
||||
" case 2:\n"
|
||||
" p = new int(100);\n"
|
||||
" break;\n"
|
||||
" };\n"
|
||||
" return p;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void realloc()
|
||||
{
|
||||
check("class A\n"
|
||||
"{\n"
|
||||
" int *p;\n"
|
||||
" void a()\n"
|
||||
" {\n"
|
||||
" delete p;\n"
|
||||
" p = new int[123];\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:7]: (style) Upon exception p becomes a dead pointer\n", errout.str());
|
||||
|
||||
check("class A\n"
|
||||
"{\n"
|
||||
" int *p;\n"
|
||||
" void a()\n"
|
||||
" {\n"
|
||||
" delete p;\n"
|
||||
" p = new (std::nothrow) int[123];\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("class A\n"
|
||||
"{\n"
|
||||
" int *p;\n"
|
||||
" void a()\n"
|
||||
" {\n"
|
||||
" try {\n"
|
||||
" delete p;\n"
|
||||
" p = new int[123];\n"
|
||||
" } catch (...) { p = 0; }\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void deallocThrow()
|
||||
{
|
||||
check("int * p;\n"
|
||||
|
|
Loading…
Reference in New Issue