Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions lib/checkexceptionsafety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,32 +296,35 @@ void CheckExceptionSafety::nothrowThrows()
if (!function)
continue;

// check noexcept and noexcept(true) functions
if (function->isNoExcept()) {
const Token *throws = functionThrows(function);
if (throws)
noexceptThrowError(throws);
bool isNoExcept = false, isEntryPoint = false;
if (function->isNoExcept() || // noexcept and noexcept(true) functions
(function->isThrow() && !function->throwArg) || // throw() functions
function->isAttributeNothrow()) { // __attribute__((nothrow)) or __declspec(nothrow) functions
isNoExcept = true;
}

// check throw() functions
else if (function->isThrow() && !function->throwArg) {
const Token *throws = functionThrows(function);
if (throws)
noexceptThrowError(throws);
else if (mSettings->library.isentrypoint(function->name())) {
isEntryPoint = true;
}
if (!isNoExcept && !isEntryPoint)
continue;

// check __attribute__((nothrow)) or __declspec(nothrow) functions
else if (function->isAttributeNothrow()) {
const Token *throws = functionThrows(function);
if (throws)
if (const Token* throws = functionThrows(function)) {
if (isEntryPoint)
entryPointThrowError(throws);
else
noexceptThrowError(throws);
}
}
}

void CheckExceptionSafety::noexceptThrowError(const Token * const tok)
{
reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
reportError(tok, Severity::error, "throwInNoexceptFunction", "Unhandled exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
}

void CheckExceptionSafety::entryPointThrowError(const Token * const tok)
{
reportError(tok, Severity::error, "throwInEntryPoint", "Unhandled exception thrown in function that is an entry point.", CWE398, Certainty::normal);
}

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -433,6 +436,7 @@ void CheckExceptionSafety::getErrorMessages(ErrorLogger *errorLogger, const Sett
c.rethrowCopyError(nullptr, "varname");
c.catchExceptionByValueError(nullptr);
c.noexceptThrowError(nullptr);
c.entryPointThrowError(nullptr);
c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname");
c.rethrowNoCurrentExceptionError(nullptr);
}
1 change: 1 addition & 0 deletions lib/checkexceptionsafety.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class CPPCHECKLIB CheckExceptionSafety : public Check {
void rethrowCopyError(const Token * tok, const std::string &varname);
void catchExceptionByValueError(const Token *tok);
void noexceptThrowError(const Token * tok);
void entryPointThrowError(const Token * tok);
/** Missing exception specification */
void unhandledExceptionSpecificationError(const Token * tok1, const Token * tok2, const std::string & funcname);
/** Rethrow without currently handled exception */
Expand Down
60 changes: 45 additions & 15 deletions test/testexceptionsafety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class TestExceptionSafety : public TestFixture {
TEST_CASE(rethrowNoCurrentException2);
TEST_CASE(rethrowNoCurrentException3);
TEST_CASE(noFunctionCall);
TEST_CASE(entryPoint);
}

struct CheckOptions
Expand Down Expand Up @@ -86,7 +87,7 @@ class TestExceptionSafety : public TestFixture {
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:3:9]: (warning) Class x is not safe, destructor throws exception [exceptThrowInDestructor]\n"
"[test.cpp:3:9]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
"[test.cpp:3:9]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

check("class x {\n"
" ~x();\n"
Expand All @@ -95,7 +96,7 @@ class TestExceptionSafety : public TestFixture {
" throw e;\n"
"}");
ASSERT_EQUALS("[test.cpp:5:5]: (warning) Class x is not safe, destructor throws exception [exceptThrowInDestructor]\n"
"[test.cpp:5:5]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
"[test.cpp:5:5]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// #3858 - throwing exception in try block in destructor.
check("class x {\n"
Expand All @@ -115,7 +116,7 @@ class TestExceptionSafety : public TestFixture {
" }\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:4:13]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
ASSERT_EQUALS("[test.cpp:4:13]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// #11031 should not warn when noexcept false
check("class A {\n"
Expand Down Expand Up @@ -348,9 +349,9 @@ class TestExceptionSafety : public TestFixture {
"void func4() noexcept(false) { throw 1; }\n"
"void func5() noexcept(true) { func1(); }\n"
"void func6() noexcept(false) { func1(); }");
ASSERT_EQUALS("[test.cpp:2:25]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:31]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:5:31]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
ASSERT_EQUALS("[test.cpp:2:25]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:31]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:5:31]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// avoid false positives
check("const char *func() noexcept { return 0; }\n"
Expand All @@ -364,8 +365,8 @@ class TestExceptionSafety : public TestFixture {
"void func3() throw(int) { throw 1; }\n"
"void func4() throw() { func1(); }\n"
"void func5() throw(int) { func1(); }");
ASSERT_EQUALS("[test.cpp:2:24]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:4:24]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
ASSERT_EQUALS("[test.cpp:2:24]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:4:24]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// avoid false positives
check("const char *func() throw() { return 0; }");
Expand Down Expand Up @@ -404,7 +405,7 @@ class TestExceptionSafety : public TestFixture {
"{\n"
" f();\n"
"}\n", dinit(CheckOptions, $.inconclusive = true));
ASSERT_EQUALS("", errout_str());
ASSERT_EQUALS("[test.cpp:4:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n", errout_str());
}

void unhandledExceptionSpecification3() {
Expand All @@ -421,20 +422,24 @@ class TestExceptionSafety : public TestFixture {
"}\n";

check(code, dinit(CheckOptions, $.inconclusive = true));
ASSERT_EQUALS("[test.cpp:3:5] -> [test.cpp:1:6]: (style, inconclusive) Unhandled exception specification when calling function f(). [unhandledExceptionSpecification]\n"
ASSERT_EQUALS("[test.cpp:10:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n"
"[test.cpp:3:5] -> [test.cpp:1:6]: (style, inconclusive) Unhandled exception specification when calling function f(). [unhandledExceptionSpecification]\n"
"[test.cpp:6:5] -> [test.cpp:1:6]: (style, inconclusive) Unhandled exception specification when calling function f(). [unhandledExceptionSpecification]\n", errout_str());

const Settings s = settingsBuilder().library("gnu.cfg").build();
check(code, dinit(CheckOptions, $.inconclusive = true, $.s = &s));
ASSERT_EQUALS("", errout_str());
ASSERT_EQUALS("[test.cpp:3:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n"
"[test.cpp:6:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n"
"[test.cpp:10:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n",
errout_str());
}

void nothrowAttributeThrow() {
check("void func1() throw(int) { throw 1; }\n"
"void func2() __attribute((nothrow)); void func2() { throw 1; }\n"
"void func3() __attribute((nothrow)); void func3() { func1(); }");
ASSERT_EQUALS("[test.cpp:2:53]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:53]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
ASSERT_EQUALS("[test.cpp:2:53]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:53]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// avoid false positives
check("const char *func() __attribute((nothrow)); void func1() { return 0; }");
Expand All @@ -454,8 +459,8 @@ class TestExceptionSafety : public TestFixture {
check("void func1() throw(int) { throw 1; }\n"
"void __declspec(nothrow) func2() { throw 1; }\n"
"void __declspec(nothrow) func3() { func1(); }");
ASSERT_EQUALS("[test.cpp:2:36]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:36]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
ASSERT_EQUALS("[test.cpp:2:36]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
"[test.cpp:3:36]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());

// avoid false positives
check("const char *func() __attribute((nothrow)); void func1() { return 0; }");
Expand Down Expand Up @@ -490,6 +495,31 @@ class TestExceptionSafety : public TestFixture {
"}\n");
ASSERT_EQUALS("", errout_str());
}

void entryPoint() {
check("void f(int i) {\n" // #14195
" if (i < 2)\n"
" throw 0;\n"
"}\n"
"int main(int argc, char* argv[]) {\n"
" f(argc);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:6:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n",
errout_str());

check("void f(int i) {\n"
" if (i < 2)\n"
" throw 0;\n"
"}\n"
"int main(int argc, char* argv[]) {\n"
" try {\n"
" f(argc);\n"
" } catch (...) {\n"
" return 1;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
};

REGISTER_TEST(TestExceptionSafety)