Author: Baranov Victor
Date: 2025-07-14T21:59:28+03:00
New Revision: 6ac286cd491b419dd18a6e8de3aaef4caa44e093

URL: 
https://github.com/llvm/llvm-project/commit/6ac286cd491b419dd18a6e8de3aaef4caa44e093
DIFF: 
https://github.com/llvm/llvm-project/commit/6ac286cd491b419dd18a6e8de3aaef4caa44e093.diff

LOG: [clang-tidy] Improve `bugprone-exception-escape`: add stacktrace of 
escaped exception (#134375)

This PR add stacktrace of escaped exception to
`bugprone-exception-escape` check.
Changes:
1. Modified `ExceptionAnalyzer` and `ExceptionInfo` classes to hold
stacktrace of escaped exception in `llvm::MapVector`. `llvm::MapVector`
is needed to hold relative positions of functions in stack as well as
have fast lookup.
2. Added new diagnostics based of `misc-no-recursion` check.

Fixes https://github.com/llvm/llvm-project/issues/87422.

Added: 
    

Modified: 
    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-coro.cpp
    
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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
index f008fa4eb9ef7..8eb7881a47a26 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -78,13 +78,45 @@ void ExceptionEscapeCheck::check(const 
MatchFinder::MatchResult &Result) {
   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 Info =
+      Tracer.analyze(MatchedDecl);
+
+  if (Info.getBehaviour() != utils::ExceptionAnalyzer::State::Throwing)
+    return;
+
+  diag(MatchedDecl->getLocation(), "an exception may be thrown in function "
+                                   "%0 which should not throw exceptions")
+      << MatchedDecl;
+
+  const auto &[ThrowType, ThrowInfo] = *Info.getExceptions().begin();
+
+  if (ThrowInfo.Loc.isInvalid())
+    return;
+
+  const utils::ExceptionAnalyzer::CallStack &Stack = ThrowInfo.Stack;
+  diag(ThrowInfo.Loc,
+       "frame #0: unhandled exception of type %0 may be thrown in function %1 "
+       "here",
+       DiagnosticIDs::Note)
+      << QualType(ThrowType, 0U) << Stack.back().first;
+
+  size_t FrameNo = 1;
+  for (auto CurrIt = ++Stack.rbegin(), PrevIt = Stack.rbegin();
+       CurrIt != Stack.rend(); ++CurrIt, ++PrevIt) {
+    const FunctionDecl *CurrFunction = CurrIt->first;
+    const FunctionDecl *PrevFunction = PrevIt->first;
+    const SourceLocation PrevLocation = PrevIt->second;
+    if (PrevLocation.isValid()) {
+      diag(PrevLocation, "frame #%0: function %1 calls function %2 here",
+           DiagnosticIDs::Note)
+          << FrameNo << CurrFunction << PrevFunction;
+    } else {
+      diag(CurrFunction->getLocation(),
+           "frame #%0: function %1 calls function %2", DiagnosticIDs::Note)
+          << FrameNo << CurrFunction << PrevFunction;
+    }
+    ++FrameNo;
+  }
 }
 
 } // namespace clang::tidy::bugprone

diff  --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp 
b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 6ccc05845220f..3fe8412e69675 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -11,10 +11,10 @@
 namespace clang::tidy::utils {
 
 void ExceptionAnalyzer::ExceptionInfo::registerException(
-    const Type *ExceptionType) {
+    const Type *ExceptionType, const ThrowInfo &ThrowInfo) {
   assert(ExceptionType != nullptr && "Only valid types are accepted");
   Behaviour = State::Throwing;
-  ThrownExceptions.insert(ExceptionType);
+  ThrownExceptions.insert({ExceptionType, ThrowInfo});
 }
 
 void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
@@ -356,10 +356,12 @@ static bool canThrow(const FunctionDecl *Func) {
   };
 }
 
-bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
-    const Type *HandlerTy, const ASTContext &Context) {
+ExceptionAnalyzer::ExceptionInfo::Throwables
+ExceptionAnalyzer::ExceptionInfo::filterByCatch(const Type *HandlerTy,
+                                                const ASTContext &Context) {
   llvm::SmallVector<const Type *, 8> TypesToDelete;
-  for (const Type *ExceptionTy : ThrownExceptions) {
+  for (const auto &ThrownException : ThrownExceptions) {
+    const Type *ExceptionTy = ThrownException.getFirst();
     CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
     CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
 
@@ -409,11 +411,18 @@ bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
     }
   }
 
-  for (const Type *T : TypesToDelete)
-    ThrownExceptions.erase(T);
+  Throwables DeletedExceptions;
+
+  for (const Type *TypeToDelete : TypesToDelete) {
+    const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
+    if (DeleteIt != ThrownExceptions.end()) {
+      DeletedExceptions.insert(*DeleteIt);
+      ThrownExceptions.erase(DeleteIt);
+    }
+  }
 
   reevaluateBehaviour();
-  return !TypesToDelete.empty();
+  return DeletedExceptions;
 }
 
 ExceptionAnalyzer::ExceptionInfo &
@@ -422,7 +431,8 @@ ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
   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 (const auto &ThrownException : ThrownExceptions) {
+    const Type *T = ThrownException.getFirst();
     if (const auto *TD = T->getAsTagDecl()) {
       if (TD->getDeclName().isIdentifier()) {
         if ((IgnoreBadAlloc &&
@@ -454,16 +464,15 @@ void 
ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
   else
     Behaviour = State::Throwing;
 }
-
 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
     const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
-    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+    CallStack &CallStack, SourceLocation CallLoc) {
   if (!Func || CallStack.contains(Func) ||
       (!CallStack.empty() && !canThrow(Func)))
     return ExceptionInfo::createNonThrowing();
 
   if (const Stmt *Body = Func->getBody()) {
-    CallStack.insert(Func);
+    CallStack.insert({Func, CallLoc});
     ExceptionInfo Result = throwsException(Body, Caught, CallStack);
 
     // For a constructor, we also have to check the initializers.
@@ -481,17 +490,23 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
 
   auto Result = ExceptionInfo::createUnknown();
   if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
-    for (const QualType &Ex : FPT->exceptions())
-      Result.registerException(Ex.getTypePtr());
+    for (const QualType &Ex : FPT->exceptions()) {
+      CallStack.insert({Func, CallLoc});
+      Result.registerException(
+          Ex.getTypePtr(),
+          {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
+      CallStack.erase(Func);
+    }
   }
   return Result;
 }
 
 /// Analyzes a single statement on it's throwing behaviour. This is in 
principle
 /// possible except some 'Unknown' functions are called.
-ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
-    const Stmt *St, const ExceptionInfo::Throwables &Caught,
-    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+ExceptionAnalyzer::ExceptionInfo
+ExceptionAnalyzer::throwsException(const Stmt *St,
+                                   const ExceptionInfo::Throwables &Caught,
+                                   CallStack &CallStack) {
   auto Results = ExceptionInfo::createNonThrowing();
   if (!St)
     return Results;
@@ -505,7 +520,8 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
                          ->getPointeeType()
                          ->getUnqualifiedDesugaredType();
       Results.registerException(
-          ThrownExpr->getType()->getUnqualifiedDesugaredType());
+          ThrownExpr->getType()->getUnqualifiedDesugaredType(),
+          {Throw->getBeginLoc(), CallStack});
     } else
       // A rethrow of a caught exception happens which makes it possible
       // to throw all exception that are caught in the 'catch' clause of
@@ -520,7 +536,7 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
       // Everything is caught through 'catch(...)'.
       if (!Catch->getExceptionDecl()) {
         ExceptionInfo Rethrown = throwsException(
-            Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
+            Catch->getHandlerBlock(), Uncaught.getExceptions(), CallStack);
         Results.merge(Rethrown);
         Uncaught.clear();
       } else {
@@ -536,12 +552,12 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
         // 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 Rethrown = throwsException(Catch->getHandlerBlock(),
-                                                   CaughtExceptions, 
CallStack);
+        const ExceptionInfo::Throwables FilteredExceptions =
+            Uncaught.filterByCatch(CaughtType,
+                                   Catch->getExceptionDecl()->getASTContext());
+        if (!FilteredExceptions.empty()) {
+          ExceptionInfo Rethrown = throwsException(
+              Catch->getHandlerBlock(), FilteredExceptions, CallStack);
           Results.merge(Rethrown);
         }
       }
@@ -549,12 +565,13 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
     Results.merge(Uncaught);
   } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
     if (const FunctionDecl *Func = Call->getDirectCallee()) {
-      ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
+      ExceptionInfo Excs =
+          throwsException(Func, Caught, CallStack, Call->getBeginLoc());
       Results.merge(Excs);
     }
   } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
-    ExceptionInfo Excs =
-        throwsException(Construct->getConstructor(), Caught, CallStack);
+    ExceptionInfo Excs = throwsException(Construct->getConstructor(), Caught,
+                                         CallStack, Construct->getBeginLoc());
     Results.merge(Excs);
   } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
     ExceptionInfo Excs =
@@ -569,11 +586,12 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
     }
     ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
     Results.merge(throwsException(Coro->getExceptionHandler(),
-                                  Excs.getExceptionTypes(), CallStack));
-    for (const Type *Throwable : Excs.getExceptionTypes()) {
-      if (const auto *ThrowableRec = Throwable->getAsCXXRecordDecl()) {
-        ExceptionInfo DestructorExcs =
-            throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
+                                  Excs.getExceptions(), CallStack));
+    for (const auto &Exception : Excs.getExceptions()) {
+      const Type *ExcType = Exception.getFirst();
+      if (const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
+        ExceptionInfo DestructorExcs = throwsException(
+            ThrowableRec->getDestructor(), Caught, CallStack, 
SourceLocation{});
         Results.merge(DestructorExcs);
       }
     }
@@ -593,9 +611,9 @@ ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
   // Check if the function has already been analyzed and reuse that result.
   const auto CacheEntry = FunctionCache.find(Func);
   if (CacheEntry == FunctionCache.end()) {
-    llvm::SmallSet<const FunctionDecl *, 32> CallStack;
-    ExceptionList =
-        throwsException(Func, ExceptionInfo::Throwables(), CallStack);
+    CallStack CallStack;
+    ExceptionList = throwsException(Func, ExceptionInfo::Throwables(),
+                                    CallStack, Func->getLocation());
 
     // Cache the result of the analysis. This is done prior to filtering
     // because it is best to keep as much information as possible.
@@ -610,7 +628,7 @@ ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
 
 ExceptionAnalyzer::ExceptionInfo
 ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
-  llvm::SmallSet<const FunctionDecl *, 32> CallStack;
+  CallStack CallStack;
   return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
 }
 

diff  --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h 
b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
index 6c2d693d64b50..bd466c99c04bb 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -12,6 +12,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringSet.h"
 
 namespace clang::tidy::utils {
@@ -28,6 +29,10 @@ class ExceptionAnalyzer {
                  ///< definition.
   };
 
+  /// We use a MapVector to preserve the order of the functions in the call
+  /// stack as well as have fast lookup.
+  using CallStack = llvm::MapVector<const FunctionDecl *, SourceLocation>;
+
   /// Bundle the gathered information about an entity like a function regarding
   /// it's exception behaviour. The 'NonThrowing'-state can be considered as 
the
   /// neutral element in terms of information propagation.
@@ -37,7 +42,15 @@ class ExceptionAnalyzer {
   /// exception at runtime.
   class ExceptionInfo {
   public:
-    using Throwables = llvm::SmallSet<const Type *, 2>;
+    /// Holds information about where an exception is thrown.
+    /// First element in the call stack is analyzed function.
+    struct ThrowInfo {
+      SourceLocation Loc;
+      CallStack Stack;
+    };
+
+    using Throwables = llvm::SmallDenseMap<const Type *, ThrowInfo, 2>;
+
     static ExceptionInfo createUnknown() { return {State::Unknown}; }
     static ExceptionInfo createNonThrowing() { return {State::Throwing}; }
 
@@ -56,7 +69,8 @@ class ExceptionAnalyzer {
 
     /// Register a single exception type as recognized potential exception to 
be
     /// thrown.
-    void registerException(const Type *ExceptionType);
+    void registerException(const Type *ExceptionType,
+                           const ThrowInfo &ThrowInfo);
 
     /// Registers a `SmallVector` of exception types as recognized potential
     /// exceptions to be thrown.
@@ -73,8 +87,8 @@ class ExceptionAnalyzer {
     /// This method is useful in case 'catch' clauses are analyzed as it is
     /// 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 *HandlerTy, const ASTContext &Context);
+    /// Returns filtered exceptions.
+    Throwables filterByCatch(const Type *HandlerTy, const ASTContext &Context);
 
     /// Filter the set of thrown exception type against a set of ignored
     /// types that shall not be considered in the exception analysis.
@@ -87,9 +101,9 @@ class ExceptionAnalyzer {
     /// neutral.
     void clear();
 
-    /// References the set of known exception types that can escape from the
+    /// References the set of known exceptions that can escape from the
     /// corresponding entity.
-    const Throwables &getExceptionTypes() const { return ThrownExceptions; }
+    const Throwables &getExceptions() const { return ThrownExceptions; }
 
     /// Signal if the there is any 'Unknown' element within the scope of
     /// the related entity. This might be relevant if the entity is 'Throwing'
@@ -126,14 +140,12 @@ class ExceptionAnalyzer {
   ExceptionInfo analyze(const Stmt *Stmt);
 
 private:
-  ExceptionInfo
-  throwsException(const FunctionDecl *Func,
-                  const ExceptionInfo::Throwables &Caught,
-                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
-  ExceptionInfo
-  throwsException(const Stmt *St, const ExceptionInfo::Throwables &Caught,
-                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
-
+  ExceptionInfo throwsException(const FunctionDecl *Func,
+                                const ExceptionInfo::Throwables &Caught,
+                                CallStack &CallStack, SourceLocation CallLoc);
+  ExceptionInfo throwsException(const Stmt *St,
+                                const ExceptionInfo::Throwables &Caught,
+                                CallStack &CallStack);
   ExceptionInfo analyzeImpl(const FunctionDecl *Func);
   ExceptionInfo analyzeImpl(const Stmt *Stmt);
 

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 36a41b4bdf42d..afaa04e4083c7 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -187,6 +187,10 @@ Changes in existing checks
   false positives on deleted constructors that cannot be used to construct
   objects, even if they have public or protected access.
 
+- Improved :doc:`bugprone-exception-escape
+  <clang-tidy/checks/bugprone/exception-escape>` check to print stack trace
+  of a potentially escaped exception.
+
 - Added an option to :doc:`bugprone-multi-level-implicit-pointer-conversion
   <clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion>` to
   choose whether to enable the check in C code or not.

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
index aff13d19fd209..cd5f019ff9f6e 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \
+// RUN: %check_clang_tidy -std=c++20-or-later %s bugprone-exception-escape %t 
-- \
 // RUN:     -- -fexceptions -Wno-error=return-type
 
 namespace std {
@@ -221,6 +221,8 @@ Task<int> c_ShouldDiag(const int a, const int b) noexcept {
 
   co_return a / b;
 }
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:11: note: frame #1: function 'c_ShouldDiag' 
calls function '~Evil'
 
 Task<int, true> d_ShouldNotDiag(const int a, const int b) {
   co_return a / b;
@@ -230,6 +232,9 @@ Task<int, true> d_ShouldDiag(const int a, const int b) 
noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: an exception may be thrown in 
function 'd_ShouldDiag' which should not throw exceptions
   co_return a / b;
 }
+// CHECK-MESSAGES: :54:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :109:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-6]]:17: note: frame #2: function 'd_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<int, false, true> e_ShouldNotDiag(const int a, const int b) {
   co_return a / b;
@@ -239,6 +244,8 @@ Task<int, false, true> e_ShouldDiag(const int a, const int 
b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: an exception may be thrown in 
function 'e_ShouldDiag' which should not throw exceptions
   co_return a / b;
 }
+// CHECK-MESSAGES: :100:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: frame #1: function 'e_ShouldDiag' 
calls function 'Promise' here
 
 Task<int, false, false, true> f_ShouldNotDiag(const int a, const int b) {
   co_return a / b;
@@ -248,6 +255,8 @@ Task<int, false, false, true> f_ShouldDiag(const int a, 
const int b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: an exception may be thrown in 
function 'f_ShouldDiag' which should not throw exceptions
   co_return a / b;
 }
+// CHECK-MESSAGES: :114:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: frame #1: function 'f_ShouldDiag' 
calls function 'initial_suspend' here
 
 Task<int, false, false, false, true> g_ShouldNotDiag(const int a, const int b) 
{
   co_return a / b;
@@ -258,6 +267,8 @@ Task<int, false, false, false, true> g_ShouldDiag(const int 
a,
   // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: an exception may be thrown in 
function 'g_ShouldDiag' which should not throw exceptions
   co_return a / b;
 }
+// CHECK-MESSAGES: :106:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:38: note: frame #1: function 'g_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<int, false, false, false, false, true> h_ShouldNotDiag(const int a,
                                                             const int b) {
@@ -269,6 +280,8 @@ Task<int, false, false, false, false, true> 
h_ShouldDiag(const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:45: warning: an exception may be thrown in 
function 'h_ShouldDiag' which should not throw exceptions
   co_return a / b;
 }
+// CHECK-MESSAGES: :133:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-6]]:45: note: frame #1: function 'h_ShouldDiag' 
calls function 'unhandled_exception' here
 
 Task<int, false, false, false, false, false, true>
 i_ShouldNotDiag(const int a, const int b) {
@@ -296,6 +309,7 @@ j_ShouldDiag(const int a, const int b) noexcept {
 
   co_return a / b;
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'j_ShouldDiag' here
 
 } // namespace coreturn
 
@@ -329,6 +343,8 @@ Task<int> c_ShouldDiag(const int a, const int b) noexcept {
 
   co_yield a / b;
 }
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:11: note: frame #1: function 'c_ShouldDiag' 
calls function '~Evil'
 
 Task<int, true> d_ShouldNotDiag(const int a, const int b) {
   co_yield a / b;
@@ -338,6 +354,9 @@ Task<int, true> d_ShouldDiag(const int a, const int b) 
noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: an exception may be thrown in 
function 'd_ShouldDiag' which should not throw exceptions
   co_yield a / b;
 }
+// CHECK-MESSAGES: :54:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :109:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-6]]:17: note: frame #2: function 'd_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<int, false, true> e_ShouldNotDiag(const int a, const int b) {
   co_yield a / b;
@@ -347,6 +366,8 @@ Task<int, false, true> e_ShouldDiag(const int a, const int 
b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: an exception may be thrown in 
function 'e_ShouldDiag' which should not throw exceptions
   co_yield a / b;
 }
+// CHECK-MESSAGES: :100:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: frame #1: function 'e_ShouldDiag' 
calls function 'Promise' here
 
 Task<int, false, false, true> f_ShouldNotDiag(const int a, const int b) {
   co_yield a / b;
@@ -356,6 +377,8 @@ Task<int, false, false, true> f_ShouldDiag(const int a, 
const int b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: an exception may be thrown in 
function 'f_ShouldDiag' which should not throw exceptions
   co_yield a / b;
 }
+// CHECK-MESSAGES: :114:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: frame #1: function 'f_ShouldDiag' 
calls function 'initial_suspend' here
 
 Task<int, false, false, false, true> g_ShouldNotDiag(const int a, const int b) 
{
   co_yield a / b;
@@ -366,6 +389,8 @@ Task<int, false, false, false, true> g_ShouldDiag(const int 
a,
   // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: an exception may be thrown in 
function 'g_ShouldDiag' which should not throw exceptions
   co_yield a / b;
 }
+// CHECK-MESSAGES: :106:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:38: note: frame #1: function 'g_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<int, false, false, false, false, true> h_ShouldNotDiag(const int a,
                                                             const int b) {
@@ -377,6 +402,8 @@ Task<int, false, false, false, false, true> 
h_ShouldDiag(const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:45: warning: an exception may be thrown in 
function 'h_ShouldDiag' which should not throw exceptions
   co_yield a / b;
 }
+// CHECK-MESSAGES: :133:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-6]]:45: note: frame #1: function 'h_ShouldDiag' 
calls function 'unhandled_exception' here
 
 Task<int, false, false, false, false, false, true>
 i_ShouldNotDiag(const int a, const int b) {
@@ -404,6 +431,7 @@ j_ShouldDiag(const int a, const int b) noexcept {
 
   co_yield a / b;
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'j_ShouldDiag' here
 
 } // namespace coyield
 
@@ -437,6 +465,8 @@ Task<void> c_ShouldDiag(const int a, const int b) noexcept {
 
   co_await returnOne();
 }
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:12: note: frame #1: function 'c_ShouldDiag' 
calls function '~Evil'
 
 Task<void, true> d_ShouldNotDiag(const int a, const int b) {
   co_await returnOne();
@@ -446,6 +476,9 @@ Task<void, true> d_ShouldDiag(const int a, const int b) 
noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: an exception may be thrown in 
function 'd_ShouldDiag' which should not throw exceptions
   co_await returnOne();
 }
+// CHECK-MESSAGES: :81:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :158:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-6]]:18: note: frame #2: function 'd_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<void, false, true> e_ShouldNotDiag(const int a, const int b) {
   co_await returnOne();
@@ -455,6 +488,8 @@ Task<void, false, true> e_ShouldDiag(const int a, const int 
b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: an exception may be thrown in 
function 'e_ShouldDiag' which should not throw exceptions
   co_await returnOne();
 }
+// CHECK-MESSAGES: :149:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: frame #1: function 'e_ShouldDiag' 
calls function 'Promise' here
 
 Task<void, false, false, true> f_ShouldNotDiag(const int a, const int b) {
   co_await returnOne();
@@ -464,6 +499,8 @@ Task<void, false, false, true> f_ShouldDiag(const int a, 
const int b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: an exception may be thrown in 
function 'f_ShouldDiag' which should not throw exceptions
   co_await returnOne();
 }
+// CHECK-MESSAGES: :163:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:32: note: frame #1: function 'f_ShouldDiag' 
calls function 'initial_suspend' here
 
 Task<void, false, false, false, true> g_ShouldNotDiag(const int a,
                                                       const int b) {
@@ -475,6 +512,8 @@ Task<void, false, false, false, true> g_ShouldDiag(const 
int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: an exception may be thrown in 
function 'g_ShouldDiag' which should not throw exceptions
   co_await returnOne();
 }
+// CHECK-MESSAGES: :155:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:39: note: frame #1: function 'g_ShouldDiag' 
calls function 'get_return_object' here
 
 Task<void, false, false, false, false, true> h_ShouldNotDiag(const int a,
                                                              const int b) {
@@ -486,6 +525,8 @@ h_ShouldDiag(const int a, const int b) noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in 
function 'h_ShouldDiag' which should not throw exceptions
   co_await returnOne();
 }
+// CHECK-MESSAGES: :175:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-5]]:1: note: frame #1: function 'h_ShouldDiag' 
calls function 'unhandled_exception' here
 
 Task<int, false, false, false, false, false, true>
 i_ShouldNotDiag(const int a, const int b) {
@@ -511,6 +552,7 @@ j_ShouldDiag(const int a, const int b) noexcept {
   if (b == 0)
     throw b;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'j_ShouldDiag' here
 
 } // namespace coawait
 
@@ -549,6 +591,8 @@ const auto c_ShouldDiag = [](const int a, const int b) 
noexcept -> Task<int> {
 
   co_return a / b;
 };
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:27: note: frame #1: function 'operator()' 
calls function '~Evil'
 
 const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<int, true> {
   co_return a / b;
@@ -559,6 +603,9 @@ const auto d_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_return a / b;
 };
+// CHECK-MESSAGES: :54:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :109:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-7]]:27: note: frame #2: function 'operator()' 
calls function 'get_return_object' here
 
 const auto e_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<int, false, true> {
@@ -570,6 +617,8 @@ const auto e_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_return a / b;
 };
+// CHECK-MESSAGES: :100:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-6]]:27: note: frame #1: function 'operator()' 
calls function 'Promise' here
 
 const auto f_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<int, false, false, true> {
@@ -581,6 +630,8 @@ const auto f_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_return a / b;
 };
+// CHECK-MESSAGES: :114:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:5: note: frame #1: function 'operator()' calls 
function 'initial_suspend' here
 
 const auto g_ShouldNotDiag =
     [](const int a, const int b) -> Task<int, false, false, false, true> {
@@ -593,6 +644,8 @@ const auto g_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_return a / b;
 };
+// CHECK-MESSAGES: :106:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'get_return_object' here
 
 const auto h_ShouldNotDiag =
     [](const int a,
@@ -606,6 +659,8 @@ const auto h_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_return a / b;
 };
+// CHECK-MESSAGES: :133:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'unhandled_exception' here
 
 const auto i_ShouldNotDiag =
     [](const int a,
@@ -637,6 +692,7 @@ const auto j_ShouldDiag =
 
   co_return a / b;
 };
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'operator()' here
 
 } // namespace coreturn
 
@@ -671,6 +727,8 @@ const auto c_ShouldDiag = [](const int a, const int b) 
noexcept -> Task<int> {
 
   co_yield a / b;
 };
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:27: note: frame #1: function 'operator()' 
calls function '~Evil'
 
 const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<int, true> {
   co_yield a / b;
@@ -681,6 +739,9 @@ const auto d_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_yield a / b;
 };
+// CHECK-MESSAGES: :54:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :109:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-7]]:27: note: frame #2: function 'operator()' 
calls function 'get_return_object' here
 
 const auto e_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<int, false, true> {
@@ -692,6 +753,8 @@ const auto e_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_yield a / b;
 };
+// CHECK-MESSAGES: :100:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-6]]:27: note: frame #1: function 'operator()' 
calls function 'Promise' here
 
 const auto f_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<int, false, false, true> {
@@ -703,6 +766,8 @@ const auto f_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_yield a / b;
 };
+// CHECK-MESSAGES: :114:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:5: note: frame #1: function 'operator()' calls 
function 'initial_suspend' here
 
 const auto g_ShouldNotDiag =
     [](const int a, const int b) -> Task<int, false, false, false, true> {
@@ -715,6 +780,8 @@ const auto g_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_yield a / b;
 };
+// CHECK-MESSAGES: :106:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'get_return_object' here
 
 const auto h_ShouldNotDiag =
     [](const int a,
@@ -728,6 +795,8 @@ const auto h_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_yield a / b;
 };
+// CHECK-MESSAGES: :133:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'unhandled_exception' here
 
 const auto i_ShouldNotDiag =
     [](const int a,
@@ -759,6 +828,7 @@ const auto j_ShouldDiag =
 
   co_yield a / b;
 };
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'operator()' here
 
 } // namespace coyield
 
@@ -793,6 +863,8 @@ const auto c_ShouldDiag = [](const int a, const int b) 
noexcept -> Task<void> {
 
   co_await returnOne();
 };
+// CHECK-MESSAGES: :186:5: note: frame #0: unhandled exception of type 'int' 
may be thrown in function '~Evil' here
+// CHECK-MESSAGES: :[[@LINE-8]]:27: note: frame #1: function 'operator()' 
calls function '~Evil'
 
 const auto d_ShouldNotDiag = [](const int a, const int b) -> Task<void, true> {
   co_await returnOne();
@@ -803,6 +875,9 @@ const auto d_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_await returnOne();
 };
+// CHECK-MESSAGES: :81:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Task' here
+// CHECK-MESSAGES: :158:12: note: frame #1: function 'get_return_object' calls 
function 'Task' here
+// CHECK-MESSAGES: :[[@LINE-7]]:27: note: frame #2: function 'operator()' 
calls function 'get_return_object' here
 
 const auto e_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<void, false, true> {
@@ -814,6 +889,8 @@ const auto e_ShouldDiag = [](const int a,
   // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_await returnOne();
 };
+// CHECK-MESSAGES: :149:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'Promise' here
+// CHECK-MESSAGES: :[[@LINE-6]]:27: note: frame #1: function 'operator()' 
calls function 'Promise' here
 
 const auto f_ShouldNotDiag = [](const int a,
                                 const int b) -> Task<void, false, false, true> 
{
@@ -825,6 +902,8 @@ const auto f_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_await returnOne();
 };
+// CHECK-MESSAGES: :163:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'initial_suspend' here
+// CHECK-MESSAGES: :[[@LINE-5]]:5: note: frame #1: function 'operator()' calls 
function 'initial_suspend' here
 
 const auto g_ShouldNotDiag =
     [](const int a, const int b) -> Task<void, false, false, false, true> {
@@ -837,6 +916,8 @@ const auto g_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_await returnOne();
 };
+// CHECK-MESSAGES: :155:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'get_return_object' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'get_return_object' here
 
 const auto h_ShouldNotDiag =
     [](const int a,
@@ -850,6 +931,8 @@ const auto h_ShouldDiag =
   // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in 
function 'operator()' which should not throw exceptions
   co_await returnOne();
 };
+// CHECK-MESSAGES: :175:7: note: frame #0: unhandled exception of type 'int' 
may be thrown in function 'unhandled_exception' here
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #1: function 'operator()' calls 
function 'unhandled_exception' here
 
 const auto i_ShouldNotDiag =
     [](const int a,
@@ -879,6 +962,7 @@ const auto j_ShouldDiag =
   if (b == 0)
     throw b;
 };
+// CHECK-MESSAGES: :[[@LINE-2]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'operator()' here
 
 } // namespace coawait
 

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
index 6f961a247b9d2..b79ce30ac630c 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 %s 
bugprone-exception-escape %t -- \
+// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-exception-escape %t 
-- \
 // RUN:     -- -fexceptions
 
 void rethrower() {
@@ -22,6 +22,7 @@ int throwsAndCallsRethrower() noexcept {
     }
     return 1;
 }
+// CHECK-MESSAGES: :[[@LINE-6]]:9: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throwsAndCallsRethrower' here
 
 int throwsAndCallsCallsRethrower() noexcept {
 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'throwsAndCallsCallsRethrower' which should not throw exceptions
@@ -32,6 +33,7 @@ int throwsAndCallsCallsRethrower() noexcept {
     }
     return 1;
 }
+// CHECK-MESSAGES: :[[@LINE-6]]:9: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throwsAndCallsCallsRethrower' here
 
 void rethrowerNoexcept() noexcept {
     throw;

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
index 4a0113b8be3b3..ba139937c0757 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-throw.cpp
@@ -4,6 +4,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
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throwing_throw_nothing' here
 
 void explicit_int_thrower() throw(int);
 
@@ -15,11 +16,15 @@ void indirect_implicit() throw() {
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'indirect_implicit' which should not throw exceptions
   implicit_int_thrower();
 }
+// CHECK-MESSAGES: :[[@LINE-7]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'implicit_int_thrower' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 
'indirect_implicit' calls function 'implicit_int_thrower' here
 
 void indirect_explicit() throw() {
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'indirect_explicit' which should not throw exceptions
   explicit_int_thrower();
 }
+// CHECK-MESSAGES: :[[@LINE-17]]:29: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'explicit_int_thrower' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 
'indirect_explicit' calls function 'explicit_int_thrower' here
 
 struct super_throws {
   super_throws() throw(int) { throw 42; }
@@ -29,3 +34,43 @@ 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-7]]:31: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'super_throws' here
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: frame #1: function 'sub_throws' 
calls function 'super_throws' here
+
+struct base_throwing_ctor {
+  base_throwing_ctor() throw(int) { throw 123; }
+};
+
+struct intermediate_ctor : base_throwing_ctor {
+  intermediate_ctor() throw(int) : base_throwing_ctor() {}
+};
+
+struct final_no_throw : intermediate_ctor {
+  final_no_throw() throw() : intermediate_ctor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in 
function 'final_no_throw' which should not throw exceptions
+};
+// CHECK-MESSAGES: :[[@LINE-11]]:37: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'base_throwing_ctor' here
+// CHECK-MESSAGES: :[[@LINE-8]]:36: note: frame #1: function 
'intermediate_ctor' calls function 'base_throwing_ctor' here
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: frame #2: function 'final_no_throw' 
calls function 'intermediate_ctor' here
+
+// Member initializer with call stack
+struct member_thrower {
+  member_thrower() throw(double) { throw 3.14; }
+};
+
+struct has_throwing_member {
+  member_thrower member;
+  has_throwing_member() throw() : member() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in 
function 'has_throwing_member' which should not throw exceptions
+};
+// CHECK-MESSAGES: :[[@LINE-8]]:36: note: frame #0: unhandled exception of 
type 'double' may be thrown in function 'member_thrower' here
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: frame #1: function 
'has_throwing_member' calls function 'member_thrower' here
+
+void multi_spec_thrower() throw(int, double, const char*);
+
+void calls_multi_spec() throw() {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'calls_multi_spec' which should not throw exceptions
+  multi_spec_thrower();
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:27: note: frame #0: unhandled exception of 
type '{{(int|double|const char \*)}}' may be thrown in function 
'multi_spec_thrower' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 'calls_multi_spec' 
calls function 'multi_spec_thrower' here

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
index aae957dd7e090..b10bd1d482867 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -12,6 +12,7 @@ struct throwing_destructor {
     throw 1;
   }
 };
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function '~throwing_destructor' here
 
 struct throwing_move_constructor {
   throwing_move_constructor(throwing_move_constructor&&) {
@@ -19,6 +20,7 @@ struct throwing_move_constructor {
     throw 1;
   }
 };
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throwing_move_constructor' here
 
 struct throwing_move_assignment {
   throwing_move_assignment& operator=(throwing_move_assignment&&) {
@@ -26,11 +28,13 @@ struct throwing_move_assignment {
     throw 1;
   }
 };
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'operator=' here
 
 void throwing_noexcept() noexcept {
     // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'throwing_noexcept' which should not throw exceptions
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throwing_noexcept' here
 
 void throw_and_catch() noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'throw_and_catch' which should not throw exceptions
@@ -48,6 +52,7 @@ void throw_and_catch_some(int n) noexcept {
   } catch(int &) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'double' may be thrown in function 'throw_and_catch_some' here
 
 void throw_and_catch_each(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'throw_and_catch_each' which should not throw exceptions
@@ -76,6 +81,7 @@ void throw_and_rethrow() noexcept {
     throw;
   }
 }
+// CHECK-MESSAGES: :[[@LINE-5]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throw_and_rethrow' here
 
 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
@@ -85,6 +91,7 @@ void throw_catch_throw() noexcept {
     throw 2;
   }
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'throw_catch_throw' here
 
 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
@@ -96,6 +103,7 @@ void throw_catch_rethrow_the_rest(int n) noexcept {
     throw;
   }
 }
+// CHECK-MESSAGES: :[[@LINE-6]]:5: note: frame #0: unhandled exception of type 
'double' may be thrown in function 'throw_catch_rethrow_the_rest' here
 
 void throw_catch_pointer_c() noexcept {
   try {
@@ -126,6 +134,7 @@ void throw_catch_multi_ptr_1() noexcept {
   } catch (const char **) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'char **' may be thrown in function 'throw_catch_multi_ptr_1' here
 
 void throw_catch_multi_ptr_2() noexcept {
   try {
@@ -171,6 +180,7 @@ void throw_c_catch_pointer() noexcept {
     throw p;
   } catch(int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'const int *' may be thrown in function 'throw_c_catch_pointer' here
 
 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
@@ -180,6 +190,7 @@ void throw_c_catch_pointer_v() noexcept {
     throw p;
   } catch(volatile int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'const int *' may be thrown in function 'throw_c_catch_pointer_v' here
 
 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
@@ -189,6 +200,7 @@ void throw_v_catch_pointer() noexcept {
     throw p;
   } catch(int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'volatile int *' may be thrown in function 'throw_v_catch_pointer' here
 
 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
@@ -198,6 +210,7 @@ void throw_v_catch_pointer_c() noexcept {
     throw p;
   } catch(const int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'volatile int *' may be thrown in function 'throw_v_catch_pointer_c' here
 
 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
@@ -207,6 +220,7 @@ void throw_cv_catch_pointer_c() noexcept {
     throw p;
   } catch(const int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'const volatile int *' may be thrown in function 'throw_cv_catch_pointer_c' here
 
 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
@@ -216,6 +230,7 @@ void throw_cv_catch_pointer_v() noexcept {
     throw p;
   } catch(volatile int *) {}
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #0: unhandled exception of type 
'const volatile int *' may be thrown in function 'throw_cv_catch_pointer_v' here
 
 class base {};
 class derived: public base {};
@@ -263,6 +278,7 @@ void throw_derived_catch_base_ptr() noexcept {
   } catch(base *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'const derived *' may be thrown in function 'throw_derived_catch_base_ptr' here
 
 class A {};
 class B : A {};
@@ -286,6 +302,7 @@ void throw_derived_catch_base_private() noexcept {
   } catch(A) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'B' may be thrown in function 'throw_derived_catch_base_private' here
 
 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
@@ -295,6 +312,7 @@ void throw_derived_catch_base_private_ptr() noexcept {
   } catch(A *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'B *' may be thrown in function 'throw_derived_catch_base_private_ptr' here
 
 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
@@ -304,6 +322,7 @@ void throw_derived_catch_base_protected() noexcept {
   } catch(A) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'C' may be thrown in function 'throw_derived_catch_base_protected' here
 
 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
@@ -313,6 +332,7 @@ void throw_derived_catch_base_protected_ptr() noexcept {
   } catch(A *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'C *' may be thrown in function 'throw_derived_catch_base_protected_ptr' here
 
 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
@@ -322,6 +342,7 @@ void throw_derived_catch_base_ambiguous() noexcept {
   } catch(A) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'E' may be thrown in function 'throw_derived_catch_base_ambiguous' here
 
 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
@@ -331,6 +352,7 @@ void throw_derived_catch_base_ambiguous_ptr() noexcept {
   } catch(A) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'E' may be thrown in function 'throw_derived_catch_base_ambiguous_ptr' here
 
 void throw_alias_catch_original() noexcept {
   using alias = int;
@@ -352,6 +374,7 @@ void throw_alias_catch_original_warn() noexcept {
   } catch (int) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'float' may be thrown in function 'throw_alias_catch_original_warn' here
 
 void throw_original_catch_alias() noexcept {
   using alias = char;
@@ -373,6 +396,7 @@ void throw_original_catch_alias_warn() noexcept {
   } catch (volatile const alias *const *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'char **' may be thrown in function 'throw_original_catch_alias_warn' here
 
 void throw_original_catch_alias_2() noexcept {
   using alias = const char *const;
@@ -433,6 +457,7 @@ void throw_basefn_catch_derivedfn() noexcept {
   } catch(void(derivedMember::*)()) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'void (baseMember::*)()' may be thrown in function 
'throw_basefn_catch_derivedfn' here
 
 void throw_basefn_catch_basefn() noexcept {
   try {
@@ -449,6 +474,7 @@ void throw_basem_catch_basem_throw() noexcept {
   } catch(const int* baseMember::* const *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int *baseMember::**' may be thrown in function 'throw_basem_catch_basem_throw' 
here
 
 void throw_basem_catch_basem() noexcept {
   try {
@@ -466,6 +492,7 @@ void throw_basem_catch_derivedm() noexcept {
   } catch(const int* const derivedMember::* const *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int *baseMember::**' may be thrown in function 'throw_basem_catch_derivedm' 
here
 
 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
@@ -475,6 +502,7 @@ void throw_derivedm_catch_basem() noexcept {
   } catch(const int* const baseMember::* const *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'int *derivedMember::**' may be thrown in function 'throw_derivedm_catch_basem' 
here
 
 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
@@ -486,6 +514,7 @@ void throw_original_catch_alias_2_warn() noexcept {
   } catch (volatile alias *) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #0: unhandled exception of type 
'char **' may be thrown in function 'throw_original_catch_alias_2_warn' here
 
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'try_nested_try' which should not throw exceptions
@@ -510,6 +539,7 @@ void bad_try_nested_try(int n) noexcept {
   } catch(double &) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-8]]:12: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'bad_try_nested_try' here
 
 void try_nested_catch() noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'try_nested_catch' which should not throw exceptions
@@ -547,6 +577,7 @@ void bad_catch_nested_try() noexcept {
   } catch(double &) {
   }
 }
+// CHECK-MESSAGES: :[[@LINE-6]]:7: note: frame #0: unhandled exception of type 
'double' may be thrown in function 'bad_catch_nested_try' here
 
 void implicit_int_thrower() {
   throw 1;
@@ -560,11 +591,15 @@ void indirect_implicit() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'indirect_implicit' which should not throw exceptions
   implicit_int_thrower();
 }
+// CHECK-MESSAGES: :[[@LINE-11]]:3: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'implicit_int_thrower' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 
'indirect_implicit' calls function 'implicit_int_thrower' here
 
 void indirect_explicit() noexcept {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'indirect_explicit' which should not throw exceptions
   explicit_int_thrower();
 }
+// CHECK-MESSAGES: :[[@LINE-14]]:3: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'explicit_int_thrower' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 
'indirect_explicit' calls function 'explicit_int_thrower' here
 
 void indirect_catch() noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'indirect_catch' which should not throw exceptions
@@ -585,16 +620,19 @@ void swap(int&, int&) {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'swap' which should not throw exceptions
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'swap' here
 
 void iter_swap(int&, int&) {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'iter_swap' which should not throw exceptions
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'iter_swap' here
 
 void iter_move(int&) {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'iter_move' which should not throw exceptions
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'iter_move' here
 
 namespace std {
 class bad_alloc {};
@@ -613,11 +651,14 @@ void enabled1() {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'enabled1' which should not throw exceptions
   throw 1;
 }
+// CHECK-MESSAGES: :[[@LINE-2]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'enabled1' here
 
 void enabled2() {
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'enabled2' which should not throw exceptions
   enabled1();
 }
+// CHECK-MESSAGES: :[[@LINE-8]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'enabled1' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 'enabled2' calls 
function 'enabled1' here
 
 void enabled3() {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'enabled3' which should not throw exceptions
@@ -627,6 +668,64 @@ void enabled3() {
   }
 }
 
+void deep_level3_thrower() {
+  throw 42;
+}
+
+void deep_level2_caller() {
+  deep_level3_thrower();
+}
+
+void deep_level1_caller() {
+  deep_level2_caller();
+}
+
+void deep_stack_test() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'deep_stack_test' which should not throw exceptions
+  deep_level1_caller();
+}
+// CHECK-MESSAGES: :[[@LINE-15]]:3: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'deep_level3_thrower' here
+// CHECK-MESSAGES: :[[@LINE-12]]:3: note: frame #1: function 
'deep_level2_caller' calls function 'deep_level3_thrower' here
+// CHECK-MESSAGES: :[[@LINE-9]]:3: note: frame #2: function 
'deep_level1_caller' calls function 'deep_level2_caller' here
+// CHECK-MESSAGES: :[[@LINE-5]]:3: note: frame #3: function 'deep_stack_test' 
calls function 'deep_level1_caller' here
+
+// Template function call stack
+template<typename T>
+void template_thrower(T t) {
+  throw T{};
+}
+
+template<typename T>
+void template_caller(T t) {
+  template_thrower<T>(t);
+}
+
+void template_stack_test() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'template_stack_test' which should not throw exceptions
+  template_caller<char>('a');
+}
+// CHECK-MESSAGES: :[[@LINE-12]]:3: note: frame #0: unhandled exception of 
type 'char' may be thrown in function 'template_thrower<char>' here
+// CHECK-MESSAGES: :[[@LINE-8]]:3: note: frame #1: function 
'template_caller<char>' calls function 'template_thrower<char>' here
+// CHECK-MESSAGES: :[[@LINE-4]]:3: note: frame #2: function 
'template_stack_test' calls function 'template_caller<char>' here
+
+// template function without instantiation is not warned
+template<typename T>
+void template_function_stack_test(T t) noexcept {
+  template_thrower(t);
+}
+
+void multiple_exception_types(int choice) noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'multiple_exception_types' which should not throw exceptions
+  if (choice == 1) {
+    throw 42;
+  } else if (choice == 2) {
+    throw 3.14;
+  } else if (choice == 3) {
+    throw "string";
+  }
+}
+// CHECK-MESSAGES: note: frame #0: unhandled exception of type 
'{{(int|double|const char \*)}}' may be thrown in function 
'multiple_exception_types' here
+
 class ignored1 {};
 class ignored2 {};
 
@@ -649,6 +748,7 @@ void this_counts(int n) noexcept {
   if (n) throw 1;
   throw ignored1();
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:10: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'this_counts' here
 
 void thrower(int n) {
   throw n;
@@ -660,6 +760,8 @@ int directly_recursive(int n) noexcept {
     thrower(n);
   return directly_recursive(n);
 }
+// CHECK-MESSAGES: :[[@LINE-9]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'thrower' here
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #1: function 
'directly_recursive' calls function 'thrower' here
 
 int indirectly_recursive(int n) noexcept;
 
@@ -674,6 +776,8 @@ int indirectly_recursive(int n) noexcept {
     thrower(n);
   return recursion_helper(n);
 }
+// CHECK-MESSAGES: :[[@LINE-25]]:3: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'thrower' here
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: frame #1: function 
'indirectly_recursive' calls function 'thrower' here
 
 struct super_throws {
   super_throws() noexcept(false) { throw 42; }
@@ -683,6 +787,8 @@ 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-7]]:36: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'super_throws' here
+// CHECK-MESSAGES: :[[@LINE-4]]:27: note: frame #1: function 'sub_throws' 
calls function 'super_throws' here
 
 struct init_member_throws {
   super_throws s;
@@ -690,6 +796,8 @@ struct init_member_throws {
   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-16]]:36: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'super_throws' here
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: frame #1: function 
'init_member_throws' calls function 'super_throws' here
 
 struct implicit_init_member_throws {
   super_throws s;
@@ -697,6 +805,8 @@ struct implicit_init_member_throws {
   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-25]]:36: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'super_throws' here
+// CHECK-MESSAGES: :[[@LINE-4]]:3: note: frame #1: function 
'implicit_init_member_throws' calls function 'super_throws'
 
 struct init {
   explicit init(int, int) noexcept(false) { throw 42; }
@@ -708,18 +818,22 @@ struct in_class_init_throws {
   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-9]]:45: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'init' here
+// CHECK-MESSAGES: :[[@LINE-6]]:9: note: frame #1: function 
'in_class_init_throws' calls function 'init' here
 
 int main() {
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'main' which should not throw exceptions
   throw 1;
   return 0;
 }
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #0: unhandled exception of type 
'int' may be thrown in function 'main' here
 
 // The following function all incorrectly throw exceptions, *but* calling them
 // should not yield a warning because they are marked as noexcept.
 
 void test_basic_no_throw() noexcept { throw 42; }
 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in 
function 'test_basic_no_throw' which should not throw exceptions
+// CHECK-MESSAGES: :[[@LINE-2]]:39: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'test_basic_no_throw' here
 
 void test_basic_throw() noexcept(false) { throw 42; }
 
@@ -732,6 +846,8 @@ void calls_non_and_throwing() noexcept {
   test_basic_no_throw();
   test_basic_throw();
 }
+// CHECK-MESSAGES: :[[@LINE-11]]:43: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'test_basic_throw' here
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: frame #1: function 
'calls_non_and_throwing' calls function 'test_basic_throw' here
 
 namespace PR55143 { namespace PR40583 {
 
@@ -749,11 +865,14 @@ struct test_implicit_throw {
     test_implicit_throw(const test_implicit_throw&) { throw 42; }
     test_implicit_throw(test_implicit_throw&&) { throw 42; }
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function 'test_implicit_throw' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE-2]]:50: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'test_implicit_throw' here
     test_implicit_throw& operator=(const test_implicit_throw&) { throw 42; }
     test_implicit_throw& operator=(test_implicit_throw&&) { throw 42; }
     // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: an exception may be thrown in 
function 'operator=' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE-2]]:61: note: frame #0: unhandled exception of 
type 'int' may be thrown in function 'operator='
     ~test_implicit_throw() { throw 42; }
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in 
function '~test_implicit_throw' which should not throw exceptions
+    // CHECK-MESSAGES: :[[@LINE-2]]:30: note: frame #0: unhandled exception of 
type 'int' may be thrown in function '~test_implicit_throw' here
 };
 
 }}


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

Reply via email to