JonasToth updated this revision to Diff 183136.
JonasToth added a comment.

- rebase to current refactoring


Repository:
  rCTE Clang Tools Extra

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D57108/new/

https://reviews.llvm.org/D57108

Files:
  clang-tidy/bugprone/ExceptionEscapeCheck.cpp
  clang-tidy/bugprone/ExceptionEscapeCheck.h
  clang-tidy/modernize/UseNoexceptCheck.cpp
  clang-tidy/modernize/UseNoexceptCheck.h
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tidy/utils/ExceptionAnalyzer.h
  docs/clang-tidy/checks/bugprone-exception-escape.rst
  test/clang-tidy/bugprone-exception-escape.cpp
  test/clang-tidy/modernize-use-noexcept-new-noexcept.cpp
  test/clang-tidy/modernize-use-noexcept-opt.cpp
  test/clang-tidy/modernize-use-noexcept.cpp

Index: test/clang-tidy/modernize-use-noexcept.cpp
===================================================================
--- test/clang-tidy/modernize-use-noexcept.cpp
+++ test/clang-tidy/modernize-use-noexcept.cpp
@@ -10,9 +10,9 @@
 
 template <typename T>
 void foo() throw();
-void footest() { foo<int>(); foo<double>(); }
-// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 // CHECK-FIXES: void foo() noexcept;
+void footest() { foo<int>(); foo<double>(); }
 
 void bar() throw(...);
 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
@@ -70,21 +70,21 @@
 
 struct S {
   void f() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 };
 void f(void (S::*)() throw());
-// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
-// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 // CHECK-FIXES: void f() noexcept;
 // CHECK-FIXES: void f(void (S::*)() noexcept);
 
 template <typename T>
 struct ST {
   void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 };
 template <typename T>
 void ft(void (ST<T>::*)() throw());
-// CHECK-MESSAGES: :[[@LINE-4]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
-// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 // CHECK-FIXES: void foo() noexcept;
 // CHECK-FIXES: void ft(void (ST<T>::*)() noexcept);
 
Index: test/clang-tidy/modernize-use-noexcept-opt.cpp
===================================================================
--- test/clang-tidy/modernize-use-noexcept-opt.cpp
+++ test/clang-tidy/modernize-use-noexcept-opt.cpp
@@ -65,11 +65,11 @@
 
 struct S {
   void f() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f() noexcept;
 };
 void f(void (S::*)() throw());
-// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
-// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
-// CHECK-FIXES: void f() noexcept;
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
 // CHECK-FIXES: void f(void (S::*)() noexcept);
 
 typedef void (*fp)(void (*fp2)(int) throw());
Index: test/clang-tidy/modernize-use-noexcept-new-noexcept.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-noexcept-new-noexcept.cpp
@@ -0,0 +1,46 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-noexcept.AddMissingNoexcept, value: 1}]}" \
+// RUN:   -- -std=c++11 -fexceptions
+
+void undefined();
+void undefinedNoexcept() noexcept;
+
+void empty() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept'
+
+int tryCatchBody() try {
+  int Array[] = {1, 2, 3, 4};
+  throw int(42);
+  return Array[0] + Array[1] + Array[2] + Array[3];
+} catch (...) {
+  return 42;
+}
+// CHECK-MESSAGES: :[[@LINE-7]]:1: warning: this function can not throw an exception, consider adding 'noexcept'
+
+int functionCanThrow() {
+  throw int(42);
+}
+
+struct ConstructorWithNoexcept {
+  ConstructorWithNoexcept() = default;
+  int nonThrowingMember() { return 42; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this function can not throw an exception, consider adding 'noexcept'
+  int nonThrowingButShown() noexcept { return 42; }
+  void undefinedMethod();
+  int Member;
+};
+
+template <typename T>
+void nonThrowingTemplateUnknown();
+
+template <typename T>
+T nonThrowingTemplateKnown() {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept'
+  return T{};
+}
+void instantiate() {
+  // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept'
+  (void)nonThrowingTemplateKnown<int>();
+  (void)nonThrowingTemplateKnown<float>();
+  (void)nonThrowingTemplateKnown<double>();
+}
Index: test/clang-tidy/bugprone-exception-escape.cpp
===================================================================
--- test/clang-tidy/bugprone-exception-escape.cpp
+++ test/clang-tidy/bugprone-exception-escape.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-std=c++11 -extra-arg=-fexceptions -config="{CheckOptions: [{key: bugprone-exception-escape.IgnoredExceptions, value: 'ignored1,ignored2'}, {key: bugprone-exception-escape.FunctionsThatShouldNotThrow, value: 'enabled1,enabled2,enabled3'}]}" --
+// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-std=c++11 -extra-arg=-fexceptions -config="{CheckOptions: [{key: bugprone-exception-escape.IgnoredExceptions, value: 'ignored1;ignored2'}, {key: bugprone-exception-escape.FunctionsThatShouldNotThrow, value: 'enabled1;enabled2;enabled3'}]}" --
 
 struct throwing_destructor {
   ~throwing_destructor() {
Index: docs/clang-tidy/checks/bugprone-exception-escape.rst
===================================================================
--- docs/clang-tidy/checks/bugprone-exception-escape.rst
+++ docs/clang-tidy/checks/bugprone-exception-escape.rst
@@ -28,12 +28,12 @@
 
 .. option:: FunctionsThatShouldNotThrow
 
-   Comma separated list containing function names which should not throw. An
+   Semicolon separated list containing function names which should not throw. An
    example value for this parameter can be ``WinMain`` which adds function
    ``WinMain()`` in the Windows API to the list of the funcions which should
    not throw. Default value is an empty string.
 
 .. option:: IgnoredExceptions
 
-   Comma separated list containing type names which are not counted as thrown
+   Semicolon separated list containing type names which are not counted as thrown
    exceptions in the check. Default value is an empty string.
Index: clang-tidy/utils/ExceptionAnalyzer.h
===================================================================
--- /dev/null
+++ clang-tidy/utils/ExceptionAnalyzer.h
@@ -0,0 +1,50 @@
+//===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
+
+#include "clang/AST/ASTContext.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// This class analysis if a `FunctionDecl` can in principle throw an exception,
+/// either directly or indirectly.
+/// It can be configured to ignore some exception types, especially
+/// `std::bad_alloc` can be disabled as using dynamic memory will always
+/// give the possibility of an exception.
+class ExceptionAnalyzer {
+public:
+  ExceptionAnalyzer() = default;
+
+  bool throwsException(const FunctionDecl *Func);
+  void ignoreExceptions(llvm::StringSet<> ExceptionNames) {
+    IgnoredExceptions = std::move(ExceptionNames);
+  }
+
+private:
+  using TypeVec = llvm::SmallVector<const Type *, 8>;
+
+  TypeVec throwsException(const FunctionDecl *Func,
+                          llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+  TypeVec throwsException(const Stmt *St, const TypeVec &Caught,
+                          llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+  bool isIgnoredExceptionType(const Type *Exception);
+
+  llvm::StringSet<> IgnoredExceptions;
+  llvm::DenseMap<const FunctionDecl *, bool> FunctionCache;
+};
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H
Index: clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -1,4 +1,4 @@
-//===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===//
+//===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,22 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "ExceptionEscapeCheck.h"
+#include "ExceptionAnalyzer.h"
 
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/StringSet.h"
-
-using namespace clang::ast_matchers;
-
-namespace {
-typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
-} // namespace
-
 namespace clang {
-
 static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
   const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
   const auto *BaseClass = BaseType->getAsCXXRecordDecl();
@@ -32,13 +22,12 @@
       [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
 }
 
-static const TypeVec
-throwsException(const Stmt *St, const TypeVec &Caught,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+namespace tidy {
+namespace utils {
 
-static const TypeVec
-throwsException(const FunctionDecl *Func,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+ExceptionAnalyzer::TypeVec ExceptionAnalyzer::throwsException(
+    const FunctionDecl *Func,
+    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
   if (CallStack.count(Func))
     return TypeVec();
 
@@ -58,9 +47,9 @@
   return Result;
 }
 
-static const TypeVec
-throwsException(const Stmt *St, const TypeVec &Caught,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+ExceptionAnalyzer::TypeVec ExceptionAnalyzer::throwsException(
+    const Stmt *St, const TypeVec &Caught,
+    llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
   TypeVec Results;
 
   if (!St)
@@ -76,8 +65,8 @@
                          ->getUnqualifiedDesugaredType();
       }
       if (const auto *TD = ThrownType->getAsTagDecl()) {
-        if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc"
-            && TD->isInStdNamespace())
+        if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc" &&
+            TD->isInStdNamespace())
           return Results;
       }
       Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
@@ -129,88 +118,37 @@
   return Results;
 }
 
-static const TypeVec throwsException(const FunctionDecl *Func) {
+bool ExceptionAnalyzer::throwsException(const FunctionDecl *Func) {
+  // Check if the function has already been analyzed and reuse that result.
+  if (FunctionCache.count(Func) > 0)
+    return FunctionCache[Func];
+
   llvm::SmallSet<const FunctionDecl *, 32> CallStack;
-  return throwsException(Func, CallStack);
-}
+  TypeVec ExceptionList = throwsException(Func, CallStack);
 
-namespace ast_matchers {
-AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
-  TypeVec ExceptionList = throwsException(&Node);
-  auto NewEnd = llvm::remove_if(
-      ExceptionList, [this, Finder, Builder](const Type *Exception) {
-        return !InnerMatcher.matches(*Exception, Finder, Builder);
-      });
+  // Remove all ignored exceptions from the list of exceptions that can be
+  // thrown.
+  auto NewEnd = llvm::remove_if(ExceptionList, [this](const Type *Exception) {
+    return isIgnoredExceptionType(Exception);
+  });
   ExceptionList.erase(NewEnd, ExceptionList.end());
-  return ExceptionList.size();
+
+  // Cache the result of the analysis.
+  bool FunctionThrows = ExceptionList.size() > 0;
+  FunctionCache.insert(std::make_pair(Func, FunctionThrows));
+
+  return FunctionThrows;
 }
 
-AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) {
-  if (const auto *TD = Node.getAsTagDecl()) {
+bool ExceptionAnalyzer::isIgnoredExceptionType(const Type *Exception) {
+  if (const auto *TD = Exception->getAsTagDecl()) {
     if (TD->getDeclName().isIdentifier())
       return IgnoredExceptions.count(TD->getName()) > 0;
   }
   return false;
 }
 
-AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
-              FunctionsThatShouldNotThrow) {
-  return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
-}
-} // namespace ast_matchers
-
-namespace tidy {
-namespace bugprone {
-
-ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
-                                           ClangTidyContext *Context)
-    : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
-                                         "FunctionsThatShouldNotThrow", "")),
-      RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
-  llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
-      IgnoredExceptionsVec;
-  StringRef(RawFunctionsThatShouldNotThrow)
-      .split(FunctionsThatShouldNotThrowVec, ",", -1, false);
-  FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
-                                     FunctionsThatShouldNotThrowVec.end());
-  StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
-  IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
-                           IgnoredExceptionsVec.end());
-}
-
-void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
-  Options.store(Opts, "FunctionsThatShouldNotThrow",
-                RawFunctionsThatShouldNotThrow);
-  Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions);
-}
-
-void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
-  if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions)
-    return;
-
-  Finder->addMatcher(
-      functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(),
-                         cxxConstructorDecl(isMoveConstructor()),
-                         cxxMethodDecl(isMoveAssignmentOperator()),
-                         hasName("main"), hasName("swap"),
-                         isEnabled(FunctionsThatShouldNotThrow)),
-                   throws(unless(isIgnored(IgnoredExceptions))))
-          .bind("thrower"),
-      this);
-}
-
-void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
-  const FunctionDecl *MatchedDecl =
-      Result.Nodes.getNodeAs<FunctionDecl>("thrower");
-  if (!MatchedDecl)
-    return;
-
-  // FIXME: We should provide more information about the exact location where
-  // the exception is thrown, maybe the full path the exception escapes
-  diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 "
-       "which should not throw exceptions") << MatchedDecl;
-}
-
-} // namespace bugprone
+} // namespace utils
 } // namespace tidy
+
 } // namespace clang
Index: clang-tidy/utils/CMakeLists.txt
===================================================================
--- clang-tidy/utils/CMakeLists.txt
+++ clang-tidy/utils/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangTidyUtils
   ASTUtils.cpp
   DeclRefExprUtils.cpp
+  ExceptionAnalyzer.cpp
   ExprSequence.cpp
   FixItHintUtils.cpp
   HeaderFileExtensionsUtils.cpp
Index: clang-tidy/modernize/UseNoexceptCheck.h
===================================================================
--- clang-tidy/modernize/UseNoexceptCheck.h
+++ clang-tidy/modernize/UseNoexceptCheck.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
 
 #include "../ClangTidy.h"
+#include "../utils/ExceptionAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -38,7 +39,9 @@
 
 private:
   const std::string NoexceptMacro;
-  bool UseNoexceptFalse;
+  const bool UseNoexceptFalse;
+  const bool AddMissingNoexcept;
+  utils::ExceptionAnalyzer Analyzer;
 };
 
 } // namespace modernize
Index: clang-tidy/modernize/UseNoexceptCheck.cpp
===================================================================
--- clang-tidy/modernize/UseNoexceptCheck.cpp
+++ clang-tidy/modernize/UseNoexceptCheck.cpp
@@ -19,11 +19,13 @@
 UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
       NoexceptMacro(Options.get("ReplacementString", "")),
-      UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {}
+      UseNoexceptFalse(Options.get("UseNoexceptFalse", true)),
+      AddMissingNoexcept(Options.get("AddMissingNoexcept", false)) {}
 
 void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "ReplacementString", NoexceptMacro);
   Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse);
+  Options.store(Opts, "AddMissingNoexcept", AddMissingNoexcept);
 }
 
 void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
@@ -56,6 +58,14 @@
                             functionProtoType(hasDynamicExceptionSpec()))))))))
           .bind("parmVarDecl"),
       this);
+
+  if (AddMissingNoexcept)
+    Finder->addMatcher(
+        functionDecl(allOf(hasBody(stmt()),
+                           unless(anyOf(isNoThrow(), hasDynamicExceptionSpec(),
+                                        isImplicit()))))
+            .bind("potentialNoexcept"),
+        this);
 }
 
 void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
@@ -82,6 +92,14 @@
                   .IgnoreParens()
                   .castAs<FunctionProtoTypeLoc>()
                   .getExceptionSpecRange();
+  } else if (const auto *PotentialNoexcept =
+                 Result.Nodes.getNodeAs<FunctionDecl>("potentialNoexcept")) {
+    if (!Analyzer.throwsException(PotentialNoexcept) &&
+        PotentialNoexcept->getBeginLoc().isValid())
+      diag(PotentialNoexcept->getBeginLoc(),
+           "this function can not throw an exception, consider adding "
+           "'noexcept'");
+    return;
   }
   CharSourceRange CRange = Lexer::makeFileCharRange(
       CharSourceRange::getTokenRange(Range), *Result.SourceManager,
@@ -92,10 +110,10 @@
   StringRef ReplacementStr =
       IsNoThrow
           ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str()
-          : NoexceptMacro.empty()
-                ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)"
-                                                          : ""
-                : "";
+          : NoexceptMacro.empty() ? (DtorOrOperatorDel || UseNoexceptFalse)
+                                        ? "noexcept(false)"
+                                        : ""
+                                  : "";
 
   FixItHint FixIt;
   if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid())
Index: clang-tidy/bugprone/ExceptionEscapeCheck.h
===================================================================
--- clang-tidy/bugprone/ExceptionEscapeCheck.h
+++ clang-tidy/bugprone/ExceptionEscapeCheck.h
@@ -1,4 +1,4 @@
-//===--- ExceptionEscapeCheck.h - clang-tidy---------------------*- C++ -*-===//
+//===--- ExceptionEscapeCheck.h - clang-tidy --------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -10,7 +10,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
 
 #include "../ClangTidy.h"
-
+#include "../utils/ExceptionAnalyzer.h"
 #include "llvm/ADT/StringSet.h"
 
 namespace clang {
@@ -36,7 +36,7 @@
   std::string RawIgnoredExceptions;
 
   llvm::StringSet<> FunctionsThatShouldNotThrow;
-  llvm::StringSet<> IgnoredExceptions;
+  utils::ExceptionAnalyzer Tracer;
 };
 
 } // namespace bugprone
Index: clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===================================================================
--- clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -1,4 +1,4 @@
-//===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===//
+//===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -8,174 +8,41 @@
 
 #include "ExceptionEscapeCheck.h"
 
+#include "../utils/OptionsUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
-
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSet.h"
 
 using namespace clang::ast_matchers;
 
-namespace {
-typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
-} // namespace
-
 namespace clang {
-
-static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
-  const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
-  const auto *BaseClass = BaseType->getAsCXXRecordDecl();
-  if (!DerivedClass || !BaseClass)
-    return false;
-
-  return !DerivedClass->forallBases(
-      [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
-}
-
-static const TypeVec
-throwsException(const Stmt *St, const TypeVec &Caught,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
-
-static const TypeVec
-throwsException(const FunctionDecl *Func,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
-  if (CallStack.count(Func))
-    return TypeVec();
-
-  if (const Stmt *Body = Func->getBody()) {
-    CallStack.insert(Func);
-    const TypeVec Result = throwsException(Body, TypeVec(), CallStack);
-    CallStack.erase(Func);
-    return Result;
-  }
-
-  TypeVec Result;
-  if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
-    for (const QualType Ex : FPT->exceptions()) {
-      Result.push_back(Ex.getTypePtr());
-    }
-  }
-  return Result;
-}
-
-static const TypeVec
-throwsException(const Stmt *St, const TypeVec &Caught,
-                llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
-  TypeVec Results;
-
-  if (!St)
-    return Results;
-
-  if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
-    if (const auto *ThrownExpr = Throw->getSubExpr()) {
-      const auto *ThrownType =
-          ThrownExpr->getType()->getUnqualifiedDesugaredType();
-      if (ThrownType->isReferenceType()) {
-        ThrownType = ThrownType->castAs<ReferenceType>()
-                         ->getPointeeType()
-                         ->getUnqualifiedDesugaredType();
-      }
-      if (const auto *TD = ThrownType->getAsTagDecl()) {
-        if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc"
-            && TD->isInStdNamespace())
-          return Results;
-      }
-      Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
-    } else {
-      Results.append(Caught.begin(), Caught.end());
-    }
-  } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
-    TypeVec Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack);
-    for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
-      const CXXCatchStmt *Catch = Try->getHandler(i);
-      if (!Catch->getExceptionDecl()) {
-        const TypeVec Rethrown =
-            throwsException(Catch->getHandlerBlock(), Uncaught, CallStack);
-        Results.append(Rethrown.begin(), Rethrown.end());
-        Uncaught.clear();
-      } else {
-        const auto *CaughtType =
-            Catch->getCaughtType()->getUnqualifiedDesugaredType();
-        if (CaughtType->isReferenceType()) {
-          CaughtType = CaughtType->castAs<ReferenceType>()
-                           ->getPointeeType()
-                           ->getUnqualifiedDesugaredType();
-        }
-        auto NewEnd =
-            llvm::remove_if(Uncaught, [&CaughtType](const Type *ThrownType) {
-              return ThrownType == CaughtType ||
-                     isBaseOf(ThrownType, CaughtType);
-            });
-        if (NewEnd != Uncaught.end()) {
-          Uncaught.erase(NewEnd, Uncaught.end());
-          const TypeVec Rethrown = throwsException(
-              Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack);
-          Results.append(Rethrown.begin(), Rethrown.end());
-        }
-      }
-    }
-    Results.append(Uncaught.begin(), Uncaught.end());
-  } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
-    if (const FunctionDecl *Func = Call->getDirectCallee()) {
-      TypeVec Excs = throwsException(Func, CallStack);
-      Results.append(Excs.begin(), Excs.end());
-    }
-  } else {
-    for (const Stmt *Child : St->children()) {
-      TypeVec Excs = throwsException(Child, Caught, CallStack);
-      Results.append(Excs.begin(), Excs.end());
-    }
-  }
-  return Results;
-}
-
-static const TypeVec throwsException(const FunctionDecl *Func) {
-  llvm::SmallSet<const FunctionDecl *, 32> CallStack;
-  return throwsException(Func, CallStack);
-}
-
-namespace ast_matchers {
-AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
-  TypeVec ExceptionList = throwsException(&Node);
-  auto NewEnd = llvm::remove_if(
-      ExceptionList, [this, Finder, Builder](const Type *Exception) {
-        return !InnerMatcher.matches(*Exception, Finder, Builder);
-      });
-  ExceptionList.erase(NewEnd, ExceptionList.end());
-  return ExceptionList.size();
-}
-
-AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) {
-  if (const auto *TD = Node.getAsTagDecl()) {
-    if (TD->getDeclName().isIdentifier())
-      return IgnoredExceptions.count(TD->getName()) > 0;
-  }
-  return false;
-}
-
+namespace {
 AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
               FunctionsThatShouldNotThrow) {
   return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
 }
-} // namespace ast_matchers
+} // namespace
 
 namespace tidy {
 namespace bugprone {
-
 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
                                            ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
                                          "FunctionsThatShouldNotThrow", "")),
       RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
-  llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
-      IgnoredExceptionsVec;
-  StringRef(RawFunctionsThatShouldNotThrow)
-      .split(FunctionsThatShouldNotThrowVec, ",", -1, false);
+  std::vector<std::string> FunctionsThatShouldNotThrowVec =
+      utils::options::parseStringList(RawFunctionsThatShouldNotThrow);
   FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
                                      FunctionsThatShouldNotThrowVec.end());
-  StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
+
+  std::vector<std::string> IgnoredExceptionsVec =
+      utils::options::parseStringList(RawIgnoredExceptions);
+
+  llvm::StringSet<> IgnoredExceptions;
   IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
                            IgnoredExceptionsVec.end());
+  Tracer.ignoreExceptions(std::move(IgnoredExceptions));
 }
 
 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
@@ -193,22 +60,25 @@
                          cxxConstructorDecl(isMoveConstructor()),
                          cxxMethodDecl(isMoveAssignmentOperator()),
                          hasName("main"), hasName("swap"),
-                         isEnabled(FunctionsThatShouldNotThrow)),
-                   throws(unless(isIgnored(IgnoredExceptions))))
+                         isEnabled(FunctionsThatShouldNotThrow)))
           .bind("thrower"),
       this);
 }
 
 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
-  const FunctionDecl *MatchedDecl =
-      Result.Nodes.getNodeAs<FunctionDecl>("thrower");
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("thrower");
+
   if (!MatchedDecl)
     return;
 
-  // FIXME: We should provide more information about the exact location where
-  // the exception is thrown, maybe the full path the exception escapes
-  diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 "
-       "which should not throw exceptions") << MatchedDecl;
+  if (Tracer.throwsException(MatchedDecl))
+    // FIXME: We should provide more information about the exact location where
+    // the exception is thrown, maybe the full path the exception escapes
+    diag(MatchedDecl->getLocation(),
+         "an exception may be thrown in function %0 "
+
+         "which should not throw exceptions")
+        << MatchedDecl;
 }
 
 } // namespace bugprone
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to