[clang] 6219b7c - [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

Piotr Zegar via cfe-commits cfe-commits at lists.llvm.org
Tue May 30 10:48:26 PDT 2023


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;
   }


        


More information about the cfe-commits mailing list