[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-11 Thread Deniz Evrenci via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGdaac014fec42: [clang-tidy] Check functions called from catch 
blocks (authored by denizevrenci).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,33 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +379,33 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_yield a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  

[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-11 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 530296.
denizevrenci added a comment.

Rebase on main


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,33 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +379,33 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_yield a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_yield a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const 

[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-06 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 529140.
denizevrenci added a comment.

Sort tests out a bit more


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,33 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +379,33 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_yield a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_yield a / b;
+}
+
+Task
+j_ShouldDiag(const 

[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-06 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 529137.
denizevrenci added a comment.

Sort out tests


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,29 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  throw 1;
+  co_return a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+  throw 1;
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +375,29 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_yield a / b;
+}
+
+Task
+j_ShouldNotDiag(const int a, const int b) {
+  throw 1;
+  co_yield a / b;
+}
+
+Task
+j_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: 

[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-06 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 529134.
denizevrenci added a comment.

Rebase on main


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,23 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+i_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'i_ShouldDiag' which should not throw exceptions
+  throw 1;
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +369,23 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_yield a / b;
+}
+
+Task
+i_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'i_ShouldDiag' which should not throw exceptions
+  throw 1;
+  co_yield a / b;
+}
+
 } // namespace coyield
 
 namespace 

[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-06 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added inline comments.



Comment at: 
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp:132-136
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }

ChuanqiXu wrote:
> I don't know clang-tidy a lot. But is these two branches different in 
> clang-tidy really?
Yes, rethrows are handled differently than throw expressions with arguments. 
You can find the relevant implementation in ExceptionAnalyzer.cpp:[462:475].


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152330/new/

https://reviews.llvm.org/D152330

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D152330: [clang-tidy] Check functions called from catch blocks

2023-06-06 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci created this revision.
denizevrenci added reviewers: njames93, PiotrZSL, ChuanqiXu.
Herald added subscribers: carlosgalvezp, xazax.hun.
Herald added a project: All.
denizevrenci requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

These functions can rethrow a current exception that is caught by the
catch block. We can pass the currently caught excections to the function
declaration analyzer just like the statement analyzer to handle this
case.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152330

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+void rethrower() {
+throw;
+}
+
+void callsRethrower() {
+rethrower();
+}
+
+void callsRethrowerNoexcept() noexcept {
+rethrower();
+}
+
+int throwsAndCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+rethrower();
+}
+}
+
+int throwsAndCallsCallsRethrower() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+try {
+throw 1;
+} catch(...) {
+callsRethrower();
+}
+}
+
+void rethrowerNoexcept() noexcept {
+throw;
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -36,17 +36,18 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise;
 
 template <
 typename T, bool ThrowInTaskConstructor = false,
 bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
-bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+bool RethrowInUnhandledException = false>
 struct Task {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -67,13 +68,13 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Task {
+ThrowInUnhandledException, RethrowInUnhandledException> {
   using promise_type =
   Promise;
+  ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
 
   explicit Task(promise_type ) {
 if constexpr (ThrowInTaskConstructor) {
@@ -92,7 +93,7 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
@@ -130,6 +131,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -138,9 +141,9 @@
 
 template 
+  bool ThrowInUnhandledException, bool RethrowInUnhandledException>
 struct Promise {
+   ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
   Promise() {
 if constexpr (ThrowInPromiseConstructor) {
   throw 1;
@@ -170,6 +173,8 @@
   void unhandled_exception() {
 if constexpr (ThrowInUnhandledException) {
   throw 1;
+} else if constexpr (RethrowInUnhandledException) {
+  throw;
 }
   }
 
@@ -266,6 +271,23 @@
   co_return a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_return a / b;
+}
+
+Task
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+  co_return a / b;
+}
+
+Task
+i_ShouldDiag(const int a, const int b) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'i_ShouldDiag' which should not throw exceptions
+  throw 1;
+  co_return a / b;
+}
+
 } // namespace coreturn
 
 namespace coyield {
@@ -347,6 +369,23 @@
   co_yield a / b;
 }
 
+Task
+i_ShouldNotDiag(const int a, const int b) {
+  co_yield a / b;
+}
+

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-29 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

Hi @PiotrZSL if you are happy with the changes, could you commit the diff for 
me as I don't have commit access to LLVM repo?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-29 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 526438.
denizevrenci added a comment.

- Rebase on trunk
- Remove unused helpers


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ 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  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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  template  std::suspend_never yield_value(U &) {
+return_val->value = static_cast(val);
+return {};
+  }
+
+  void unhandled_exception() {
+if constexpr (ThrowInUnhandledException) {
+  throw 1;
+}
+  }
+
+  Task *return_val;
+};
+
+template 
+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 {}; }
+
+  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 

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-29 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

Addressed 1 and 3. As for the new methods in StmtCXX.h, I think they may be 
used elsewhere too where we need to make a distinction between the visible body 
of the coroutine and the desugared version.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-29 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 526430.
denizevrenci added a comment.

- Update release notes
- Remove checks for no messages


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,731 @@
+// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+namespace std {
+
+template  struct enable_if {};
+
+template  struct enable_if {
+  typedef T type;
+};
+
+template 
+using enable_if_t = typename enable_if::type;
+
+template  struct is_same {
+  static constexpr bool value = false;
+};
+
+template  struct is_same {
+  static constexpr bool value = false;
+};
+
+template 
+inline constexpr bool is_same_v = is_same::value;
+
+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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  template  std::suspend_never yield_value(U &) {
+return_val->value = static_cast(val);
+return {};
+  }
+
+  void unhandled_exception() {
+if constexpr (ThrowInUnhandledException) {
+  throw 1;
+}
+  }
+
+  Task *return_val;
+};
+
+template 
+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) {
+

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-28 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

@PiotrZSL & @ChuanqiXu may I request a re-review? I addressed all comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-28 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 526359.
denizevrenci added a comment.

Add tests for co_yield and co_await


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,779 @@
+// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \
+// RUN: -- -fexceptions
+
+namespace std {
+
+template  struct enable_if {};
+
+template  struct enable_if {
+  typedef T type;
+};
+
+template 
+using enable_if_t = typename enable_if::type;
+
+template  struct is_same {
+  static constexpr bool value = false;
+};
+
+template  struct is_same {
+  static constexpr bool value = false;
+};
+
+template 
+inline constexpr bool is_same_v = is_same::value;
+
+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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+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 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  template  std::suspend_never yield_value(U &) {
+return_val->value = static_cast(val);
+return {};
+  }
+
+  void unhandled_exception() {
+if constexpr (ThrowInUnhandledException) {
+  throw 1;
+}
+  }
+
+  Task *return_val;
+};
+
+template 
+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 {};
+  }
+
+  

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-16 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 522829.
denizevrenci added a comment.

Fix commit message


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,296 @@
+// 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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+if constexpr (ThrowInTaskConstructor) {
+  throw 1;
+}
+
+p.return_val = this;
+  }
+
+  T value;
+};
+
+template 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  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;
+  }
+};
+
+namespace function {
+
+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;
+}
+
+Task c_ShouldNotDiag(const int a, const int b) {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'c_ShouldNotDiag' which should not throw exceptions
+  if (b == 0)
+throw Evil{};
+
+  co_return a / b;
+}
+
+Task 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 d_ShouldNotDiag(const int a, const int b) {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:17: warning: an exception may be 

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-15 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 522386.
denizevrenci added a comment.

a_shouldNotDiag -> b_shouldNotDiag


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,296 @@
+// 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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+if constexpr (ThrowInTaskConstructor) {
+  throw 1;
+}
+
+p.return_val = this;
+  }
+
+  T value;
+};
+
+template 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  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;
+  }
+};
+
+namespace function {
+
+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;
+}
+
+Task c_ShouldNotDiag(const int a, const int b) {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'c_ShouldNotDiag' which should not throw exceptions
+  if (b == 0)
+throw Evil{};
+
+  co_return a / b;
+}
+
+Task 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 d_ShouldNotDiag(const int a, const int b) {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:17: warning: an 

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-05-15 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 522381.
denizevrenci added a comment.
Herald added a project: clang.

Address comments. Implement a special case for coroutines in ExceptionAnalyzer


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
  clang/include/clang/AST/StmtCXX.h

Index: clang/include/clang/AST/StmtCXX.h
===
--- clang/include/clang/AST/StmtCXX.h
+++ clang/include/clang/AST/StmtCXX.h
@@ -443,6 +443,17 @@
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;
   }
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,296 @@
+// 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 );
+  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 <
+typename T, bool ThrowInTaskConstructor = false,
+bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
+bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+struct Task {
+  using promise_type =
+  Promise;
+
+  explicit Task(promise_type ) {
+if constexpr (ThrowInTaskConstructor) {
+  throw 1;
+}
+
+p.return_val = this;
+  }
+
+  T value;
+};
+
+template 
+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  void return_value(U &) {
+return_val->value = static_cast(val);
+  }
+
+  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;
+  }
+};
+
+namespace function {
+
+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 'a_ShouldNotDiag' which should not throw exceptions
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+Task c_ShouldNotDiag(const int a, const int b) {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:11: warning: an exception may be thrown in function 'c_ShouldNotDiag' which should not throw exceptions
+  if (b == 0)
+throw Evil{};
+
+  co_return a / b;
+}
+
+Task 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 d_ShouldNotDiag(const int 

[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-04 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added inline comments.



Comment at: 
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp:75-79
+  // 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;

ChuanqiXu wrote:
> denizevrenci wrote:
> > ChuanqiXu wrote:
> > > I don't understand why we shouldn't emit the warning here. Since the 
> > > function is marked `noexcept` but it may throw actually in 
> > > `unhandled_exception`. I think it is meaningful to warn for this.
> > Right, I now see that this behavior is different between Clang's 
> > `-Wexceptions` and Clang Tidy's `bugprone-exception-escape`. The former 
> > does not warn on this code, the latter does.
> > 
> > ```
> > int foo() {
> >   throw 1;
> > }
> > 
> > int bar() noexcept {
> >   return foo();
> > }
> > ```
> > 
> > We need to treat coroutines differently and check whether `task::task`, 
> > `promise::promise`,  `promise::initial_suspend`, 
> > `promise::get_return_object`, and `promise::unhandled_exception` can throw 
> > instead of the body of the coroutine.
> I investigated the exception issue in coroutines before: 
> https://reviews.llvm.org/D108277. And it is much more complex than I thought. 
> The short conclusion here is that the coroutine is still may throw even if 
> all the promise's method wouldn't throw. For example:
> 
> ```
> struct Evil {
> ~Evil() noexcept(false) { throw 32; }
> };
> 
> task foo() noexcept { // all promise's method of task wouldn't throw
> throw Evil;
> }
> ```
> 
> And in the above example, foo() may throw actually. Although the implicit 
> `catch` block of `foo()` will catch `Evil`, the exception in the destructor 
> of `Evil` will be thrown again.
> 
> So we can't be sure that a coroutine wouldn't throw even if all of its 
> promise's method wouldn't throw.
It looks like the function `foo` can throw until the first suspension point in 
the coroutine. If `promise::initial_suspend` is `std::suspend_always`, then it 
will not throw. Of course, determining this statically is quite complicated.
But I also think that this is a rather niche example,  it looks like clang-tidy 
already warns with `bugprone-exception-escape` on the destructor of `Evil` even 
when it is marked `noexcept(false)`. I assume this is due to the other
complications brought by throwing from destructors. Would that not be the 
appropriate place to warn about this anyway?

For example, the code below terminates because the destructor of `Evil` gets 
called while there is an active exception.
```
task foo() { // all promise's method of task wouldn't throw
Evil e;
throw 1;
co_return;
}
```


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-03 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added inline comments.



Comment at: 
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp:75-79
+  // 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;

ChuanqiXu wrote:
> I don't understand why we shouldn't emit the warning here. Since the function 
> is marked `noexcept` but it may throw actually in `unhandled_exception`. I 
> think it is meaningful to warn for this.
Right, I now see that this behavior is different between Clang's `-Wexceptions` 
and Clang Tidy's `bugprone-exception-escape`. The former does not warn on this 
code, the latter does.

```
int foo() {
  throw 1;
}

int bar() noexcept {
  return foo();
}
```

We need to treat coroutines differently and check whether `task::task`, 
`promise::promise`,  `promise::initial_suspend`, `promise::get_return_object`, 
and `promise::unhandled_exception` can throw instead of the body of the 
coroutine.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-02 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

I don't have commit access so after the review is complete, please commit this 
diff in my place.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-02 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 510383.
denizevrenci added a comment.

Fix the line number for the warning


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ 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 );
+  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 ) {
+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 &) {
+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
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ 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);
 }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-02 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 510378.
denizevrenci added a comment.

Remove line breaks in CHECK-MESSAGES lines


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147417/new/

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ 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 );
+  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 ) {
+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 &) {
+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-2]]: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
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ 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);
 }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D147417: [clang-tidy] Do not emit bugprone-exception-escape warnings from coroutines

2023-04-02 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci created this revision.
denizevrenci added a reviewer: njames93.
Herald added subscribers: PiotrZSL, carlosgalvezp, ChuanqiXu, xazax.hun.
Herald added a project: All.
denizevrenci requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D147417

Files:
  clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -0,0 +1,121 @@
+// 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 );
+  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 ) {
+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 &) {
+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-2]]: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
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ 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()),
   

[PATCH] D144352: Do not emit exception diagnostics from coroutines and coroutine lambdas

2023-02-27 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

Author info for the commit: Deniz Evrenci (denizevre...@gmail.com)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144352/new/

https://reviews.llvm.org/D144352

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D144352: Do not emit exception diagnostics from coroutines and coroutine lambdas

2023-02-22 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci added a comment.

@NoQ I don't have commit access. Should I leave landing the diff to you?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144352/new/

https://reviews.llvm.org/D144352

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D144352: Do not emit exception diagnostics from coroutines and coroutine lambdas

2023-02-19 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci updated this revision to Diff 498695.
denizevrenci added a comment.

Added some throws that should be ignored.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144352/new/

https://reviews.llvm.org/D144352

Files:
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp


Index: clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions 
-verify -fdeclspec
+
+#include "Inputs/std-coroutine.h"
+
+// expected-no-diagnostics
+
+template 
+struct promise;
+ 
+template 
+struct task {
+using promise_type = promise;
+
+explicit task(promise_type& p) { throw 1; p.return_val = this; }
+
+task(const task&) = delete;
+
+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) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+task b_ShouldNotDiag(const int a, const int b) noexcept {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+const auto c_ShouldNotDiag = [](const int a, const int b) -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
+
+const auto d_ShouldNotDiag = [](const int a, const int b) noexcept -> 
task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2509,7 +2509,7 @@
   // Check for throw out of non-throwing function.
   if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getBeginLoc()))
 if (const FunctionDecl *FD = dyn_cast(D))
-  if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
+  if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && 
isNoexcept(FD))
 checkThrowInNonThrowingFunc(S, FD, AC);
 
   // Emit unsafe buffer usage warnings and fixits.


Index: clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -fdeclspec
+
+#include "Inputs/std-coroutine.h"
+
+// expected-no-diagnostics
+
+template 
+struct promise;
+ 
+template 
+struct task {
+using promise_type = promise;
+
+explicit task(promise_type& p) { throw 1; p.return_val = this; }
+
+task(const task&) = delete;
+
+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) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+task b_ShouldNotDiag(const int a, const int b) noexcept {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+const auto c_ShouldNotDiag = [](const int a, const int b) -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
+
+const auto d_ShouldNotDiag = [](const int a, const int b) noexcept -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2509,7 +2509,7 @@
   // Check for throw out of non-throwing function.
   if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getBeginLoc()))
 if (const FunctionDecl *FD = dyn_cast(D))
-  if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
+  if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD))
 checkThrowInNonThrowingFunc(S, FD, AC);
 
   // Emit unsafe buffer usage warnings and fixits.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D144352: Do not emit exception diagnostics from coroutines and coroutine lambdas

2023-02-19 Thread Deniz Evrenci via Phabricator via cfe-commits
denizevrenci created this revision.
denizevrenci added reviewers: NoQ, gribozavr.
Herald added a subscriber: ChuanqiXu.
Herald added a project: All.
denizevrenci requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Fixes #48797

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D144352

Files:
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp


Index: clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions 
-verify -fdeclspec
+
+#include "Inputs/std-coroutine.h"
+
+// expected-no-diagnostics
+
+template 
+struct promise;
+ 
+template 
+struct task {
+using promise_type = promise;
+
+explicit task(promise_type& p) noexcept { p.return_val = this; }
+
+task(const task&) = delete;
+
+T value;
+};
+ 
+template 
+struct promise {
+task get_return_object() noexcept { 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() {}
+
+task* return_val;
+};
+
+task a_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+task b_ShouldNotDiag(const int a, const int b) noexcept {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+const auto c_ShouldNotDiag = [](const int a, const int b) -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
+
+const auto d_ShouldNotDiag = [](const int a, const int b) noexcept -> 
task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2509,7 +2509,7 @@
   // Check for throw out of non-throwing function.
   if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getBeginLoc()))
 if (const FunctionDecl *FD = dyn_cast(D))
-  if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
+  if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && 
isNoexcept(FD))
 checkThrowInNonThrowingFunc(S, FD, AC);
 
   // Emit unsafe buffer usage warnings and fixits.


Index: clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
===
--- /dev/null
+++ clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -fdeclspec
+
+#include "Inputs/std-coroutine.h"
+
+// expected-no-diagnostics
+
+template 
+struct promise;
+ 
+template 
+struct task {
+using promise_type = promise;
+
+explicit task(promise_type& p) noexcept { p.return_val = this; }
+
+task(const task&) = delete;
+
+T value;
+};
+ 
+template 
+struct promise {
+task get_return_object() noexcept { 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() {}
+
+task* return_val;
+};
+
+task a_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+task b_ShouldNotDiag(const int a, const int b) noexcept {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+}
+
+const auto c_ShouldNotDiag = [](const int a, const int b) -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
+
+const auto d_ShouldNotDiag = [](const int a, const int b) noexcept -> task {
+  if (b == 0)
+throw b;
+
+  co_return a / b;
+};
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2509,7 +2509,7 @@
   // Check for throw out of non-throwing function.
   if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getBeginLoc()))
 if (const FunctionDecl *FD = dyn_cast(D))
-  if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
+  if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD))