Exception safety: refactorings, use the symbol database

This commit is contained in:
PKEuS 2011-12-09 19:53:00 +01:00 committed by Daniel Marjamäki
parent 8ca8887849
commit f46cf5fd65
2 changed files with 69 additions and 70 deletions

View File

@ -18,7 +18,7 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "checkexceptionsafety.h" #include "checkexceptionsafety.h"
#include "symboldatabase.h"
#include "token.h" #include "token.h"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -37,43 +37,33 @@ void CheckExceptionSafety::destructors()
if (!_settings->isEnabled("style")) if (!_settings->isEnabled("style"))
return; return;
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
// Perform check.. // Perform check..
for (const Token * tok = _tokenizer->tokens(); tok; tok = tok->next()) { for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
// Skip executable scopes for (std::list<Function>::const_iterator j = i->functionList.begin(); j != i->functionList.end(); ++j) {
if (Token::simpleMatch(tok, ") {"))
tok = tok->next()->link();
// only looking for destructors // only looking for destructors
if (!Token::Match(tok, "~ %var% ( ) {")) if (j->type == Function::eDestructor && j->start) {
continue;
// Inspect this destructor.. // Inspect this destructor..
unsigned int indentlevel = 0; for (const Token *tok = j->start->next(); tok != j->start->link(); tok = tok->next()) {
for (const Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) {
if (tok2->str() == "{") {
++indentlevel;
}
else if (tok2->str() == "}") {
if (indentlevel <= 1)
break;
--indentlevel;
}
// throw found within a destructor // throw found within a destructor
else if (tok2->str() == "throw") { if (tok->str() == "throw") {
destructorsError(tok2); destructorsError(tok);
break; break;
} }
} }
} }
} }
}
}
void CheckExceptionSafety::deallocThrow() void CheckExceptionSafety::deallocThrow()
{ {
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
// Deallocate a global/member pointer and then throw exception // Deallocate a global/member pointer and then throw exception
// the pointer will be a dead pointer // the pointer will be a dead pointer
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
@ -93,35 +83,10 @@ void CheckExceptionSafety::deallocThrow()
if (varid == 0) if (varid == 0)
continue; continue;
// is this variable a global variable? // we only look for global variables
{ const Variable* var = symbolDatabase->getVariableFromVarId(varid);
// TODO: Isn't it better to use symbol database instead? if (!var || !var->isGlobal())
bool globalVar = false;
for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) {
if (tok2->varId() == varid) {
globalVar = true;
break;
}
if (tok2->str() == "class") {
while (tok2 && tok2->str() != ";" && tok2->str() != "{")
tok2 = tok2->next();
tok2 = tok2 ? tok2->next() : 0;
if (!tok2)
break;
}
if (tok2->str() == "{") {
tok2 = tok2->link();
if (!tok2)
break;
}
}
// Not a global variable.. skip checking this var.
if (!globalVar)
continue; continue;
}
// indentlevel.. // indentlevel..
unsigned int indentlevel = 0; unsigned int indentlevel = 0;
@ -139,7 +104,7 @@ void CheckExceptionSafety::deallocThrow()
--indentlevel; --indentlevel;
} }
if (tok2->str() == "throw") else if (tok2->str() == "throw")
ThrowToken = tok2; ThrowToken = tok2;
// if the variable is not assigned after the throw then it // if the variable is not assigned after the throw then it
@ -163,20 +128,27 @@ void CheckExceptionSafety::checkRethrowCopy()
{ {
if (!_settings->isEnabled("style")) if (!_settings->isEnabled("style"))
return; return;
const char catchPattern[] = "catch ( const| %type% &|*| %var% ) { %any%";
const Token *tok = Token::findmatch(_tokenizer->tokens(), catchPattern); const char catchPattern1[] = "catch (";
const char catchPattern2[] = "%var% ) { %any%";
const Token* tok = Token::findsimplematch(_tokenizer->tokens(), catchPattern1);
while (tok) { while (tok) {
const Token *startBlockTok = tok->next()->link()->next(); const Token* endScopeTok = tok->next();
const Token *endBlockTok = startBlockTok->link(); const Token* endBracketTok = tok->next()->link();
const unsigned int varid = startBlockTok->tokAt(-2)->varId();
const Token* rethrowTok = Token::findmatch(startBlockTok, "throw %varid%", endBlockTok, varid); if (endBracketTok && Token::Match(endBracketTok->previous(), catchPattern2)) {
const Token* startScopeTok = endBracketTok->next();
endScopeTok = startScopeTok->link();
const unsigned int varid = endBracketTok->previous()->varId();
const Token* rethrowTok = Token::findmatch(startScopeTok->next(), "throw %varid%", endScopeTok->previous(), varid);
if (rethrowTok) { if (rethrowTok) {
rethrowCopyError(rethrowTok, startBlockTok->strAt(-2)); rethrowCopyError(rethrowTok, endBracketTok->strAt(-1));
} }
}
tok = Token::findmatch(endBlockTok->next(), catchPattern);
tok = Token::findsimplematch(endScopeTok->next(), catchPattern1);
} }
} }

View File

@ -38,6 +38,7 @@ private:
TEST_CASE(rethrowCopy1); TEST_CASE(rethrowCopy1);
TEST_CASE(rethrowCopy2); TEST_CASE(rethrowCopy2);
TEST_CASE(rethrowCopy3); TEST_CASE(rethrowCopy3);
TEST_CASE(rethrowCopy4);
} }
void check(const std::string &code) { void check(const std::string &code) {
@ -59,11 +60,25 @@ private:
} }
void destructors() { void destructors() {
check("x::~x()\n" check("class x {\n"
"{\n" " ~x() {\n"
" throw e;\n" " throw e;\n"
"}\n"); " }\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (error) Throwing exception in destructor\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (error) Throwing exception in destructor\n", errout.str());
check("class x {\n"
" ~x();\n"
"};\n"
"x::~x() {\n"
" throw e;\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Throwing exception in destructor\n", errout.str());
check("x::~x() {\n"
" throw e;\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Throwing exception in destructor\n", "", errout.str());
} }
void deallocThrow1() { void deallocThrow1() {
@ -117,6 +132,18 @@ private:
} }
void rethrowCopy3() { void rethrowCopy3() {
check("void f() {\n"
" try {\n"
" foo();\n"
" }\n"
" catch(std::runtime_error err) {\n"
" throw err;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:6]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception\n", errout.str());
}
void rethrowCopy4() {
check("void f() {\n" check("void f() {\n"
" try\n" " try\n"
" {\n" " {\n"