aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra
diff options
context:
space:
mode:
authorPiotr Zegar <me@piotrzegar.pl>2023-07-17 15:01:38 +0000
committerPiotr Zegar <me@piotrzegar.pl>2023-07-17 15:59:34 +0000
commit27245077643ae8b94a0511ee1c3a39d6f4ca8076 (patch)
tree5c729ac9cd94b9f912f81f1b8a88b9a486550846 /clang-tools-extra
parenta64b3e92c7cb0dd474e0ecbdb9fb86c29487451f (diff)
downloadllvm-27245077643ae8b94a0511ee1c3a39d6f4ca8076.zip
llvm-27245077643ae8b94a0511ee1c3a39d6f4ca8076.tar.gz
llvm-27245077643ae8b94a0511ee1c3a39d6f4ca8076.tar.bz2
[clang-tidy] Model noexcept more properly in bugprone-exception-escape
During call stack analysis skip called noexcept functions as they wont throw exceptions, they will crash. Check will emit warnings for those functions separately. Fixes: #43667, #49151, #51596, #54668, #54956 Reviewed By: carlosgalvezp Differential Revision: https://reviews.llvm.org/D153458
Diffstat (limited to 'clang-tools-extra')
-rw-r--r--clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp30
-rw-r--r--clang-tools-extra/docs/ReleaseNotes.rst3
-rw-r--r--clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp37
3 files changed, 59 insertions, 11 deletions
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index c3e9a13..f1f1cc3 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -316,6 +316,34 @@ bool isQualificationConvertiblePointer(QualType From, QualType To,
}
} // namespace
+static bool canThrow(const FunctionDecl *Func) {
+ const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
+ if (!FunProto)
+ return true;
+
+ switch (FunProto->canThrow()) {
+ case CT_Cannot:
+ return false;
+ case CT_Dependent: {
+ const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
+ if (!NoexceptExpr)
+ return true; // no noexept - can throw
+
+ if (NoexceptExpr->isValueDependent())
+ return true; // depend on template - some instance can throw
+
+ bool Result = false;
+ if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
+ /*InConstantContext=*/true))
+ return true; // complex X condition in noexcept(X), cannot validate,
+ // assume that may throw
+ return !Result; // noexcept(false) - can throw
+ }
+ default:
+ return true;
+ };
+}
+
bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
const Type *HandlerTy, const ASTContext &Context) {
llvm::SmallVector<const Type *, 8> TypesToDelete;
@@ -421,7 +449,7 @@ void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
- if (CallStack.count(Func))
+ if (!Func || CallStack.count(Func) || (!CallStack.empty() && !canThrow(Func)))
return ExceptionInfo::createNonThrowing();
if (const Stmt *Body = Func->getBody()) {
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 820a76c..abe815e 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -275,7 +275,8 @@ Changes in existing checks
- Improved :doc:`bugprone-exception-escape
<clang-tidy/checks/bugprone/exception-escape>` to not emit warnings for
- forward declarations of functions.
+ forward declarations of functions and additionally modified it to skip
+ ``noexcept`` functions during call stack analysis.
- Fixed :doc:`bugprone-exception-escape<clang-tidy/checks/bugprone/exception-escape>`
for coroutines where previously a warning would be emitted with coroutines
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 f575a18..39cf547 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
@@ -152,7 +152,7 @@ void throw_catch_multi_ptr_4() noexcept {
}
// FIXME: In this case 'a' is convertible to the handler and should be caught
-// but in reality it's thrown. Note that clang doesn't report a warning for
+// but in reality it's thrown. Note that clang doesn't report a warning for
// this either.
void throw_catch_multi_ptr_5() noexcept {
try {
@@ -249,7 +249,7 @@ void throw_derived_catch_base_alias() noexcept {
void throw_derived_catch_base_ptr_c() noexcept {
try {
derived d;
- throw &d;
+ throw &d;
} catch(const base *) {
}
}
@@ -259,7 +259,7 @@ void throw_derived_catch_base_ptr() noexcept {
try {
derived d;
const derived *p = &d;
- throw p;
+ throw p;
} catch(base *) {
}
}
@@ -282,7 +282,7 @@ void throw_derived_catch_base_private() noexcept {
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_private' which should not throw exceptions
try {
B b;
- throw b;
+ throw b;
} catch(A) {
}
}
@@ -291,7 +291,7 @@ 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
try {
B b;
- throw &b;
+ throw &b;
} catch(A *) {
}
}
@@ -300,7 +300,7 @@ 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
try {
C c;
- throw c;
+ throw c;
} catch(A) {
}
}
@@ -309,7 +309,7 @@ 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
try {
C c;
- throw &c;
+ throw &c;
} catch(A *) {
}
}
@@ -318,7 +318,7 @@ 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
try {
E e;
- throw e;
+ throw e;
} catch(A) {
}
}
@@ -327,7 +327,7 @@ 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
try {
E e;
- throw e;
+ throw e;
} catch(A) {
}
}
@@ -703,3 +703,22 @@ int main() {
throw 1;
return 0;
}
+
+// 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
+
+void test_basic_throw() noexcept(false) { throw 42; }
+
+void only_calls_non_throwing() noexcept {
+ test_basic_no_throw();
+}
+
+void calls_non_and_throwing() noexcept {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'calls_non_and_throwing' which should not throw exceptions
+ test_basic_no_throw();
+ test_basic_throw();
+}
+