Skip to content

Commit 8563eed

Browse files
author
John Siegel
committed
Fixed exception dispatch idiom not being recognized in the middle of a function.
1 parent e0ce648 commit 8563eed

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

Diff for: lib/checkexceptionsafety.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -379,21 +379,21 @@ void CheckExceptionSafety::rethrowNoCurrentException()
379379
if (!function)
380380
continue;
381381

382-
// Rethrow can be used in 'exception dispatcher' idiom which is FP in such case
383-
// https://isocpp.org/wiki/faq/exceptions#throw-without-an-object
384-
// We check the beginning of the function with idiom pattern
385-
if (Token::simpleMatch(function->functionScope->bodyStart->next(), "try { throw ; } catch ("))
386-
continue;
387-
382+
//// Rethrow can be used in 'exception dispatcher' idiom which is FP in such case
383+
//// https://isocpp.org/wiki/faq/exceptions#throw-without-an-object
384+
////
385+
//// After the idiom is found, continue to the next function scope in the above for loop.
388386
for (const Token *tok = function->functionScope->bodyStart->next();
389-
tok != function->functionScope->bodyEnd; tok = tok->next()) {
387+
tok != function->functionScope->bodyEnd &&
388+
!Token::simpleMatch(tok, "try { throw ; } catch ("); tok = tok->next()) {
390389
if (Token::simpleMatch(tok, "catch (")) {
391390
tok = tok->linkAt(1); // skip catch argument
392391
if (Token::simpleMatch(tok, ") {"))
393392
tok = tok->linkAt(1); // skip catch scope
394393
else
395394
break;
396395
}
396+
397397
if (Token::simpleMatch(tok, "throw ;")) {
398398
rethrowNoCurrentExceptionError(tok);
399399
}

Diff for: test/testexceptionsafety.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class TestExceptionSafety : public TestFixture {
5555
TEST_CASE(rethrowNoCurrentException1);
5656
TEST_CASE(rethrowNoCurrentException2);
5757
TEST_CASE(rethrowNoCurrentException3);
58+
TEST_CASE(exceptionDispatchIdiomInMiddleOfFunction);
59+
TEST_CASE(exceptionDispatchIdiomExtraStatements);
60+
TEST_CASE(exceptionDispatchIdiomSubsequentRethrows);
5861
}
5962

6063
#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
@@ -449,6 +452,26 @@ class TestExceptionSafety : public TestFixture {
449452
"void func3() { throw 0; }");
450453
ASSERT_EQUALS("", errout_str());
451454
}
455+
456+
void exceptionDispatchIdiomInMiddleOfFunction() {
457+
check("void on_error() { int foo = 5; try { throw; } catch (...) { ; } }");
458+
ASSERT_EQUALS("", errout.str());
459+
460+
check("void on_error() { int foo = 5; try { throw; } catch (const std::exception& e) { ; } }");
461+
ASSERT_EQUALS("", errout.str());
462+
}
463+
464+
void exceptionDispatchIdiomExtraStatements() {
465+
// extra statements aside from the rethrow should be done before/after the idiom rather than inside it.
466+
check("void on_error() { try { int foo = 5; throw; } catch (...) { ; } }");
467+
ASSERT_EQUALS("[test.cpp:1]: (error) Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow. If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object\n", errout.str());
468+
}
469+
470+
void exceptionDispatchIdiomSubsequentRethrows() {
471+
// if you use the exception dispatch idiom, you have already asserted that you are in a catch block, so subsequent rethrows are valid.
472+
check("void on_error() { int foo = 5; try { throw; } catch (...) { ; } throw; }");
473+
ASSERT_EQUALS("", errout.str());
474+
}
452475
};
453476

454477
REGISTER_TEST(TestExceptionSafety)

0 commit comments

Comments
 (0)