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();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue