[clang-tools-extra] [clang-tidy] Fix FP in bugprone-exception-escape with unevaluated exception specs (PR #190593)
Zeyi Xu via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 6 03:17:35 PDT 2026
https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/190593
>From 3a7525b227ae0306dd2c85d7d2e3aa5e0cc23782 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Mon, 6 Apr 2026 17:19:18 +0800
Subject: [PATCH 1/2] [clang-tidy] Fix FP in bugprone-exception-escape with
unevaluated exception specs
---
.../clang-tidy/utils/ExceptionAnalyzer.cpp | 9 +++++++++
...-functions-without-specification-as-throwing.cpp | 13 -------------
2 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 9165be3c850d7..ea3ceac65af20 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -330,6 +330,12 @@ static bool canThrow(const FunctionDecl *Func) {
if (!FunProto)
return true;
+ // An unresolved exception spec means the function is a defaulted or
+ // uninstantiated special member that has never been called. It cannot
+ // propagate exceptions to any caller, so non-throwing.
+ if (isUnresolvedExceptionSpec(FunProto->getExceptionSpecType()))
+ return false;
+
switch (FunProto->canThrow()) {
case CT_Cannot:
return false;
@@ -499,6 +505,9 @@ ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
auto Result = ExceptionInfo::createUnknown();
if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
+ if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
+ return ExceptionInfo::createNonThrowing();
+
for (const QualType &Ex : FPT->exceptions()) {
CallStack.insert({Func, CallLoc});
Result.registerException(
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 6f955fa5a012a..53bf8cd62f0cf 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
@@ -68,19 +68,6 @@ struct Member {
};
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;
};
>From eb0f1e9d56cb6f9ec85b4ab34bfe1db3626fd219 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Mon, 6 Apr 2026 18:17:12 +0800
Subject: [PATCH 2/2] improve comments
---
clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index ea3ceac65af20..8f0d6b1360cd5 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -330,9 +330,9 @@ static bool canThrow(const FunctionDecl *Func) {
if (!FunProto)
return true;
- // An unresolved exception spec means the function is a defaulted or
- // uninstantiated special member that has never been called. It cannot
- // propagate exceptions to any caller, so non-throwing.
+ // Clang evaluates unresolved exception specs before generating any call to
+ // the function, so these functions cannot appear at a call site and cannot
+ // throw.
if (isUnresolvedExceptionSpec(FunProto->getExceptionSpecType()))
return false;
More information about the cfe-commits
mailing list