ngg created this revision.
ngg added reviewers: clang-tools-extra, njames93.
Herald added subscribers: carlosgalvezp, lebedev.ri, xazax.hun.
Herald added a reviewer: lebedev.ri.
Herald added a project: All.
ngg requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

If there is a function call that calls a non-throwing function,
this check should not report a warning, even if there is a throw
statement within that function.

The dependent_throw() test case was misleading and suggested that this
warning is not triggered when a throw exception is in unreachable code,
but in reality that warning did not trigger because it could not
determine whether the function should be noexcept without specializing
the T template argument. That test case is fixed to show non-specialized
and specialized cases.

Fixes https://github.com/llvm/llvm-project/issues/54668 and
https://github.com/llvm/llvm-project/issues/54956.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134588

Files:
  clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tools-extra/clang-tidy/openmp/ExceptionEscapeCheck.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -31,11 +31,21 @@
   throw 1;
 }
 
+void indirect_noexcept() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_noexcept' which should not throw exceptions
+  throwing_noexcept();
+}
+
 void throwing_throw_nothing() throw() {
     // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_throw_nothing' which should not throw exceptions
   throw 1;
 }
 
+void indirect_throw_nothing() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_nothing' which should not throw exceptions
+  throwing_throw_nothing();
+}
+
 void throw_and_catch() noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch' which should not throw exceptions
   try {
@@ -200,8 +210,19 @@
 template<typename T>
 void dependent_throw() noexcept(sizeof(T)<4) {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'dependent_throw' which should not throw exceptions
-  if (sizeof(T) > 4)
-    throw 1;
+  // CHECK-MESSAGES-NOT: :[[@LINE-2]]:6: warning: an exception may be thrown in function 'dependent_throw<int>' which should not throw exceptions
+  // CHECK-MESSAGES: :[[@LINE-3]]:6: warning: an exception may be thrown in function 'dependent_throw<char>' which should not throw exceptions
+  throw 1;
+}
+
+void indirect_dependent_noexcept_true() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_dependent_noexcept_true' which should not throw exceptions
+  dependent_throw<char>();
+}
+
+void indirect_dependent_noexcept_false() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_dependent_noexcept_false' which should not throw exceptions
+  dependent_throw<int>();
 }
 
 void swap(int&, int&) {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -128,21 +128,24 @@
     IgnoredExceptions = std::move(ExceptionNames);
   }
 
-  ExceptionInfo analyze(const FunctionDecl *Func);
-  ExceptionInfo analyze(const Stmt *Stmt);
+  ExceptionInfo analyze(const FunctionDecl *Func, ASTContext &Context);
+  ExceptionInfo analyze(const Stmt *Stmt, ASTContext &Context);
 
 private:
   ExceptionInfo
   throwsException(const FunctionDecl *Func,
-                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack,
+                  ASTContext &Context);
   ExceptionInfo
   throwsException(const Stmt *St, const ExceptionInfo::Throwables &Caught,
-                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+                  llvm::SmallSet<const FunctionDecl *, 32> &CallStack,
+                  ASTContext &Context);
 
-  ExceptionInfo analyzeImpl(const FunctionDecl *Func);
-  ExceptionInfo analyzeImpl(const Stmt *Stmt);
+  ExceptionInfo analyzeImpl(const FunctionDecl *Func, ASTContext &Context);
+  ExceptionInfo analyzeImpl(const Stmt *Stmt, ASTContext &Context);
 
-  template <typename T> ExceptionInfo analyzeDispatch(const T *Node);
+  template <typename T>
+  ExceptionInfo analyzeDispatch(const T *Node, ASTContext &Context);
 
   bool IgnoreBadAlloc = true;
   llvm::StringSet<> IgnoredExceptions;
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -12,6 +12,8 @@
 namespace tidy {
 namespace utils {
 
+using namespace ast_matchers;
+
 void ExceptionAnalyzer::ExceptionInfo::registerException(
     const Type *ExceptionType) {
   assert(ExceptionType != nullptr && "Only valid types are accepted");
@@ -111,20 +113,22 @@
 
 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
     const FunctionDecl *Func,
-    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
-  if (CallStack.count(Func))
+    llvm::SmallSet<const FunctionDecl *, 32> &CallStack, ASTContext &Context) {
+  if (CallStack.count(Func) ||
+      (!CallStack.empty() &&
+       !match(functionDecl(isNoThrow()), *Func, Context).empty()))
     return ExceptionInfo::createNonThrowing();
 
   if (const Stmt *Body = Func->getBody()) {
     CallStack.insert(Func);
     ExceptionInfo Result =
-        throwsException(Body, ExceptionInfo::Throwables(), CallStack);
+        throwsException(Body, ExceptionInfo::Throwables(), CallStack, Context);
 
     // For a constructor, we also have to check the initializers.
     if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
       for (const CXXCtorInitializer *Init : Ctor->inits()) {
         ExceptionInfo Excs = throwsException(
-            Init->getInit(), ExceptionInfo::Throwables(), CallStack);
+            Init->getInit(), ExceptionInfo::Throwables(), CallStack, Context);
         Result.merge(Excs);
       }
     }
@@ -145,7 +149,7 @@
 /// possible except some 'Unknown' functions are called.
 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
     const Stmt *St, const ExceptionInfo::Throwables &Caught,
-    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+    llvm::SmallSet<const FunctionDecl *, 32> &CallStack, ASTContext &Context) {
   auto Results = ExceptionInfo::createNonThrowing();
   if (!St)
     return Results;
@@ -167,14 +171,15 @@
       Results.registerExceptions(Caught);
   } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
     ExceptionInfo Uncaught =
-        throwsException(Try->getTryBlock(), Caught, CallStack);
+        throwsException(Try->getTryBlock(), Caught, CallStack, Context);
     for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
       const CXXCatchStmt *Catch = Try->getHandler(I);
 
       // Everything is catched through 'catch(...)'.
       if (!Catch->getExceptionDecl()) {
-        ExceptionInfo Rethrown = throwsException(
-            Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
+        ExceptionInfo Rethrown =
+            throwsException(Catch->getHandlerBlock(),
+                            Uncaught.getExceptionTypes(), CallStack, Context);
         Results.merge(Rethrown);
         Uncaught.clear();
       } else {
@@ -193,8 +198,8 @@
         if (Uncaught.filterByCatch(CaughtType)) {
           ExceptionInfo::Throwables CaughtExceptions;
           CaughtExceptions.insert(CaughtType);
-          ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
-                                                   CaughtExceptions, CallStack);
+          ExceptionInfo Rethrown = throwsException(
+              Catch->getHandlerBlock(), CaughtExceptions, CallStack, Context);
           Results.merge(Rethrown);
         }
       }
@@ -202,20 +207,20 @@
     Results.merge(Uncaught);
   } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
     if (const FunctionDecl *Func = Call->getDirectCallee()) {
-      ExceptionInfo Excs = throwsException(Func, CallStack);
+      ExceptionInfo Excs = throwsException(Func, CallStack, Context);
       Results.merge(Excs);
     }
   } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
     ExceptionInfo Excs =
-        throwsException(Construct->getConstructor(), CallStack);
+        throwsException(Construct->getConstructor(), CallStack, Context);
     Results.merge(Excs);
   } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
     ExceptionInfo Excs =
-        throwsException(DefaultInit->getExpr(), Caught, CallStack);
+        throwsException(DefaultInit->getExpr(), Caught, CallStack, Context);
     Results.merge(Excs);
   } else {
     for (const Stmt *Child : St->children()) {
-      ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
+      ExceptionInfo Excs = throwsException(Child, Caught, CallStack, Context);
       Results.merge(Excs);
     }
   }
@@ -223,13 +228,13 @@
 }
 
 ExceptionAnalyzer::ExceptionInfo
-ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
+ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func, ASTContext &Context) {
   ExceptionInfo ExceptionList;
 
   // Check if the function has already been analyzed and reuse that result.
   if (FunctionCache.count(Func) == 0) {
     llvm::SmallSet<const FunctionDecl *, 32> CallStack;
-    ExceptionList = throwsException(Func, CallStack);
+    ExceptionList = throwsException(Func, CallStack, Context);
 
     // Cache the result of the analysis. This is done prior to filtering
     // because it is best to keep as much information as possible.
@@ -243,15 +248,15 @@
 }
 
 ExceptionAnalyzer::ExceptionInfo
-ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
+ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt, ASTContext &Context) {
   llvm::SmallSet<const FunctionDecl *, 32> CallStack;
-  return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
+  return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack, Context);
 }
 
 template <typename T>
 ExceptionAnalyzer::ExceptionInfo
-ExceptionAnalyzer::analyzeDispatch(const T *Node) {
-  ExceptionInfo ExceptionList = analyzeImpl(Node);
+ExceptionAnalyzer::analyzeDispatch(const T *Node, ASTContext &Context) {
+  ExceptionInfo ExceptionList = analyzeImpl(Node, Context);
 
   if (ExceptionList.getBehaviour() == State::NotThrowing ||
       ExceptionList.getBehaviour() == State::Unknown)
@@ -265,13 +270,13 @@
 }
 
 ExceptionAnalyzer::ExceptionInfo
-ExceptionAnalyzer::analyze(const FunctionDecl *Func) {
-  return analyzeDispatch(Func);
+ExceptionAnalyzer::analyze(const FunctionDecl *Func, ASTContext &Context) {
+  return analyzeDispatch(Func, Context);
 }
 
 ExceptionAnalyzer::ExceptionInfo
-ExceptionAnalyzer::analyze(const Stmt *Stmt) {
-  return analyzeDispatch(Stmt);
+ExceptionAnalyzer::analyze(const Stmt *Stmt, ASTContext &Context) {
+  return analyzeDispatch(Stmt, Context);
 }
 
 } // namespace utils
Index: clang-tools-extra/clang-tidy/openmp/ExceptionEscapeCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/openmp/ExceptionEscapeCheck.cpp
+++ clang-tools-extra/clang-tidy/openmp/ExceptionEscapeCheck.cpp
@@ -58,7 +58,7 @@
       Result.Nodes.getNodeAs<Stmt>("structured-block");
   assert(StructuredBlock && "Expected to get some OpenMP Structured Block.");
 
-  if (Tracer.analyze(StructuredBlock).getBehaviour() !=
+  if (Tracer.analyze(StructuredBlock, *Result.Context).getBehaviour() !=
       utils::ExceptionAnalyzer::State::Throwing)
     return; // No exceptions have been proven to escape out of the struc. block.
 
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -68,7 +68,7 @@
   if (!MatchedDecl)
     return;
 
-  if (Tracer.analyze(MatchedDecl).getBehaviour() ==
+  if (Tracer.analyze(MatchedDecl, *Result.Context).getBehaviour() ==
       utils::ExceptionAnalyzer::State::Throwing)
     // FIXME: We should provide more information about the exact location where
     // the exception is thrown, maybe the full path the exception escapes
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D134588: [clang-tid... Gergely Nagy via Phabricator via cfe-commits

Reply via email to