From 36f44314817e572c5629ebf6f1939f2f36e02f9a Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Sun, 20 Apr 2014 08:58:36 +0200 Subject: [PATCH] Fixed #4800 (Check unhandled exceptions) --- lib/checkexceptionsafety.cpp | 65 +++++++++++++++++++++++++++++++++--- lib/checkexceptionsafety.h | 20 ++++++++++- test/testexceptionsafety.cpp | 40 +++++++++++++++++----- 3 files changed, 112 insertions(+), 13 deletions(-) diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index fefdf7531..9d7ba51a5 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -188,12 +188,26 @@ void CheckExceptionSafety::noexceptThrows() const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; - // onlycheck noexcept functions + // only check noexcept functions if (scope->function && scope->function->isNoExcept && (!scope->function->noexceptArg || scope->function->noexceptArg->str() == "true")) { 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); + 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(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; - // onlycheck throw() functions + // only check throw() functions if (scope->function && scope->function->isThrow && !scope->function->throwArg) { 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); + 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; + } } } } diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index 3740930a6..1ecd3ee10 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -63,6 +63,7 @@ public: checkExceptionSafety.checkCatchExceptionByValue(); checkExceptionSafety.noexceptThrows(); checkExceptionSafety.nothrowThrows(); + checkExceptionSafety.unhandledExceptionSpecification(); } /** Don't throw exceptions in destructors */ @@ -83,6 +84,9 @@ public: /** @brief %Check for throw() functions that throw */ void nothrowThrows(); + /** @brief %Check for unhandled exception specification */ + void unhandledExceptionSpecification(); + private: /** Don't throw exceptions in destructors */ void destructorsError(const Token * const tok) { @@ -118,6 +122,18 @@ private: 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 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) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckExceptionSafety c(0, settings, errorLogger); @@ -127,6 +143,7 @@ private: c.catchExceptionByValueError(0); c.noexceptThrowError(0); c.nothrowThrowError(0); + c.unhandledExceptionSpecificationError(0, 0, "funcname"); } /** 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" "* Exception caught by value instead of by reference\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"; } }; /// @} diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index fa415b49f..1fc1636cc 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -44,6 +44,7 @@ private: TEST_CASE(catchExceptionByValue); TEST_CASE(noexceptThrow); TEST_CASE(nothrowThrow); + TEST_CASE(unhandledExceptionSpecification); // #4800 } void check(const char code[], bool inconclusive = false) { @@ -312,11 +313,15 @@ private: } void noexceptThrow() { - check("void func1() noexcept { throw 1; }\n" - "void func2() noexcept(true) { throw 1; }\n" - "void func3() noexcept(false) { throw 1; }\n"); - ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in noexcept function.\n" - "[test.cpp:2]: (error) Exception thrown in noexcept function.\n", errout.str()); + check("void func1() noexcept(false) { throw 1; }\n" + "void func2() noexcept { throw 1; }\n" + "void func3() noexcept(true) { throw 1; }\n" + "void func4() noexcept(false) { throw 1; }\n" + "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 check("const char *func() noexcept { return 0; }\n"); @@ -324,14 +329,33 @@ private: } void nothrowThrow() { - check("void func1() throw() { throw 1; }\n" - "void func2() throw(int) { throw 1; }\n"); - ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in throw() function.\n", errout.str()); + check("void func1() throw(int) { throw 1; }\n" + "void func2() throw() { throw 1; }\n" + "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 check("const char *func() throw() { return 0; }\n"); 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)