PiotrZSL updated this revision to Diff 532783.
PiotrZSL marked 3 inline comments as done.
PiotrZSL added a comment.

Review fixes.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153298

Files:
  clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -9,6 +9,7 @@
 struct throwing_destructor {
   ~throwing_destructor() {
     // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function '~throwing_destructor' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here
     throw 1;
   }
 };
@@ -16,6 +17,7 @@
 struct throwing_move_constructor {
   throwing_move_constructor(throwing_move_constructor&&) {
     // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'throwing_move_constructor' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here
     throw 1;
   }
 };
@@ -23,12 +25,14 @@
 struct throwing_move_assignment {
   throwing_move_assignment& operator=(throwing_move_assignment&&) {
     // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: an exception may be thrown in function 'operator=' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE+1]]:5: note: may throw 'int' here
     throw 1;
   }
 };
 
 void throwing_noexcept() noexcept {
-    // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here
   throw 1;
 }
 
@@ -42,6 +46,7 @@
 
 void throw_and_catch_some(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch_some' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'double' here
   try {
     if (n) throw 1;
     throw 1.1;
@@ -70,6 +75,7 @@
 
 void throw_and_rethrow() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_rethrow' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+2]]:5: note: may throw 'int' here
   try {
     throw 1;
   } catch(int &) {
@@ -79,6 +85,7 @@
 
 void throw_catch_throw() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_throw' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'int' here
   try {
     throw 1;
   } catch(int &) {
@@ -88,6 +95,7 @@
 
 void throw_catch_rethrow_the_rest(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_rethrow_the_rest' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'double' here
   try {
     if (n) throw 1;
     throw 1.1;
@@ -120,6 +128,7 @@
 
 void throw_catch_multi_ptr_1() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'char **' here
   try {
     char **p = 0;
     throw p;
@@ -152,7 +161,7 @@
 }
 
 // FIXME: In this case 'a' is convertible to the handler and should be caught
-// but in reality it's thrown. Note that clang doesn't report a warning for 
+// but in reality it's thrown. Note that clang doesn't report a warning for
 // this either.
 void throw_catch_multi_ptr_5() noexcept {
   try {
@@ -165,6 +174,7 @@
 
 void throw_c_catch_pointer() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const int *' here
   try {
     int a = 1;
     const int *p = &a;
@@ -174,6 +184,7 @@
 
 void throw_c_catch_pointer_v() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const int *' here
   try {
     int a = 1;
     const int *p = &a;
@@ -183,6 +194,7 @@
 
 void throw_v_catch_pointer() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'volatile int *' here
   try {
     int a = 1;
     volatile int *p = &a;
@@ -192,6 +204,7 @@
 
 void throw_v_catch_pointer_c() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'volatile int *' here
   try {
     int a = 1;
     volatile int *p = &a;
@@ -201,6 +214,7 @@
 
 void throw_cv_catch_pointer_c() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const volatile int *' here
   try {
     int a = 1;
     const volatile int *p = &a;
@@ -210,6 +224,7 @@
 
 void throw_cv_catch_pointer_v() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const volatile int *' here
   try {
     int a = 1;
     const volatile int *p = &a;
@@ -249,17 +264,18 @@
 void throw_derived_catch_base_ptr_c() noexcept {
   try {
     derived d;
-    throw &d; 
+    throw &d;
   } catch(const base *) {
   }
 }
 
 void throw_derived_catch_base_ptr() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'const derived *' here
   try {
     derived d;
     const derived *p = &d;
-    throw p; 
+    throw p;
   } catch(base *) {
   }
 }
@@ -280,55 +296,61 @@
 
 void throw_derived_catch_base_private() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'B' here
   try {
     B b;
-    throw b; 
+    throw b;
   } catch(A) {
   }
 }
 
 void throw_derived_catch_base_private_ptr() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private_ptr' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'B *' here
   try {
     B b;
-    throw &b; 
+    throw &b;
   } catch(A *) {
   }
 }
 
 void throw_derived_catch_base_protected() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'C' here
   try {
     C c;
-    throw c; 
+    throw c;
   } catch(A) {
   }
 }
 
 void throw_derived_catch_base_protected_ptr() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_protected_ptr' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'C *' here
   try {
     C c;
-    throw &c; 
+    throw &c;
   } catch(A *) {
   }
 }
 
 void throw_derived_catch_base_ambiguous() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'E' here
   try {
     E e;
-    throw e; 
+    throw e;
   } catch(A) {
   }
 }
 
 void throw_derived_catch_base_ambiguous_ptr() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ambiguous_ptr' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'E *' here
   try {
     E e;
-    throw e; 
-  } catch(A) {
+    throw &e;
+  } catch(A *) {
   }
 }
 
@@ -344,6 +366,7 @@
 
 void throw_alias_catch_original_warn() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_alias_catch_original_warn' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'float' here
   using alias = float;
 
   try {
@@ -365,6 +388,7 @@
 
 void throw_original_catch_alias_warn() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_original_catch_alias_warn' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'char **' here
   using alias = int;
 
   try {
@@ -428,6 +452,7 @@
 
 void throw_basefn_catch_derivedfn() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basefn_catch_derivedfn' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+2]]:5: note: may throw 'void (baseMember::*)()' here
   try {
     throw &baseMember::foo;
   } catch(void(derivedMember::*)()) {
@@ -443,6 +468,7 @@
 
 void throw_basem_catch_basem_throw() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basem_catch_basem_throw' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *baseMember::**' here
   try {
     auto ptr = &baseMember::iptr;
     throw &ptr;
@@ -460,6 +486,7 @@
 
 void throw_basem_catch_derivedm() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_basem_catch_derivedm' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *baseMember::**' here
   try {
     auto ptr = &baseMember::iptr;
     throw &ptr;
@@ -469,6 +496,7 @@
 
 void throw_derivedm_catch_basem() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derivedm_catch_basem' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'int *derivedMember::**' here
   try {
     int *derivedMember::* ptr = &derivedMember::iptr;
     throw &ptr;
@@ -478,6 +506,7 @@
 
 void throw_original_catch_alias_2_warn() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_original_catch_alias_2_warn' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+5]]:5: note: may throw 'char **' here
   using alias = const int *const;
 
   try {
@@ -501,6 +530,7 @@
 
 void bad_try_nested_try(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_try_nested_try' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+2]]:12: note: may throw 'int' here
   try {
     if (n) throw 1;
     try {
@@ -537,6 +567,7 @@
 
 void bad_catch_nested_try() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_catch_nested_try' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+5]]:7: note: may throw 'double' here
   try {
     throw 1;
   } catch(int &) {
@@ -558,11 +589,13 @@
 
 void indirect_implicit() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_implicit' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-9]]:3: note: may throw 'int' here
   implicit_int_thrower();
 }
 
 void indirect_explicit() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_explicit' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-11]]:3: note: may throw 'int' here
   explicit_int_thrower();
 }
 
@@ -583,6 +616,7 @@
 
 void swap(int&, int&) {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'swap' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here
   throw 1;
 }
 
@@ -601,11 +635,13 @@
 
 void enabled1() {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled1' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here
   throw 1;
 }
 
 void enabled2() {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled2' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here
   enabled1();
 }
 
@@ -636,6 +672,7 @@
 
 void this_counts(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'this_counts' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+1]]:10: note: may throw 'int' here
   if (n) throw 1;
   throw ignored1();
 }
@@ -646,6 +683,7 @@
 
 int directly_recursive(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'directly_recursive' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here
   if (n == 0)
     thrower(n);
   return directly_recursive(n);
@@ -659,6 +697,7 @@
 
 int indirectly_recursive(int n) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'indirectly_recursive' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-19]]:3: note: may throw 'int' here
   if (n == 0)
     thrower(n);
   return recursion_helper(n);
@@ -671,6 +710,7 @@
 struct sub_throws : super_throws {
   sub_throws() noexcept : super_throws() {}
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-6]]:36: note: may throw 'int' here
 };
 
 struct init_member_throws {
@@ -678,6 +718,7 @@
 
   init_member_throws() noexcept : s() {}
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'init_member_throws' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-14]]:36: note: may throw 'int' here
 };
 
 struct implicit_init_member_throws {
@@ -685,6 +726,7 @@
 
   implicit_init_member_throws() noexcept {}
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'implicit_init_member_throws' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-22]]:36: note: may throw 'int' here
 };
 
 struct init {
@@ -696,10 +738,35 @@
 
   in_class_init_throws() noexcept {}
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'in_class_init_throws' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-8]]:45: note: may throw 'int' here
 };
 
 int main() {
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'main' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here
   throw 1;
   return 0;
 }
+
+struct Exception1 {};
+struct Exception2 {};
+
+void throwException1Or2(bool value) noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwException1Or2' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE+3]]:5: note: may throw 'Exception1' here
+// CHECK-MESSAGES: :[[@LINE+4]]:5: note: may throw 'Exception2' here
+  if (value)
+    throw Exception1();
+  else
+    throw Exception2();
+}
+
+void throwSomething() noexcept(false);
+
+void throwKnownAndUnknownException() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwKnownAndUnknownException' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE+3]]:3: note: may throw 'Exception1' here
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: may throw unknown exceptions here
+  throwSomething();
+  throw Exception1();
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
@@ -2,6 +2,7 @@
 
 void throwing_throw_nothing() throw() {
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_throw_nothing' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE+1]]:3: note: may throw 'int' here
   throw 1;
 }
 
@@ -13,11 +14,14 @@
 
 void indirect_implicit() throw() {
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_implicit' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here
   implicit_int_thrower();
 }
 
 void indirect_explicit() throw() {
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_explicit' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE-14]]:29: note: may throw 'int' here
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: may throw unknown exceptions here
   explicit_int_thrower();
 }
 
@@ -28,4 +32,5 @@
 struct sub_throws : super_throws {
   sub_throws() throw() : super_throws() {}
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-6]]:31: note: may throw 'int' here
 };
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -15,6 +15,7 @@
 
 int throwsAndCallsRethrower() noexcept {
 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsRethrower' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE+2]]:9: note: may throw 'int' here
     try {
         throw 1;
     } catch(...) {
@@ -24,6 +25,7 @@
 
 int throwsAndCallsCallsRethrower() noexcept {
 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'throwsAndCallsCallsRethrower' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE+2]]:9: note: may throw 'int' here
     try {
         throw 1;
     } catch(...) {
@@ -34,3 +36,18 @@
 void rethrowerNoexcept() noexcept {
     throw;
 }
+
+void throwInt() {
+  throw 5;
+}
+
+void rethrowInt() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'rethrowInt' which should not throw exceptions [bugprone-exception-escape]
+// CHECK-MESSAGES: :[[@LINE-5]]:3: note: may throw 'int' here
+  try {
+    throwInt();
+  } catch(int) {
+    throw;
+  } catch(...) {
+  }
+}
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -253,8 +253,9 @@
   Global options of the same name should be used instead.
 
 - Improved :doc:`bugprone-exception-escape
-  <clang-tidy/checks/bugprone/exception-escape>` to not emit warnings for
-  forward declarations of functions.
+  <clang-tidy/checks/bugprone/exception-escape>` check to not emit warnings for
+  forward declarations of functions. Emit additional diagnostic information
+  about uncaught exceptions.
 
 - Fixed :doc:`bugprone-exception-escape<clang-tidy/checks/bugprone/exception-escape>`
   for coroutines where previously a warning would be emitted with coroutines
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -13,6 +13,7 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSet.h"
+#include <functional>
 
 namespace clang::tidy::utils {
 
@@ -35,9 +36,23 @@
   /// does not include *ALL* possible types as there is the possibility that
   /// an 'Unknown' function is called that might throw a previously unknown
   /// exception at runtime.
+
   class ExceptionInfo {
   public:
-    using Throwables = llvm::SmallSet<const Type *, 2>;
+    struct ThrowInfo {
+      const Type *ThrowType;
+      SourceLocation ThrowLoc;
+
+      bool operator<(const ThrowInfo &R) const noexcept {
+        return ThrowType < R.ThrowType;
+      }
+
+      bool operator==(const ThrowInfo &R) const noexcept {
+        return ThrowType == R.ThrowType;
+      }
+    };
+
+    using Throwables = llvm::SmallSet<ThrowInfo, 2>;
     static ExceptionInfo createUnknown() {
       return ExceptionInfo(State::Unknown);
     }
@@ -60,11 +75,12 @@
 
     /// Register a single exception type as recognized potential exception to be
     /// thrown.
-    void registerException(const Type *ExceptionType);
+    void registerException(const Type *ExceptionType, const SourceLocation Loc);
 
     /// Registers a `SmallVector` of exception types as recognized potential
     /// exceptions to be thrown.
-    void registerExceptions(const Throwables &Exceptions);
+    void registerExceptions(const Throwables &Exceptions,
+                            const SourceLocation Loc);
 
     /// Updates the local state according to the other state. That means if
     /// for example a function contains multiple statements the 'ExceptionInfo'
@@ -78,7 +94,8 @@
     /// possible to catch multiple exception types by one 'catch' if they
     /// are a subclass of the 'catch'ed exception type.
     /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
-    bool filterByCatch(const Type *BaseClass, const ASTContext &Context);
+    bool filterByCatch(const Type *BaseClass, const ASTContext &Context,
+                       Throwables &CaughtExceptions);
 
     /// Filter the set of thrown exception type against a set of ignored
     /// types that shall not be considered in the exception analysis.
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -11,18 +11,20 @@
 namespace clang::tidy::utils {
 
 void ExceptionAnalyzer::ExceptionInfo::registerException(
-    const Type *ExceptionType) {
+    const Type *ExceptionType, const SourceLocation Loc) {
   assert(ExceptionType != nullptr && "Only valid types are accepted");
   Behaviour = State::Throwing;
-  ThrownExceptions.insert(ExceptionType);
+  ThrownExceptions.insert({ExceptionType, Loc});
 }
 
 void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
-    const Throwables &Exceptions) {
-  if (Exceptions.size() == 0)
+    const Throwables &Exceptions, const SourceLocation Loc) {
+  if (Exceptions.empty())
     return;
   Behaviour = State::Throwing;
-  ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
+  for (const ThrowInfo &Info : Exceptions)
+    ThrownExceptions.insert(
+        {Info.ThrowType, Info.ThrowLoc.isInvalid() ? Loc : Info.ThrowLoc});
 }
 
 ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge(
@@ -317,9 +319,10 @@
 } // namespace
 
 bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
-    const Type *HandlerTy, const ASTContext &Context) {
+    const Type *HandlerTy, const ASTContext &Context,
+    ExceptionInfo::Throwables &CaughtExceptions) {
   llvm::SmallVector<const Type *, 8> TypesToDelete;
-  for (const Type *ExceptionTy : ThrownExceptions) {
+  for (auto [ExceptionTy, ThrowLoc] : ThrownExceptions) {
     CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
     CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
 
@@ -327,13 +330,17 @@
     // (ignoring the top-level cv-qualifiers) ...
     if (ExceptionCanTy == HandlerCanTy) {
       TypesToDelete.push_back(ExceptionTy);
+      CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+      continue;
     }
 
     // The handler is of type cv T or cv T& and T is an unambiguous public base
     // class of E ...
-    else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
-                                          HandlerCanTy->getTypePtr())) {
+    if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
+                                     HandlerCanTy->getTypePtr())) {
       TypesToDelete.push_back(ExceptionTy);
+      CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+      continue;
     }
 
     if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
@@ -352,15 +359,21 @@
               ExceptionCanTy->getTypePtr()->getPointeeType().getTypePtr(),
               HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr())) {
         TypesToDelete.push_back(ExceptionTy);
+        CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+        continue;
       }
       // A function pointer conversion ...
-      else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
+      if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
         TypesToDelete.push_back(ExceptionTy);
+        CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+        continue;
       }
       // A a qualification conversion ...
-      else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
-                                                 Context.getLangOpts())) {
+      if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
+                                            Context.getLangOpts())) {
         TypesToDelete.push_back(ExceptionTy);
+        CaughtExceptions.insert({ExceptionTy, ThrowLoc});
+        continue;
       }
     }
 
@@ -369,11 +382,12 @@
     else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
              ExceptionCanTy->isNullPtrType()) {
       TypesToDelete.push_back(ExceptionTy);
+      CaughtExceptions.insert({ExceptionTy, ThrowLoc});
     }
   }
 
   for (const Type *T : TypesToDelete)
-    ThrownExceptions.erase(T);
+    ThrownExceptions.erase({T, SourceLocation()});
 
   reevaluateBehaviour();
   return TypesToDelete.size() > 0;
@@ -385,7 +399,7 @@
   llvm::SmallVector<const Type *, 8> TypesToDelete;
   // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible.
   // Therefore this slightly hacky implementation is required.
-  for (const Type *T : ThrownExceptions) {
+  for (auto [T, TLoc] : ThrownExceptions) {
     if (const auto *TD = T->getAsTagDecl()) {
       if (TD->getDeclName().isIdentifier()) {
         if ((IgnoreBadAlloc &&
@@ -396,7 +410,7 @@
     }
   }
   for (const Type *T : TypesToDelete)
-    ThrownExceptions.erase(T);
+    ThrownExceptions.erase({T, SourceLocation()});
 
   reevaluateBehaviour();
   return *this;
@@ -444,7 +458,8 @@
   auto Result = ExceptionInfo::createUnknown();
   if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
     for (const QualType &Ex : FPT->exceptions())
-      Result.registerException(Ex.getTypePtr());
+      Result.registerException(Ex.getTypePtr(),
+                               Func->getExceptionSpecSourceRange().getBegin());
   }
   return Result;
 }
@@ -467,12 +482,13 @@
                          ->getPointeeType()
                          ->getUnqualifiedDesugaredType();
       Results.registerException(
-          ThrownExpr->getType()->getUnqualifiedDesugaredType());
+          ThrownExpr->getType()->getUnqualifiedDesugaredType(),
+          Throw->getThrowLoc());
     } else
       // A rethrow of a caught exception happens which makes it possible
       // to throw all exception that are caught in the 'catch' clause of
       // the parent try-catch block.
-      Results.registerExceptions(Caught);
+      Results.registerExceptions(Caught, Throw->getThrowLoc());
   } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
     ExceptionInfo Uncaught =
         throwsException(Try->getTryBlock(), Caught, CallStack);
@@ -498,10 +514,11 @@
         // thrown types (because it's sensitive to inheritance) the throwing
         // situation changes. First of all filter the exception types and
         // analyze if the baseclass-exception is rethrown.
-        if (Uncaught.filterByCatch(
-                CaughtType, Catch->getExceptionDecl()->getASTContext())) {
-          ExceptionInfo::Throwables CaughtExceptions;
-          CaughtExceptions.insert(CaughtType);
+        ExceptionInfo::Throwables CaughtExceptions;
+        if (Uncaught.filterByCatch(CaughtType,
+                                   Catch->getExceptionDecl()->getASTContext(),
+                                   CaughtExceptions)) {
+          CaughtExceptions.insert({CaughtType, SourceLocation()});
           ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
                                                    CaughtExceptions, CallStack);
           Results.merge(Rethrown);
@@ -532,7 +549,7 @@
     ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
     Results.merge(throwsException(Coro->getExceptionHandler(),
                                   Excs.getExceptionTypes(), CallStack));
-    for (const Type *Throwable : Excs.getExceptionTypes()) {
+    for (auto [Throwable, ThrowLoc] : Excs.getExceptionTypes()) {
       if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
         ExceptionInfo DestructorExcs =
             throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
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
@@ -68,13 +68,22 @@
   if (!MatchedDecl)
     return;
 
-  if (Tracer.analyze(MatchedDecl).getBehaviour() ==
-      utils::ExceptionAnalyzer::State::Throwing)
-    // FIXME: We should provide more information about the exact location where
-    // the exception is thrown, maybe the full path the exception escapes
-    diag(MatchedDecl->getLocation(), "an exception may be thrown in function "
-                                     "%0 which should not throw exceptions")
-        << MatchedDecl;
+  const utils::ExceptionAnalyzer::ExceptionInfo AnalyzeResult =
+      Tracer.analyze(MatchedDecl);
+  if (AnalyzeResult.getBehaviour() != utils::ExceptionAnalyzer::State::Throwing)
+    return;
+
+  diag(MatchedDecl->getLocation(), "an exception may be thrown in function "
+                                   "%0 which should not throw exceptions")
+      << MatchedDecl;
+
+  for (auto [ThrowType, ThrowLoc] : AnalyzeResult.getExceptionTypes())
+    diag(ThrowLoc, "may throw %0 here", DiagnosticIDs::Note)
+        << QualType(ThrowType, 0U);
+
+  if (AnalyzeResult.containsUnknownElements())
+    diag(MatchedDecl->getLocation(), "may throw unknown exceptions here",
+         DiagnosticIDs::Note);
 }
 
 } // namespace tidy::bugprone
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to