Fixed #4800 (Check unhandled exceptions)

This commit is contained in:
Robert Reif 2014-04-20 08:58:36 +02:00 committed by Daniel Marjamäki
parent a9990bbe33
commit 36f4431481
3 changed files with 112 additions and 13 deletions

View File

@ -188,12 +188,26 @@ void CheckExceptionSafety::noexceptThrows()
const std::size_t functions = symbolDatabase->functionScopes.size(); const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) { for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i]; const Scope * scope = symbolDatabase->functionScopes[i];
// onlycheck noexcept functions // only check noexcept functions
if (scope->function && scope->function->isNoExcept && if (scope->function && scope->function->isNoExcept &&
(!scope->function->noexceptArg || scope->function->noexceptArg->str() == "true")) { (!scope->function->noexceptArg || scope->function->noexceptArg->str() == "true")) {
for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->str() == "throw") { if (tok->str() == "try") {
break;
} else if (tok->str() == "throw") {
noexceptThrowError(tok); noexceptThrowError(tok);
break;
} else if (tok->function()) {
const Function * called = tok->function();
// check if called function has an exception specification
if (called->isThrow && called->throwArg) {
noexceptThrowError(tok);
break;
} else if (called->isNoExcept && called->noexceptArg &&
called->noexceptArg->str() != "true") {
noexceptThrowError(tok);
break;
}
} }
} }
} }
@ -210,11 +224,54 @@ void CheckExceptionSafety::nothrowThrows()
const std::size_t functions = symbolDatabase->functionScopes.size(); const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) { for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i]; const Scope * scope = symbolDatabase->functionScopes[i];
// onlycheck throw() functions // only check throw() functions
if (scope->function && scope->function->isThrow && !scope->function->throwArg) { if (scope->function && scope->function->isThrow && !scope->function->throwArg) {
for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->str() == "throw") { if (tok->str() == "try") {
break;
} else if (tok->str() == "throw") {
nothrowThrowError(tok); nothrowThrowError(tok);
break;
} else if (tok->function()) {
const Function * called = tok->function();
// check if called function has an exception specification
if (called->isThrow && called->throwArg) {
nothrowThrowError(tok);
break;
} else if (called->isNoExcept && called->noexceptArg &&
called->noexceptArg->str() != "true") {
nothrowThrowError(tok);
break;
}
}
}
}
}
}
//--------------------------------------------------------------------------
// void func() { functionWithExceptionSpecification(); }
//--------------------------------------------------------------------------
void CheckExceptionSafety::unhandledExceptionSpecification()
{
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
// only check functions without exception epecification
if (scope->function && !scope->function->isThrow) {
for (const Token *tok = scope->function->functionScope->classStart->next();
tok != scope->function->functionScope->classEnd; tok = tok->next()) {
if (tok->str() == "try") {
break;
} else if (tok->function()) {
const Function * called = tok->function();
// check if called function has an exception specification
if (called->isThrow && called->throwArg) {
unhandledExceptionSpecificationError(tok, called->tokenDef, scope->function->name());
break;
}
} }
} }
} }

View File

@ -63,6 +63,7 @@ public:
checkExceptionSafety.checkCatchExceptionByValue(); checkExceptionSafety.checkCatchExceptionByValue();
checkExceptionSafety.noexceptThrows(); checkExceptionSafety.noexceptThrows();
checkExceptionSafety.nothrowThrows(); checkExceptionSafety.nothrowThrows();
checkExceptionSafety.unhandledExceptionSpecification();
} }
/** Don't throw exceptions in destructors */ /** Don't throw exceptions in destructors */
@ -83,6 +84,9 @@ public:
/** @brief %Check for throw() functions that throw */ /** @brief %Check for throw() functions that throw */
void nothrowThrows(); void nothrowThrows();
/** @brief %Check for unhandled exception specification */
void unhandledExceptionSpecification();
private: private:
/** Don't throw exceptions in destructors */ /** Don't throw exceptions in destructors */
void destructorsError(const Token * const tok) { void destructorsError(const Token * const tok) {
@ -118,6 +122,18 @@ private:
reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function."); reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function.");
} }
/** Missing exception specification */
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
std::string str1(tok1 ? tok1->str() : "foo");
std::list<const Token*> locationList;
locationList.push_back(tok1);
locationList.push_back(tok2);
reportError(locationList, Severity::warning, "unhandledExceptionSpecification",
"Unhandled exception specification when calling function " + str1 + "().\n"
"Unhandled exception specification when calling function " + str1 + "(). "
"Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.");
}
/** Generate all possible errors (for --errorlist) */ /** Generate all possible errors (for --errorlist) */
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
CheckExceptionSafety c(0, settings, errorLogger); CheckExceptionSafety c(0, settings, errorLogger);
@ -127,6 +143,7 @@ private:
c.catchExceptionByValueError(0); c.catchExceptionByValueError(0);
c.noexceptThrowError(0); c.noexceptThrowError(0);
c.nothrowThrowError(0); c.nothrowThrowError(0);
c.unhandledExceptionSpecificationError(0, 0, "funcname");
} }
/** Short description of class (for --doc) */ /** Short description of class (for --doc) */
@ -142,7 +159,8 @@ private:
"* Throwing a copy of a caught exception instead of rethrowing the original exception\n" "* Throwing a copy of a caught exception instead of rethrowing the original exception\n"
"* Exception caught by value instead of by reference\n" "* Exception caught by value instead of by reference\n"
"* Throwing exception in noexcept function\n" "* Throwing exception in noexcept function\n"
"* Throwing exception in nothrow() function\n"; "* Throwing exception in nothrow() function\n"
"* Unhandled exception specification when calling function foo()\n";
} }
}; };
/// @} /// @}

View File

@ -44,6 +44,7 @@ private:
TEST_CASE(catchExceptionByValue); TEST_CASE(catchExceptionByValue);
TEST_CASE(noexceptThrow); TEST_CASE(noexceptThrow);
TEST_CASE(nothrowThrow); TEST_CASE(nothrowThrow);
TEST_CASE(unhandledExceptionSpecification); // #4800
} }
void check(const char code[], bool inconclusive = false) { void check(const char code[], bool inconclusive = false) {
@ -312,11 +313,15 @@ private:
} }
void noexceptThrow() { void noexceptThrow() {
check("void func1() noexcept { throw 1; }\n" check("void func1() noexcept(false) { throw 1; }\n"
"void func2() noexcept(true) { throw 1; }\n" "void func2() noexcept { throw 1; }\n"
"void func3() noexcept(false) { throw 1; }\n"); "void func3() noexcept(true) { throw 1; }\n"
ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in noexcept function.\n" "void func4() noexcept(false) { throw 1; }\n"
"[test.cpp:2]: (error) Exception thrown in noexcept function.\n", errout.str()); "void func5() noexcept(true) { func1(); }\n"
"void func6() noexcept(false) { func1(); }\n");
ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in noexcept function.\n"
"[test.cpp:3]: (error) Exception thrown in noexcept function.\n"
"[test.cpp:5]: (error) Exception thrown in noexcept function.\n", errout.str());
// avoid false positives // avoid false positives
check("const char *func() noexcept { return 0; }\n"); check("const char *func() noexcept { return 0; }\n");
@ -324,14 +329,33 @@ private:
} }
void nothrowThrow() { void nothrowThrow() {
check("void func1() throw() { throw 1; }\n" check("void func1() throw(int) { throw 1; }\n"
"void func2() throw(int) { throw 1; }\n"); "void func2() throw() { throw 1; }\n"
ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in throw() function.\n", errout.str()); "void func3() throw(int) { throw 1; }\n"
"void func4() throw() { func1(); }\n"
"void func5() throw(int) { func1(); }\n");
ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in throw() function.\n"
"[test.cpp:4]: (error) Exception thrown in throw() function.\n", errout.str());
// avoid false positives // avoid false positives
check("const char *func() throw() { return 0; }\n"); check("const char *func() throw() { return 0; }\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void unhandledExceptionSpecification() { // #4800
check("void myThrowingFoo() throw(MyException) {\n"
" throw MyException();\n"
"}\n"
"void myNonCatchingFoo() {\n"
" myThrowingFoo();\n"
"}\n"
"void myCatchingFoo() {\n"
" try {\n"
" myThrowingFoo();\n"
" } catch(MyException &) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (warning) Unhandled exception specification when calling function myThrowingFoo().\n", errout.str());
}
}; };
REGISTER_TEST(TestExceptionSafety) REGISTER_TEST(TestExceptionSafety)