https://github.com/zeyi2 created 
https://github.com/llvm/llvm-project/pull/187971

None

>From c2add4b0e75cacf95491dcd52078f38c62daec98 Mon Sep 17 00:00:00 2001
From: mtx <[email protected]>
Date: Mon, 23 Mar 2026 12:45:53 +0800
Subject: [PATCH] [clang-tidy] Add frames for bugprone-exception-escape options

---
 .../bugprone/ExceptionEscapeCheck.cpp         | 39 ++++++++++++-------
 .../clang-tidy/utils/ExceptionAnalyzer.cpp    | 14 +++++--
 .../clang-tidy/utils/ExceptionAnalyzer.h      | 14 +++++++
 ...ions-without-specification-as-throwing.cpp | 31 ++++++++++++++-
 4 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
index 41e0cdaf6ee61..07b7eb9a8ba94 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -146,24 +146,35 @@ void ExceptionEscapeCheck::check(const 
MatchFinder::MatchResult &Result) {
                                    "%0 which should not throw exceptions")
       << MatchedDecl;
 
-  if (Info.getExceptions().empty())
+  const utils::ExceptionAnalyzer::ExceptionInfo::Throwables &Exceptions =
+      Info.getExceptions();
+  const utils::ExceptionAnalyzer::ExceptionInfo::ThrowInfo *TI = nullptr;
+  if (!Exceptions.empty())
+    TI = &Exceptions.begin()->second;
+  else if (Info.containsUnknownElements())
+    TI = &Info.getUnknownThrowInfo();
+
+  if (!TI || TI->Loc.isInvalid())
     return;
 
-  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;
+  if (!Exceptions.empty()) {
+    const auto &[ThrowType, ThrowInfo] = *Exceptions.begin();
+    diag(ThrowInfo.Loc,
+         "frame #0: unhandled exception of type %0 may be thrown in function "
+         "%1 here",
+         DiagnosticIDs::Note)
+        << QualType(ThrowType, 0U) << ThrowInfo.Stack.back().first;
+  } else {
+    diag(TI->Loc,
+         "frame #0: an exception of unknown type may be thrown in function %0 "
+         "here",
+         DiagnosticIDs::Note)
+        << TI->Stack.back().first;
+  }
 
   size_t FrameNo = 1;
-  for (auto CurrIt = ++Stack.rbegin(), PrevIt = Stack.rbegin();
-       CurrIt != Stack.rend(); ++CurrIt, ++PrevIt) {
+  for (auto CurrIt = ++TI->Stack.rbegin(), PrevIt = TI->Stack.rbegin();
+       CurrIt != TI->Stack.rend(); ++CurrIt, ++PrevIt) {
     const FunctionDecl *CurrFunction = CurrIt->first;
     const FunctionDecl *PrevFunction = PrevIt->first;
     const SourceLocation PrevLocation = PrevIt->second;
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp 
b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 60dade82e6155..9bffa5d1cf441 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -39,6 +39,8 @@ ExceptionAnalyzer::ExceptionInfo 
&ExceptionAnalyzer::ExceptionInfo::merge(
     Behaviour = State::Unknown;
 
   ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
+  if (!ThrowsUnknown && Other.ThrowsUnknown)
+    UnknownThrowInfo = Other.UnknownThrowInfo;
   ThrowsUnknown = ThrowsUnknown || Other.ThrowsUnknown;
   ThrownExceptions.insert_range(Other.ThrownExceptions);
   return *this;
@@ -452,6 +454,7 @@ void ExceptionAnalyzer::ExceptionInfo::clear() {
   Behaviour = State::NotThrowing;
   ContainsUnknown = false;
   ThrowsUnknown = false;
+  UnknownThrowInfo = {};
   ThrownExceptions.clear();
 }
 
@@ -489,7 +492,9 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
     // are not explicitly non-throwing and no throw was discovered.
     if (AssumeUnannotatedFunctionsAsThrowing &&
         Result.getBehaviour() == State::NotThrowing && canThrow(Func)) {
-      Result.registerUnknownException();
+      CallStack.insert({Func, CallLoc});
+      Result.registerUnknownException({Func->getLocation(), CallStack});
+      CallStack.erase(Func);
     }
     return Result;
   }
@@ -507,8 +512,11 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
   }
 
   if (AssumeMissingDefinitionsFunctionsAsThrowing &&
-      Result.getBehaviour() == State::Unknown)
-    Result.registerUnknownException();
+      Result.getBehaviour() == State::Unknown) {
+    CallStack.insert({Func, CallLoc});
+    Result.registerUnknownException({Func->getLocation(), CallStack});
+    CallStack.erase(Func);
+  }
 
   return Result;
 }
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h 
b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
index 08479ef58240a..c433cdb5d5193 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -117,6 +117,16 @@ class ExceptionAnalyzer {
       ContainsUnknown = true;
     }
 
+    /// Mark the entity as throwing due to an unknown cause and record Info
+    /// as the location for diagnostic notes.
+    void registerUnknownException(ThrowInfo Info) {
+      registerUnknownException();
+      UnknownThrowInfo = std::move(Info);
+    }
+
+    /// Return the location info recorded for the unknown throw, if any.
+    const ThrowInfo &getUnknownThrowInfo() const { return UnknownThrowInfo; }
+
   private:
     /// Recalculate the 'Behaviour' for example after filtering.
     void reevaluateBehaviour();
@@ -134,6 +144,10 @@ class ExceptionAnalyzer {
     /// based on analyzer configuration.
     bool ThrowsUnknown = false;
 
+    /// Location info for the assumed-throwing function when ThrowsUnknown is
+    /// true. May be invalid if no location is available.
+    ThrowInfo UnknownThrowInfo;
+
     /// 'ThrownException' is empty if the 'Behaviour' is either 'NotThrowing' 
or
     /// 'Unknown'.
     Throwables ThrownExceptions;
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-treat-functions-without-specification-as-throwing.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-treat-functions-without-specification-as-throwing.cpp
index 6e9aa03323ec7..d9d6b2d922511 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-treat-functions-without-specification-as-throwing.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-treat-functions-without-specification-as-throwing.cpp
@@ -15,6 +15,8 @@ void unannotated_no_throw_body() {}
 
 void calls_unannotated() noexcept {
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'calls_unannotated' which should not throw exceptions
+  // CHECK-MESSAGES-ALL: :[[@LINE-4]]:6: note: frame #0: an exception of 
unknown type may be thrown in function 'unannotated_no_throw_body' here
+  // CHECK-MESSAGES-ALL: :[[@LINE+3]]:3: note: frame #1: function 
'calls_unannotated' calls function 'unannotated_no_throw_body' here
   // CHECK-MESSAGES-UNDEFINED-NOT: warning:
   // CHECK-MESSAGES-NONE-NOT: warning:
   unannotated_no_throw_body();
@@ -24,7 +26,11 @@ void extern_declared();
 
 void calls_unknown() noexcept {
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'calls_unknown' which should not throw exceptions
-  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-2]]:6: warning: an exception may be 
thrown in function 'calls_unknown' which should not throw exceptions
+  // CHECK-MESSAGES-ALL: :[[@LINE-4]]:6: note: frame #0: an exception of 
unknown type may be thrown in function 'extern_declared' here
+  // CHECK-MESSAGES-ALL: :[[@LINE+5]]:3: note: frame #1: function 
'calls_unknown' calls function 'extern_declared' here
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-4]]:6: warning: an exception may be 
thrown in function 'calls_unknown' which should not throw exceptions
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-7]]:6: note: frame #0: an exception of 
unknown type may be thrown in function 'extern_declared' here
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE+2]]:3: note: frame #1: function 
'calls_unknown' calls function 'extern_declared' here
   // CHECK-MESSAGES-NONE-NOT: warning:
   extern_declared();
 }
@@ -54,6 +60,29 @@ void call() noexcept {
   nothrow_nobody();
 }
 
+struct Member {
+  Member() noexcept {}
+  Member(const Member &) noexcept {}
+  Member &operator=(const Member &) noexcept { return *this; }
+  ~Member() noexcept {}
+};
+
+struct S {
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: an exception may be thrown 
in function 'S' which should not throw exceptions
+  // CHECK-MESSAGES-ALL: :[[@LINE-2]]:8: note: frame #0: an exception of 
unknown type may be thrown in function 'S' here
+  // CHECK-MESSAGES-ALL: :[[@LINE-3]]:8: warning: an exception may be thrown 
in function 'operator=' which should not throw exceptions
+  // CHECK-MESSAGES-ALL: :[[@LINE-4]]:8: note: frame #0: an exception of 
unknown type may be thrown in function 'operator=' here
+  // CHECK-MESSAGES-ALL: :[[@LINE-5]]:8: warning: an exception may be thrown 
in function '~S' which should not throw exceptions
+  // CHECK-MESSAGES-ALL: :[[@LINE-6]]:8: note: frame #0: an exception of 
unknown type may be thrown in function '~S' here
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-7]]:8: warning: an exception may be 
thrown in function 'S' which should not throw exceptions
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-8]]:8: note: frame #0: an exception of 
unknown type may be thrown in function 'S' here
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-9]]:8: warning: an exception may be 
thrown in function 'operator=' which should not throw exceptions
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-10]]:8: note: frame #0: an exception 
of unknown type may be thrown in function 'operator=' here
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-11]]:8: warning: an exception may be 
thrown in function '~S' which should not throw exceptions
+  // CHECK-MESSAGES-UNDEFINED: :[[@LINE-12]]:8: note: frame #0: an exception 
of unknown type may be thrown in function '~S' here
+  Member m;
+};
+
 void explicit_throw() { throw 1; }
 void calls_explicit_throw() noexcept {
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: an exception may be thrown 
in function 'calls_explicit_throw' which should not throw exceptions

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to