diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 3d93ce6042e..36cec717d90 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); } } @@ -321,7 +319,12 @@ void CheckExceptionSafety::nothrowThrows() 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); } //-------------------------------------------------------------------------- @@ -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..5bbe6a1984c 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 @@ -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" @@ -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" @@ -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" @@ -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" @@ -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; }"); @@ -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() { @@ -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; }"); @@ -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; }"); @@ -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)