Author: Deniz Evrenci Date: 2023-05-30T17:48:11Z New Revision: 6219b7c61a942fb8b6d931b4aac021d293cdde4d
URL: https://github.com/llvm/llvm-project/commit/6219b7c61a942fb8b6d931b4aac021d293cdde4d DIFF: https://github.com/llvm/llvm-project/commit/6219b7c61a942fb8b6d931b4aac021d293cdde4d.diff LOG: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines All exceptions thrown in coroutine bodies are caught and unhandled_exception member of the coroutine promise type is called. In accordance with the existing rules of diagnostics related to exceptions thrown in functions marked noexcept, even if the promise type's constructor, get_return_object, or unhandled_exception throws, diagnostics should not be emitted. Fixes #61905. Reviewed By: PiotrZSL, ChuanqiXu Differential Revision: https://reviews.llvm.org/D147417 Added: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp Modified: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp clang-tools-extra/docs/ReleaseNotes.rst clang/include/clang/AST/StmtCXX.h Removed: ################################################################################ diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp index c862303706ccb..690e771414a75 100644 --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -523,6 +523,19 @@ ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException( ExceptionInfo Excs = throwsException(DefaultInit->getExpr(), Caught, CallStack); Results.merge(Excs); + } else if (const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) { + for (const Stmt *Child : Coro->childrenExclBody()) { + ExceptionInfo Excs = throwsException(Child, Caught, CallStack); + Results.merge(Excs); + } + ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack); + for (const Type *Throwable : Excs.getExceptionTypes()) { + if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) { + ExceptionInfo DestructorExcs = + throwsException(ThrowableRec->getDestructor(), CallStack); + Results.merge(DestructorExcs); + } + } } else { for (const Stmt *Child : St->children()) { ExceptionInfo Excs = throwsException(Child, Caught, CallStack); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 1eb8c5ba4b71b..b336cd2fc0e24 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -395,6 +395,10 @@ Changes in existing checks <clang-tidy/checks/performance/no-automatic-move>`: warn on ``const &&`` constructors. +- Fixed :doc:`bugprone-exception-escape<clang-tidy/checks/bugprone/exception-escape>` + for coroutines where previously a warning would be emitted with coroutines + throwing exceptions in their bodies. + Removed checks ^^^^^^^^^^^^^^ 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 index 0000000000000..9caafe7676f4e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp @@ -0,0 +1,711 @@ +// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \ +// RUN: -- -fexceptions + +namespace std { + +template <class Ret, typename... T> struct coroutine_traits { + using promise_type = typename Ret::promise_type; +}; + +template <class Promise = void> 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<void> { + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) 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 <typename Task, typename T, bool ThrowInPromiseConstructor, + bool ThrowInInitialSuspend, bool ThrowInGetReturnObject, + bool ThrowInUnhandledException> +struct Promise; + +template < + typename T, bool ThrowInTaskConstructor = false, + bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false, + bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false> +struct Task { + using promise_type = + Promise<Task, T, ThrowInPromiseConstructor, ThrowInInitialSuspend, + ThrowInGetReturnObject, ThrowInUnhandledException>; + + explicit Task(promise_type &p) { + if constexpr (ThrowInTaskConstructor) { + throw 1; + } + + p.return_val = this; + } + + bool await_ready() { return true; } + + void await_suspend(std::coroutine_handle<> h) {} + + void await_resume() {} + + T value; +}; + +template <bool ThrowInTaskConstructor, bool ThrowInPromiseConstructor, + bool ThrowInInitialSuspend, bool ThrowInGetReturnObject, + bool ThrowInUnhandledException> +struct Task<void, ThrowInTaskConstructor, ThrowInPromiseConstructor, + ThrowInInitialSuspend, ThrowInGetReturnObject, + ThrowInUnhandledException> { + using promise_type = + Promise<Task, void, ThrowInPromiseConstructor, ThrowInInitialSuspend, + ThrowInGetReturnObject, ThrowInUnhandledException>; + + explicit Task(promise_type &p) { + if constexpr (ThrowInTaskConstructor) { + throw 1; + } + + p.return_val = this; + } + + bool await_ready() { return true; } + + void await_suspend(std::coroutine_handle<> h) {} + + void await_resume() {} +}; + +template <typename Task, typename T, bool ThrowInPromiseConstructor, + bool ThrowInInitialSuspend, bool ThrowInGetReturnObject, + bool ThrowInUnhandledException> +struct Promise { + Promise() { + if constexpr (ThrowInPromiseConstructor) { + throw 1; + } + } + + Task get_return_object() { + if constexpr (ThrowInGetReturnObject) { + throw 1; + } + + return Task{*this}; + } + + std::suspend_never initial_suspend() const { + if constexpr (ThrowInInitialSuspend) { + throw 1; + } + + return {}; + } + + std::suspend_never final_suspend() const noexcept { return {}; } + + template <typename U> void return_value(U &&val) { + return_val->value = static_cast<U &&>(val); + } + + template <typename U> std::suspend_never yield_value(U &&val) { + return_val->value = static_cast<U &&>(val); + return {}; + } + + void unhandled_exception() { + if constexpr (ThrowInUnhandledException) { + throw 1; + } + } + + Task *return_val; +}; + +template <typename Task, bool ThrowInPromiseConstructor, + bool ThrowInInitialSuspend, bool ThrowInGetReturnObject, + bool ThrowInUnhandledException> +struct Promise<Task, void, ThrowInPromiseConstructor, ThrowInInitialSuspend, + ThrowInGetReturnObject, ThrowInUnhandledException> { + Promise() { + if constexpr (ThrowInPromiseConstructor) { + throw 1; + } + } + + Task get_return_object() { + if constexpr (ThrowInGetReturnObject) { + throw 1; + } + + return Task{*this}; + } + + std::suspend_never initial_suspend() const { + if constexpr (ThrowInInitialSuspend) { + throw 1; + } + + return {}; + } + + std::suspend_never final_suspend() const noexcept { return {}; } + + void return_void() {} + + void unhandled_exception() { + if constexpr (ThrowInUnhandledException) { + throw 1; + } + } + + Task *return_val; +}; + +struct Evil { + ~Evil() noexcept(false) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function '~Evil' which should not throw exceptions + throw 42; + } +}; + +Task<int> returnOne() { co_return 1; } + +namespace function { + +namespace coreturn { + +Task<int> a_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw b; + + co_return a / b; +} + +Task<int> b_ShouldNotDiag(const int a, const int b) noexcept { + if (b == 0) + throw b; + + co_return a / b; +} + +Task<int> c_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw Evil{}; + + co_return a / b; +} + +Task<int> c_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'c_ShouldDiag' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_return a / b; +} + +Task<int, true> d_ShouldNotDiag(const int a, const int b) { + co_return a / b; +} + +Task<int, true> d_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: an exception may be thrown in function 'd_ShouldDiag' which should not throw exceptions + co_return a / b; +} + +Task<int, false, true> e_ShouldNotDiag(const int a, const int b) { + co_return a / b; +} + +Task<int, false, true> e_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: an exception may be thrown in function 'e_ShouldDiag' which should not throw exceptions + co_return a / b; +} + +Task<int, false, false, true> f_ShouldNotDiag(const int a, const int b) { + co_return a / b; +} + +Task<int, false, false, true> f_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: an exception may be thrown in function 'f_ShouldDiag' which should not throw exceptions + co_return a / b; +} + +Task<int, false, false, false, true> g_ShouldNotDiag(const int a, const int b) { + co_return a / b; +} + +Task<int, false, false, false, true> g_ShouldDiag(const int a, + const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: an exception may be thrown in function 'g_ShouldDiag' which should not throw exceptions + co_return a / b; +} + +Task<int, false, false, false, false, true> h_ShouldNotDiag(const int a, + const int b) { + co_return a / b; +} + +Task<int, false, false, false, false, true> h_ShouldDiag(const int a, + const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-2]]:45: warning: an exception may be thrown in function 'h_ShouldDiag' which should not throw exceptions + co_return a / b; +} + +} // namespace coreturn + +namespace coyield { + +Task<int> a_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw b; + + co_yield a / b; +} + +Task<int> b_ShouldNotDiag(const int a, const int b) noexcept { + if (b == 0) + throw b; + + co_yield a / b; +} + +Task<int> c_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw Evil{}; + + co_yield a / b; +} + +Task<int> c_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'c_ShouldDiag' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_yield a / b; +} + +Task<int, true> d_ShouldNotDiag(const int a, const int b) { + co_yield a / b; +} + +Task<int, true> d_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: an exception may be thrown in function 'd_ShouldDiag' which should not throw exceptions + co_yield a / b; +} + +Task<int, false, true> e_ShouldNotDiag(const int a, const int b) { + co_yield a / b; +} + +Task<int, false, true> e_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: an exception may be thrown in function 'e_ShouldDiag' which should not throw exceptions + co_yield a / b; +} + +Task<int, false, false, true> f_ShouldNotDiag(const int a, const int b) { + co_yield a / b; +} + +Task<int, false, false, true> f_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: an exception may be thrown in function 'f_ShouldDiag' which should not throw exceptions + co_yield a / b; +} + +Task<int, false, false, false, true> g_ShouldNotDiag(const int a, const int b) { + co_yield a / b; +} + +Task<int, false, false, false, true> g_ShouldDiag(const int a, + const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: an exception may be thrown in function 'g_ShouldDiag' which should not throw exceptions + co_yield a / b; +} + +Task<int, false, false, false, false, true> h_ShouldNotDiag(const int a, + const int b) { + co_yield a / b; +} + +Task<int, false, false, false, false, true> h_ShouldDiag(const int a, + const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-2]]:45: warning: an exception may be thrown in function 'h_ShouldDiag' which should not throw exceptions + co_yield a / b; +} + +} // namespace coyield + +namespace coawait { + +Task<void> a_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw b; + + co_await returnOne(); +} + +Task<void> b_ShouldNotDiag(const int a, const int b) noexcept { + if (b == 0) + throw b; + + co_await returnOne(); +} + +Task<void> c_ShouldNotDiag(const int a, const int b) { + if (b == 0) + throw Evil{}; + + co_await returnOne(); +} + +Task<void> c_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: an exception may be thrown in function 'c_ShouldDiag' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_await returnOne(); +} + +Task<void, true> d_ShouldNotDiag(const int a, const int b) { + co_await returnOne(); +} + +Task<void, true> d_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: an exception may be thrown in function 'd_ShouldDiag' which should not throw exceptions + co_await returnOne(); +} + +Task<void, false, true> e_ShouldNotDiag(const int a, const int b) { + co_await returnOne(); +} + +Task<void, false, true> e_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: an exception may be thrown in function 'e_ShouldDiag' which should not throw exceptions + co_await returnOne(); +} + +Task<void, false, false, true> f_ShouldNotDiag(const int a, const int b) { + co_await returnOne(); +} + +Task<void, false, false, true> f_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: an exception may be thrown in function 'f_ShouldDiag' which should not throw exceptions + co_await returnOne(); +} + +Task<void, false, false, false, true> g_ShouldNotDiag(const int a, + const int b) { + co_await returnOne(); +} + +Task<void, false, false, false, true> g_ShouldDiag(const int a, + const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: an exception may be thrown in function 'g_ShouldDiag' which should not throw exceptions + co_await returnOne(); +} + +Task<void, false, false, false, false, true> h_ShouldNotDiag(const int a, + const int b) { + co_await returnOne(); +} + +Task<void, false, false, false, false, true> +h_ShouldDiag(const int a, const int b) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'h_ShouldDiag' which should not throw exceptions + co_await returnOne(); +} + +} // namespace coawait + +} // namespace function + +namespace lambda { + +namespace coreturn { + +const auto a_ShouldNotDiag = [](const int a, const int b) -> Task<int> { + if (b == 0) + throw b; + + co_return a / b; +}; + +const auto b_ShouldNotDiag = [](const int a, + const int b) noexcept -> Task<int> { + if (b == 0) + throw b; + + co_return a / b; +}; + +const auto c_ShouldNotDiag = [](const int a, const int b) -> Task<int> { + if (b == 0) + throw Evil{}; + + co_return a / b; +}; + +const auto c_ShouldDiag = [](const int a, const int b) noexcept -> Task<int> { + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_return a / b; +}; + +const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<int, true> { + co_return a / b; +}; + +const auto d_ShouldDiag = [](const int a, + const int b) noexcept -> Task<int, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_return a / b; +}; + +const auto e_ShouldNotDiag = [](const int a, + const int b) -> Task<int, false, true> { + co_return a / b; +}; + +const auto e_ShouldDiag = [](const int a, + const int b) noexcept -> Task<int, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_return a / b; +}; + +const auto f_ShouldNotDiag = [](const int a, + const int b) -> Task<int, false, false, true> { + co_return a / b; +}; + +const auto f_ShouldDiag = + [](const int a, const int b) noexcept -> Task<int, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_return a / b; +}; + +const auto g_ShouldNotDiag = + [](const int a, const int b) -> Task<int, false, false, false, true> { + co_return a / b; +}; + +const auto g_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<int, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_return a / b; +}; + +const auto h_ShouldNotDiag = + [](const int a, + const int b) -> Task<int, false, false, false, false, true> { + co_return a / b; +}; + +const auto h_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<int, false, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_return a / b; +}; + +} // namespace coreturn + +namespace coyield { + +const auto a_ShouldNotDiag = [](const int a, const int b) -> Task<int> { + if (b == 0) + throw b; + + co_yield a / b; +}; + +const auto b_ShouldNotDiag = [](const int a, + const int b) noexcept -> Task<int> { + if (b == 0) + throw b; + + co_yield a / b; +}; + +const auto c_ShouldNotDiag = [](const int a, const int b) -> Task<int> { + if (b == 0) + throw Evil{}; + + co_yield a / b; +}; + +const auto c_ShouldDiag = [](const int a, const int b) noexcept -> Task<int> { + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_yield a / b; +}; + +const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<int, true> { + co_yield a / b; +}; + +const auto d_ShouldDiag = [](const int a, + const int b) noexcept -> Task<int, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_yield a / b; +}; + +const auto e_ShouldNotDiag = [](const int a, + const int b) -> Task<int, false, true> { + co_yield a / b; +}; + +const auto e_ShouldDiag = [](const int a, + const int b) noexcept -> Task<int, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_yield a / b; +}; + +const auto f_ShouldNotDiag = [](const int a, + const int b) -> Task<int, false, false, true> { + co_yield a / b; +}; + +const auto f_ShouldDiag = + [](const int a, const int b) noexcept -> Task<int, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_yield a / b; +}; + +const auto g_ShouldNotDiag = + [](const int a, const int b) -> Task<int, false, false, false, true> { + co_yield a / b; +}; + +const auto g_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<int, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_yield a / b; +}; + +const auto h_ShouldNotDiag = + [](const int a, + const int b) -> Task<int, false, false, false, false, true> { + co_yield a / b; +}; + +const auto h_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<int, false, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_yield a / b; +}; + +} // namespace coyield + +namespace coawait { + +const auto a_ShouldNotDiag = [](const int a, const int b) -> Task<void> { + if (b == 0) + throw b; + + co_await returnOne(); +}; + +const auto b_ShouldNotDiag = [](const int a, + const int b) noexcept -> Task<void> { + if (b == 0) + throw b; + + co_await returnOne(); +}; + +const auto c_ShouldNotDiag = [](const int a, const int b) -> Task<void> { + if (b == 0) + throw Evil{}; + + co_await returnOne(); +}; + +const auto c_ShouldDiag = [](const int a, const int b) noexcept -> Task<void> { + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + if (b == 0) + throw Evil{}; + + co_await returnOne(); +}; + +const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<void, true> { + co_await returnOne(); +}; + +const auto d_ShouldDiag = [](const int a, + const int b) noexcept -> Task<void, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_await returnOne(); +}; + +const auto e_ShouldNotDiag = [](const int a, + const int b) -> Task<void, false, true> { + co_await returnOne(); +}; + +const auto e_ShouldDiag = [](const int a, + const int b) noexcept -> Task<void, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_await returnOne(); +}; + +const auto f_ShouldNotDiag = [](const int a, + const int b) -> Task<void, false, false, true> { + co_await returnOne(); +}; + +const auto f_ShouldDiag = + [](const int a, const int b) noexcept -> Task<void, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_await returnOne(); +}; + +const auto g_ShouldNotDiag = + [](const int a, const int b) -> Task<void, false, false, false, true> { + co_await returnOne(); +}; + +const auto g_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<void, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_await returnOne(); +}; + +const auto h_ShouldNotDiag = + [](const int a, + const int b) -> Task<void, false, false, false, false, true> { + co_await returnOne(); +}; + +const auto h_ShouldDiag = + [](const int a, + const int b) noexcept -> Task<void, false, false, false, false, true> { + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions + co_await returnOne(); +}; + +} // namespace coawait + +} // namespace lambda diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h index 60fc3f3a63f49..8b4ef24ed376a 100644 --- a/clang/include/clang/AST/StmtCXX.h +++ b/clang/include/clang/AST/StmtCXX.h @@ -443,6 +443,17 @@ class CoroutineBodyStmt final NumParams); } + child_range childrenExclBody() { + return child_range(getStoredStmts() + SubStmt::Body + 1, + getStoredStmts() + SubStmt::FirstParamMove + NumParams); + } + + const_child_range childrenExclBody() const { + return const_child_range(getStoredStmts() + SubStmt::Body + 1, + getStoredStmts() + SubStmt::FirstParamMove + + NumParams); + } + static bool classof(const Stmt *T) { return T->getStmtClass() == CoroutineBodyStmtClass; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits