Fixed #4800 (Check unhandled exceptions)
This commit is contained in:
parent
a9990bbe33
commit
36f4431481
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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) */
|
||||
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";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue