Skip to content

Commit c6f3d72

Browse files
Fix #14195 Show warning when main() throws an exception (danmar#7887)
Co-authored-by: chrchr-github <[email protected]>
1 parent 3e8c9d4 commit c6f3d72

File tree

3 files changed

+66
-31
lines changed

3 files changed

+66
-31
lines changed

lib/checkexceptionsafety.cpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -296,32 +296,35 @@ void CheckExceptionSafety::nothrowThrows()
296296
if (!function)
297297
continue;
298298

299-
// check noexcept and noexcept(true) functions
300-
if (function->isNoExcept()) {
301-
const Token *throws = functionThrows(function);
302-
if (throws)
303-
noexceptThrowError(throws);
299+
bool isNoExcept = false, isEntryPoint = false;
300+
if (function->isNoExcept() || // noexcept and noexcept(true) functions
301+
(function->isThrow() && !function->throwArg) || // throw() functions
302+
function->isAttributeNothrow()) { // __attribute__((nothrow)) or __declspec(nothrow) functions
303+
isNoExcept = true;
304304
}
305-
306-
// check throw() functions
307-
else if (function->isThrow() && !function->throwArg) {
308-
const Token *throws = functionThrows(function);
309-
if (throws)
310-
noexceptThrowError(throws);
305+
else if (mSettings->library.isentrypoint(function->name())) {
306+
isEntryPoint = true;
311307
}
308+
if (!isNoExcept && !isEntryPoint)
309+
continue;
312310

313-
// check __attribute__((nothrow)) or __declspec(nothrow) functions
314-
else if (function->isAttributeNothrow()) {
315-
const Token *throws = functionThrows(function);
316-
if (throws)
311+
if (const Token* throws = functionThrows(function)) {
312+
if (isEntryPoint)
313+
entryPointThrowError(throws);
314+
else
317315
noexceptThrowError(throws);
318316
}
319317
}
320318
}
321319

322320
void CheckExceptionSafety::noexceptThrowError(const Token * const tok)
323321
{
324-
reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
322+
reportError(tok, Severity::error, "throwInNoexceptFunction", "Unhandled exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
323+
}
324+
325+
void CheckExceptionSafety::entryPointThrowError(const Token * const tok)
326+
{
327+
reportError(tok, Severity::error, "throwInEntryPoint", "Unhandled exception thrown in function that is an entry point.", CWE398, Certainty::normal);
325328
}
326329

327330
//--------------------------------------------------------------------------
@@ -433,6 +436,7 @@ void CheckExceptionSafety::getErrorMessages(ErrorLogger *errorLogger, const Sett
433436
c.rethrowCopyError(nullptr, "varname");
434437
c.catchExceptionByValueError(nullptr);
435438
c.noexceptThrowError(nullptr);
439+
c.entryPointThrowError(nullptr);
436440
c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname");
437441
c.rethrowNoCurrentExceptionError(nullptr);
438442
}

lib/checkexceptionsafety.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class CPPCHECKLIB CheckExceptionSafety : public Check {
8282
void rethrowCopyError(const Token * tok, const std::string &varname);
8383
void catchExceptionByValueError(const Token *tok);
8484
void noexceptThrowError(const Token * tok);
85+
void entryPointThrowError(const Token * tok);
8586
/** Missing exception specification */
8687
void unhandledExceptionSpecificationError(const Token * tok1, const Token * tok2, const std::string & funcname);
8788
/** Rethrow without currently handled exception */

test/testexceptionsafety.cpp

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class TestExceptionSafety : public TestFixture {
5757
TEST_CASE(rethrowNoCurrentException2);
5858
TEST_CASE(rethrowNoCurrentException3);
5959
TEST_CASE(noFunctionCall);
60+
TEST_CASE(entryPoint);
6061
}
6162

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

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

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

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

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

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

409410
void unhandledExceptionSpecification3() {
@@ -420,20 +421,24 @@ class TestExceptionSafety : public TestFixture {
420421
"}\n";
421422

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

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

431436
void nothrowAttributeThrow() {
432437
check("void func1() throw(int) { throw 1; }\n"
433438
"void func2() __attribute((nothrow)); void func2() { throw 1; }\n"
434439
"void func3() __attribute((nothrow)); void func3() { func1(); }");
435-
ASSERT_EQUALS("[test.cpp:2:53]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
436-
"[test.cpp:3:53]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
440+
ASSERT_EQUALS("[test.cpp:2:53]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
441+
"[test.cpp:3:53]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
437442

438443
// avoid false positives
439444
check("const char *func() __attribute((nothrow)); void func1() { return 0; }");
@@ -453,8 +458,8 @@ class TestExceptionSafety : public TestFixture {
453458
check("void func1() throw(int) { throw 1; }\n"
454459
"void __declspec(nothrow) func2() { throw 1; }\n"
455460
"void __declspec(nothrow) func3() { func1(); }");
456-
ASSERT_EQUALS("[test.cpp:2:36]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
457-
"[test.cpp:3:36]: (error) Exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
461+
ASSERT_EQUALS("[test.cpp:2:36]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n"
462+
"[test.cpp:3:36]: (error) Unhandled exception thrown in function declared not to throw exceptions. [throwInNoexceptFunction]\n", errout_str());
458463

459464
// avoid false positives
460465
check("const char *func() __attribute((nothrow)); void func1() { return 0; }");
@@ -489,6 +494,31 @@ class TestExceptionSafety : public TestFixture {
489494
"}\n");
490495
ASSERT_EQUALS("", errout_str());
491496
}
497+
498+
void entryPoint() {
499+
check("void f(int i) {\n" // #14195
500+
" if (i < 2)\n"
501+
" throw 0;\n"
502+
"}\n"
503+
"int main(int argc, char* argv[]) {\n"
504+
" f(argc);\n"
505+
"}\n");
506+
ASSERT_EQUALS("[test.cpp:6:5]: (error) Unhandled exception thrown in function that is an entry point. [throwInEntryPoint]\n",
507+
errout_str());
508+
509+
check("void f(int i) {\n"
510+
" if (i < 2)\n"
511+
" throw 0;\n"
512+
"}\n"
513+
"int main(int argc, char* argv[]) {\n"
514+
" try {\n"
515+
" f(argc);\n"
516+
" } catch (...) {\n"
517+
" return 1;\n"
518+
" }\n"
519+
"}\n");
520+
ASSERT_EQUALS("", errout_str());
521+
}
492522
};
493523

494524
REGISTER_TEST(TestExceptionSafety)

0 commit comments

Comments
 (0)