PiotrZSL updated this revision to Diff 551761.
PiotrZSL added a comment.

Rebase + Ping


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;
@@ -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;
@@ -256,6 +271,7 @@
 
 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;
@@ -280,6 +296,7 @@
 
 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;
@@ -289,6 +306,7 @@
 
 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;
@@ -298,6 +316,7 @@
 
 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;
@@ -307,6 +326,7 @@
 
 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;
@@ -316,6 +336,7 @@
 
 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;
@@ -325,10 +346,11 @@
 
 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,12 @@
 
   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;
 }
@@ -746,3 +790,26 @@
 };
 
 }}
+
+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
@@ -169,6 +169,11 @@
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Improved :doc:`bugprone-exception-escape
+  <clang-tidy/checks/bugprone/exception-escape>` check to not emit warnings for
+  forward declarations of functions. Emit additional diagnostic information
+  about uncaught exceptions.
+
 - Fixed bug in :doc:`bugprone-reserved-identifier
   <clang-tidy/checks/bugprone/reserved-identifier>`, so that it does not warn
   on macros starting with underscore and lowercase letter.
@@ -187,7 +192,7 @@
   ignore delegate constructors.
 
 - Improved :doc `cppcoreguidelines-pro-bounds-array-to-pointer-decay
-  <clang-tidy/checks/cppcoreguidelines/pro-bounds-array-to-pointer-decay>` check 
+  <clang-tidy/checks/cppcoreguidelines/pro-bounds-array-to-pointer-decay>` check
   to ignore predefined expression (e.g., ``__func__``, ...).
 
 - Improved :doc:`cppcoreguidelines-pro-type-member-init
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,8 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSet.h"
+#include <functional>
+#include <map>
 
 namespace clang::tidy::utils {
 
@@ -35,9 +37,11 @@
   /// 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>;
+    using Throwables = std::map<const Type *, SourceLocation>;
+
     static ExceptionInfo createUnknown() {
       return ExceptionInfo(State::Unknown);
     }
@@ -60,11 +64,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 +83,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,19 @@
 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.emplace(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 auto [ThrowType, ThrowLoc] : Exceptions)
+    ThrownExceptions.emplace(ThrowType, ThrowLoc.isInvalid() ? Loc : ThrowLoc);
 }
 
 ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge(
@@ -345,29 +346,29 @@
 }
 
 bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
-    const Type *HandlerTy, const ASTContext &Context) {
-  llvm::SmallVector<const Type *, 8> TypesToDelete;
-  for (const Type *ExceptionTy : ThrownExceptions) {
+    const Type *HandlerTy, const ASTContext &Context,
+    ExceptionInfo::Throwables &CaughtExceptions) {
+
+  auto ShouldRemoveFnt = [HandlerTy, &Context](const Type *ExceptionTy) {
     CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
     CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
 
     // The handler is of type cv T or cv T& and E and T are the same type
     // (ignoring the top-level cv-qualifiers) ...
-    if (ExceptionCanTy == HandlerCanTy) {
-      TypesToDelete.push_back(ExceptionTy);
-    }
+    if (ExceptionCanTy == HandlerCanTy)
+      return true;
 
     // 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())) {
-      TypesToDelete.push_back(ExceptionTy);
-    }
+    if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
+                                     HandlerCanTy->getTypePtr()))
+      return true;
 
     if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
         (HandlerCanTy->getTypeClass() == Type::LValueReference &&
          !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
-      continue;
+      return false;
+
     // The handler is of type cv T or const T& where T is a pointer or
     // pointer-to-member type and E is a pointer or pointer-to-member type that
     // can be converted to T by one or more of ...
@@ -378,53 +379,59 @@
       if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy) &&
           isUnambiguousPublicBaseClass(
               ExceptionCanTy->getTypePtr()->getPointeeType().getTypePtr(),
-              HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr())) {
-        TypesToDelete.push_back(ExceptionTy);
-      }
+              HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr()))
+        return true;
       // A function pointer conversion ...
-      else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
-        TypesToDelete.push_back(ExceptionTy);
-      }
+      if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy))
+        return true;
       // A a qualification conversion ...
-      else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
-                                                 Context.getLangOpts())) {
-        TypesToDelete.push_back(ExceptionTy);
-      }
+      if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
+                                            Context.getLangOpts()))
+        return true;
+      return false;
     }
 
     // The handler is of type cv T or const T& where T is a pointer or
     // pointer-to-member type and E is std::nullptr_t.
-    else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
-             ExceptionCanTy->isNullPtrType()) {
-      TypesToDelete.push_back(ExceptionTy);
+    return isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
+           ExceptionCanTy->isNullPtrType();
+  };
+
+  bool Result = false;
+  for (Throwables::iterator It = ThrownExceptions.begin();
+       It != ThrownExceptions.end();) {
+    if (!ShouldRemoveFnt(It->first)) {
+      ++It;
+      continue;
     }
-  }
 
-  for (const Type *T : TypesToDelete)
-    ThrownExceptions.erase(T);
+    CaughtExceptions.emplace(*It);
+    It = ThrownExceptions.erase(It);
+    Result = true;
+  }
 
   reevaluateBehaviour();
-  return TypesToDelete.size() > 0;
+  return Result;
 }
 
 ExceptionAnalyzer::ExceptionInfo &
 ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
     const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) {
-  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) {
-    if (const auto *TD = T->getAsTagDecl()) {
+
+  for (Throwables::iterator It = ThrownExceptions.begin();
+       It != ThrownExceptions.end();) {
+    if (const auto *TD = It->first->getAsTagDecl()) {
       if (TD->getDeclName().isIdentifier()) {
         if ((IgnoreBadAlloc &&
              (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
-            (IgnoredTypes.count(TD->getName()) > 0))
-          TypesToDelete.push_back(T);
+            (IgnoredTypes.count(TD->getName()) > 0)) {
+          It = ThrownExceptions.erase(It);
+          continue;
+        }
       }
     }
+    ++It;
   }
-  for (const Type *T : TypesToDelete)
-    ThrownExceptions.erase(T);
 
   reevaluateBehaviour();
   return *this;
@@ -472,7 +479,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;
 }
@@ -495,12 +503,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);
@@ -526,10 +535,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.emplace(CaughtType, SourceLocation());
           ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
                                                    CaughtExceptions, CallStack);
           Results.merge(Rethrown);
@@ -560,7 +570,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
@@ -76,13 +76,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 clang::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