Author: Zeyi Xu
Date: 2026-04-05T22:07:20+08:00
New Revision: 0b7f01ad515d83f1f3144a44a1b0145187eff484

URL: 
https://github.com/llvm/llvm-project/commit/0b7f01ad515d83f1f3144a44a1b0145187eff484
DIFF: 
https://github.com/llvm/llvm-project/commit/0b7f01ad515d83f1f3144a44a1b0145187eff484.diff

LOG: [clang-tidy] Add frames for bugprone-exception-escape options (#187971)

This patch adds frames emitting for
`bugprone-exception-escape.TreatFunctionsWithoutSpecificationAsThrowing`.

As of AI Usage: Gemini 3 is used for pre-commit reviewing.

Closes https://github.com/llvm/llvm-project/issues/184781

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
    clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-treat-functions-without-specification-as-throwing.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
index e4facdad2c7aa..7d23e2d2b5630 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -155,11 +155,19 @@ void ExceptionEscapeCheck::check(const 
MatchFinder::MatchResult &Result) {
     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 (ThrowType) {
+    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;
+  } else {
+    diag(ThrowInfo.Loc,
+         "frame #0: an exception of unknown type may be thrown in function %0 "
+         "here",
+         DiagnosticIDs::Note)
+        << Stack.back().first;
+  }
 
   size_t FrameNo = 1;
   for (auto CurrIt = ++Stack.rbegin(), PrevIt = Stack.rbegin();

diff  --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp 
b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 2a54de88dc4d6..9165be3c850d7 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -12,7 +12,6 @@ namespace clang::tidy::utils {
 
 void ExceptionAnalyzer::ExceptionInfo::registerException(
     const Type *ExceptionType, const ThrowInfo &ThrowInfo) {
-  assert(ExceptionType != nullptr && "Only valid types are accepted");
   Behaviour = State::Throwing;
   ThrownExceptions.insert({ExceptionType, ThrowInfo});
 }
@@ -432,6 +431,8 @@ ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
   // Therefore this slightly hacky implementation is required.
   for (const auto &ThrownException : ThrownExceptions) {
     const Type *T = ThrownException.getFirst();
+    if (!T)
+      continue;
     if (const auto *TD = T->getAsTagDecl()) {
       if (TD->getDeclName().isIdentifier()) {
         if ((IgnoreBadAlloc &&
@@ -484,13 +485,14 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
       }
     }
 
-    CallStack.erase(Func);
     // Optionally treat unannotated functions as potentially throwing if they
     // are not explicitly non-throwing and no throw was discovered.
     if (AssumeUnannotatedFunctionsAsThrowing &&
         Result.getBehaviour() == State::NotThrowing && canThrow(Func)) {
-      Result.registerUnknownException();
+      Result.registerException(nullptr, {Func->getLocation(), CallStack});
     }
+
+    CallStack.erase(Func);
     return Result;
   }
 
@@ -507,8 +509,11 @@ ExceptionAnalyzer::ExceptionInfo 
ExceptionAnalyzer::throwsException(
   }
 
   if (AssumeMissingDefinitionsFunctionsAsThrowing &&
-      Result.getBehaviour() == State::Unknown)
-    Result.registerUnknownException();
+      Result.getBehaviour() == State::Unknown) {
+    CallStack.insert({Func, CallLoc});
+    Result.registerException(nullptr, {Func->getLocation(), CallStack});
+    CallStack.erase(Func);
+  }
 
   return Result;
 }

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..6f955fa5a012a 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,30 @@ 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
+  // FIXME: clearly non-throwing functions should not be marked as throwing
+  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