Skip to content
Open
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
34 changes: 19 additions & 15 deletions lib/checkexceptionsafety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,24 +296,22 @@ 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);
}
}
Expand All @@ -324,6 +322,11 @@ void CheckExceptionSafety::noexceptThrowError(const Token * const tok)
reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest something like:

Unhandled exception is thrown in function that is an entry point.

We only warn if it's unhandled right?

This is undefined behavior right? To throw an exception that is not catched..

}

//--------------------------------------------------------------------------
// void func() { functionWithExceptionSpecification(); }
//--------------------------------------------------------------------------
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
23 changes: 20 additions & 3 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 @@ -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) Exception thrown in function that is an entry point. [throwInEntryPoint]\n", errout_str());
}

void unhandledExceptionSpecification3() {
Expand All @@ -421,12 +422,16 @@ 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) 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) Exception thrown in function that is an entry point. [throwInEntryPoint]\n"
"[test.cpp:6:5]: (error) Exception thrown in function that is an entry point. [throwInEntryPoint]\n"
"[test.cpp:10:5]: (error) Exception thrown in function that is an entry point. [throwInEntryPoint]\n",
errout_str());
}

void nothrowAttributeThrow() {
Expand Down Expand Up @@ -490,6 +495,18 @@ 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) Exception thrown in function that is an entry point. [throwInEntryPoint]\n",
errout_str());
}
};

REGISTER_TEST(TestExceptionSafety)