[clang-tools-extra] [clang-tidy] Fix crashes when analyzing unknown exceptions in bugprone-exception-escape (PR #218067)
Zeyi Xu via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 22 05:47:22 PDT 2026
https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/218067
>From 14bf89bebc6dd308a2d057d168071cf9598f603b Mon Sep 17 00:00:00 2001
From: Zeyi Xu <mitchell.xu2 at gmail.com>
Date: Sat, 22 Aug 2026 07:31:05 +0800
Subject: [PATCH 1/2] [clang-tidy] Fix crashes when analyzing unknown
exceptions in bugprone-exception-escape
---
.../clang-tidy/utils/ExceptionAnalyzer.cpp | 4 ++
clang-tools-extra/docs/ReleaseNotes.md | 6 ++-
.../exception-escape-coro-unknown.cpp | 42 +++++++++++++++++++
...ions-without-specification-as-throwing.cpp | 11 +++++
4 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro-unknown.cpp
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 7decd18dd25b9..f414272ba15bd 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -362,6 +362,8 @@ ExceptionAnalyzer::ExceptionInfo::filterByCatch(const Type *HandlerTy,
SmallVector<const Type *, 8> TypesToDelete;
for (const auto &ThrownException : ThrownExceptions) {
const Type *ExceptionTy = ThrownException.getFirst();
+ if (!ExceptionTy)
+ continue;
const CanQualType ExceptionCanTy =
ExceptionTy->getCanonicalTypeUnqualified();
const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
@@ -607,6 +609,8 @@ ExceptionAnalyzer::throwsException(const Stmt *St,
Excs.getExceptions(), CallStack));
for (const auto &Exception : Excs.getExceptions()) {
const Type *ExcType = Exception.getFirst();
+ if (!ExcType)
+ continue;
if (const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
const ExceptionInfo DestructorExcs = throwsException(
ThrowableRec->getDestructor(), Caught, CallStack, SourceLocation{});
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index 9cead803ad0e5..69660191e0077 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -118,6 +118,10 @@ infrastructure are described first, followed by tool-specific sections.
#### Changes in existing checks
+- Fixed crashes in {doc}`bugprone-exception-escape
+ <clang-tidy/checks/bugprone/exception-escape>` when analyzing exceptions of
+ unknown type with `TreatFunctionsWithoutSpecificationAsThrowing` enabled.
+
- Fixed a crash in {doc}`bugprone-misplaced-operator-in-strlen-in-alloc
<clang-tidy/checks/bugprone/misplaced-operator-in-strlen-in-alloc>` when
checking an array new expression without a size expression.
@@ -129,7 +133,7 @@ infrastructure are described first, followed by tool-specific sections.
- Improved {doc}`cppcoreguidelines-pro-type-member-init
<clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by treating
`std::array` the same as built-in arrays when `IgnoreArrays` option is enabled.
-
+
- Improved {doc}`cppcoreguidelines-use-enum-class
<clang-tidy/checks/cppcoreguidelines/use-enum-class>` check by omitting unnamed enums from the `enum class` requirement, as previously the check suggested users an ill-formed fix.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro-unknown.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro-unknown.cpp
new file mode 100644
index 0000000000000..a2325c401c404
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro-unknown.cpp
@@ -0,0 +1,42 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s bugprone-exception-escape %t -- \
+// RUN: -config='{"CheckOptions": { \
+// RUN: "bugprone-exception-escape.TreatFunctionsWithoutSpecificationAsThrowing": "OnlyUndefined" \
+// RUN: }}' -- -fexceptions
+
+namespace std {
+
+template <class Ret, typename... T> struct coroutine_traits {
+ using promise_type = typename Ret::promise_type;
+};
+
+template <class Promise = void> struct coroutine_handle {
+ template <class OtherPromise>
+ coroutine_handle(coroutine_handle<OtherPromise>) noexcept;
+ static coroutine_handle from_address(void *) noexcept;
+};
+
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+} // namespace std
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() noexcept { return {}; }
+ std::suspend_never initial_suspend() noexcept { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() noexcept {}
+ void unhandled_exception() noexcept {}
+ };
+};
+
+void undefined();
+
+Task calls_undefined() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'calls_undefined' which should not throw exceptions
+ undefined();
+ co_return;
+}
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 ee9f19b5a2896..051c88496ec21 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
@@ -47,6 +47,17 @@ void calls_unknown_caught() noexcept {
}
}
+struct Error {};
+
+void calls_unknown_typed_catch() noexcept {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'calls_unknown_typed_catch' which should not throw exceptions
+ // CHECK-MESSAGES-UNDEFINED: :[[@LINE-2]]:6: warning: an exception may be thrown in function 'calls_unknown_typed_catch' which should not throw exceptions
+ try {
+ extern_declared();
+ } catch (const Error &) {
+ }
+}
+
void definitely_nothrow() noexcept {}
void calls_nothrow() noexcept {
>From f7eabb48ca4aaa599f836e32e3e7d01da46d212f Mon Sep 17 00:00:00 2001
From: Zeyi Xu <mitchell.xu2 at gmail.com>
Date: Sat, 22 Aug 2026 20:46:51 +0800
Subject: [PATCH 2/2] address feedback
---
clang-tools-extra/docs/ReleaseNotes.md | 4 ----
...-functions-without-specification-as-throwing.cpp | 13 +++++++++----
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index 69660191e0077..a28bb49c7ce1c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -118,10 +118,6 @@ infrastructure are described first, followed by tool-specific sections.
#### Changes in existing checks
-- Fixed crashes in {doc}`bugprone-exception-escape
- <clang-tidy/checks/bugprone/exception-escape>` when analyzing exceptions of
- unknown type with `TreatFunctionsWithoutSpecificationAsThrowing` enabled.
-
- Fixed a crash in {doc}`bugprone-misplaced-operator-in-strlen-in-alloc
<clang-tidy/checks/bugprone/misplaced-operator-in-strlen-in-alloc>` when
checking an array new expression without a size expression.
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 051c88496ec21..14c280a5901d6 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
@@ -37,21 +37,26 @@ void calls_unknown() noexcept {
extern_declared();
}
+struct Error {};
+
void calls_unknown_caught() noexcept {
// CHECK-MESSAGES-ALL-NOT: warning:
// CHECK-MESSAGES-UNDEFINED-NOT: warning:
// CHECK-MESSAGES-NONE-NOT: warning:
try {
extern_declared();
- } catch(...) {
+ } catch (const Error &) {
+ } catch (...) {
}
}
-struct Error {};
-
void calls_unknown_typed_catch() noexcept {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'calls_unknown_typed_catch' which should not throw exceptions
- // CHECK-MESSAGES-UNDEFINED: :[[@LINE-2]]:6: warning: an exception may be thrown in function 'calls_unknown_typed_catch' which should not throw exceptions
+ // CHECK-MESSAGES-ALL: :[[@LINE-28]]:6: note: frame #0: an exception of unknown type may be thrown in function 'extern_declared' here
+ // CHECK-MESSAGES-ALL: :[[@LINE+5]]:5: note: frame #1: function 'calls_unknown_typed_catch' calls function 'extern_declared' here
+ // CHECK-MESSAGES-UNDEFINED: :[[@LINE-4]]:6: warning: an exception may be thrown in function 'calls_unknown_typed_catch' which should not throw exceptions
+ // CHECK-MESSAGES-UNDEFINED: :[[@LINE-31]]:6: note: frame #0: an exception of unknown type may be thrown in function 'extern_declared' here
+ // CHECK-MESSAGES-UNDEFINED: :[[@LINE+2]]:5: note: frame #1: function 'calls_unknown_typed_catch' calls function 'extern_declared' here
try {
extern_declared();
} catch (const Error &) {
More information about the cfe-commits
mailing list