diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 3d93ce6042e..ca36d9627d2 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -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); } } @@ -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); +} + //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- @@ -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); } diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index 37b8ddf3a33..7c5a52b5605 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -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 */ diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 0ec92c982e7..12ba2ae59ba 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -57,6 +57,7 @@ class TestExceptionSafety : public TestFixture { TEST_CASE(rethrowNoCurrentException2); TEST_CASE(rethrowNoCurrentException3); TEST_CASE(noFunctionCall); + TEST_CASE(entryPoint); } struct CheckOptions @@ -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() { @@ -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() { @@ -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)