diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -51,12 +51,15 @@ } void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { + auto IsCoroMatcher = + hasDescendant(expr(anyOf(coyieldExpr(), coreturnStmt(), coawaitExpr()))); Finder->addMatcher( functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(), cxxConstructorDecl(isMoveConstructor()), cxxMethodDecl(isMoveAssignmentOperator()), hasName("main"), hasName("swap"), - isEnabled(FunctionsThatShouldNotThrow))) + isEnabled(FunctionsThatShouldNotThrow)), + unless(IsCoroMatcher)) .bind("thrower"), this); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp @@ -0,0 +1,115 @@ +// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \ +// RUN: -- -fexceptions + +namespace std { + +template struct coroutine_traits { + using promise_type = typename Ret::promise_type; +}; + +template struct coroutine_handle { + static coroutine_handle from_address(void *) noexcept; + static coroutine_handle from_promise(Promise &promise); + constexpr void *address() const noexcept; +}; + +template <> struct coroutine_handle { + template + coroutine_handle(coroutine_handle) noexcept; + static coroutine_handle from_address(void *); + constexpr void *address() const noexcept; +}; + +struct suspend_always { + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +struct suspend_never { + bool await_ready() noexcept { return true; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +} // namespace std + +template struct promise; + +template struct task { + using promise_type = promise; + + explicit task(promise_type &p) { + throw 1; + p.return_val = this; + } + + T value; +}; + +template struct promise { + task get_return_object() { return task{*this}; } + + std::suspend_never initial_suspend() const noexcept { return {}; } + + std::suspend_never final_suspend() const noexcept { return {}; } + + template void return_value(U &&val) { + return_val->value = static_cast(val); + } + + void unhandled_exception() { throw 1; } + + task *return_val; +}; + +task a_ShouldNotDiag(const int a, const int b) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'a_ShouldNotDiag' which should not throw exceptions + if (b == 0) + throw b; + + co_return a / b; +} + +task b_ShouldNotDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'b_ShouldNotDiag' which should not throw exceptions + if (b == 0) + throw b; + + co_return a / b; +} + +int c_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'c_ShouldDiag' which should not throw exceptions + if (b == 0) + throw b; + + return a / b; +} + +const auto d_ShouldNotDiag = [](const int a, const int b) -> task { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:30: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw b; + + co_return a / b; +}; + +const auto e_ShouldNotDiag = [](const int a, + const int b) noexcept -> task { + // CHECK-MESSAGES-NOT: :[[@LINE-2]]:30: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw b; + + co_return a / b; +}; + +const auto f_ShouldDiag = [](const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw b; + + return a / b; +}; + +// CHECK-MESSAGES