[clang-tools-extra] r336997 - [clang-tidy] Exception Escape Checker

Adam Balogh via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 13 06:09:40 PDT 2018


Author: baloghadamsoftware
Date: Fri Jul 13 06:09:40 2018
New Revision: 336997

URL: http://llvm.org/viewvc/llvm-project?rev=336997&view=rev
Log:
[clang-tidy] Exception Escape Checker

Finds functions which may throw an exception directly or indirectly, but they
should not: Destructors, move constructors, move assignment operators, the
main() function, swap() functions, functions marked with throw() or noexcept
and functions given as option to the checker.

Differential Revision: https://reviews.llvm.org/D33537


Added:
    clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
    clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-exception-escape.rst
    clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst

Modified: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp?rev=336997&r1=336996&r2=336997&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp Fri Jul 13 06:09:40 2018
@@ -16,6 +16,7 @@
 #include "BoolPointerImplicitConversionCheck.h"
 #include "CopyConstructorInitCheck.h"
 #include "DanglingHandleCheck.h"
+#include "ExceptionEscapeCheck.h"
 #include "FoldInitTypeCheck.h"
 #include "ForwardDeclarationNamespaceCheck.h"
 #include "ForwardingReferenceOverloadCheck.h"
@@ -67,6 +68,8 @@ public:
         "bugprone-copy-constructor-init");
     CheckFactories.registerCheck<DanglingHandleCheck>(
         "bugprone-dangling-handle");
+    CheckFactories.registerCheck<ExceptionEscapeCheck>(
+        "bugprone-exception-escape");
     CheckFactories.registerCheck<FoldInitTypeCheck>(
         "bugprone-fold-init-type");
     CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(

Modified: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt?rev=336997&r1=336996&r2=336997&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt Fri Jul 13 06:09:40 2018
@@ -7,6 +7,7 @@ add_clang_library(clangTidyBugproneModul
   BugproneTidyModule.cpp
   CopyConstructorInitCheck.cpp
   DanglingHandleCheck.cpp
+  ExceptionEscapeCheck.cpp
   FoldInitTypeCheck.cpp
   ForwardDeclarationNamespaceCheck.cpp
   ForwardingReferenceOverloadCheck.cpp

Added: clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.cpp?rev=336997&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.cpp Fri Jul 13 06:09:40 2018
@@ -0,0 +1,214 @@
+//===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExceptionEscapeCheck.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;
+}
+
+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) {
+  Finder->addMatcher(
+      functionDecl(allOf(throws(unless(isIgnored(IgnoredExceptions))),
+                         anyOf(isNoThrow(), cxxDestructorDecl(),
+                               cxxConstructorDecl(isMoveConstructor()),
+                               cxxMethodDecl(isMoveAssignmentOperator()),
+                               hasName("main"), hasName("swap"),
+                               isEnabled(FunctionsThatShouldNotThrow))))
+          .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 tidy
+} // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.h?rev=336997&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/ExceptionEscapeCheck.h Fri Jul 13 06:09:40 2018
@@ -0,0 +1,47 @@
+//===--- ExceptionEscapeCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/StringSet.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds functions which should not throw exceptions: Destructors, move
+/// constructors, move assignment operators, the main() function,
+/// swap() functions, functions marked with throw() or noexcept and functions
+/// given as option to the checker.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-exception-escape.html
+class ExceptionEscapeCheck : public ClangTidyCheck {
+public:
+  ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::string RawFunctionsThatShouldNotThrow;
+  std::string RawIgnoredExceptions;
+
+  llvm::StringSet<> FunctionsThatShouldNotThrow;
+  llvm::StringSet<> IgnoredExceptions;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=336997&r1=336996&r2=336997&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Jul 13 06:09:40 2018
@@ -79,6 +79,9 @@ Improvements to clang-tidy
   Diagnoses comparisons that appear to be incorrectly placed in the argument to
   the ``TEMP_FAILURE_RETRY`` macro.
 
+- New :doc:`bugprone-exception-escape
+  <clang-tidy/checks/bugprone-exception-escape>` check
+
 - New :doc:`bugprone-parent-virtual-call
   <clang-tidy/checks/bugprone-parent-virtual-call>` check.
 

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-exception-escape.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-exception-escape.rst?rev=336997&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-exception-escape.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-exception-escape.rst Fri Jul 13 06:09:40 2018
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - bugprone-exception-escape
+
+bugprone-exception-escape
+=========================
+
+Finds functions which may throw an exception directly or indirectly, but they
+should not. The functions which should not throw exceptions are the following:
+* Destructors
+* Move constructors
+* Move assignment operators
+* The ``main()`` functions
+* ``swap()`` functions
+* Functions marked with ``throw()`` or ``noexcept``
+* Other functions given as option
+
+A destructor throwing an exception may result in undefined behavior, resource
+leaks or unexpected termination of the program. Throwing move constructor or
+move assignment also may result in undefined behavior or resource leak. The
+``swap()`` operations expected to be non throwing most of the cases and they
+are always possible to implement in a non throwing way. Non throwing ``swap()``
+operations are also used to create move operations. A throwing ``main()``
+function also results in unexpected termination.
+
+Options
+-------
+
+.. option:: FunctionsThatShouldNotThrow
+
+   Comma 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
+   exceptions in the check. Default value is an empty string.

Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=336997&r1=336996&r2=336997&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Jul 13 06:09:40 2018
@@ -24,6 +24,7 @@ Clang-Tidy Checks
    bugprone-bool-pointer-implicit-conversion
    bugprone-copy-constructor-init
    bugprone-dangling-handle
+   bugprone-exception-escape
    bugprone-fold-init-type
    bugprone-forward-declaration-namespace
    bugprone-forwarding-reference-overload

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape.cpp?rev=336997&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-exception-escape.cpp Fri Jul 13 06:09:40 2018
@@ -0,0 +1,265 @@
+// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-std=c++11 -config="{CheckOptions: [{key: bugprone-exception-escape.IgnoredExceptions, value: 'ignored1,ignored2'}, {key: bugprone-exception-escape.FunctionsThatShouldNotThrow, value: 'enabled1,enabled2,enabled3'}]}" --
+
+struct throwing_destructor {
+  ~throwing_destructor() {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function '~throwing_destructor' which should not throw exceptions
+    throw 1;
+  }
+};
+
+struct throwing_move_constructor {
+  throwing_move_constructor(throwing_move_constructor&&) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'throwing_move_constructor' which should not throw exceptions
+    throw 1;
+  }
+};
+
+struct throwing_move_assignment {
+  throwing_move_assignment& operator=(throwing_move_assignment&&) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: an exception may be thrown in function 'operator=' which should not throw exceptions
+    throw 1;
+  }
+};
+
+void throwing_noexcept() noexcept {
+    // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions
+  throw 1;
+}
+
+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 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 {
+    throw 1;
+  } catch(int &) {
+  }
+}
+
+void throw_and_catch_some(int n) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch_some' which should not throw exceptions
+  try {
+    if (n) throw 1;
+    throw 1.1;
+  } catch(int &) {
+  }
+}
+
+void throw_and_catch_each(int n) noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch_each' which should not throw exceptions
+  try {
+    if (n) throw 1;
+    throw 1.1;
+  } catch(int &) {
+  } catch(double &) {
+  }
+}
+
+void throw_and_catch_all(int n) noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_catch_all' which should not throw exceptions
+  try {
+    if (n) throw 1;
+    throw 1.1;
+  } catch(...) {
+  }
+}
+
+void throw_and_rethrow() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_and_rethrow' which should not throw exceptions
+  try {
+    throw 1;
+  } catch(int &) {
+    throw;
+  }
+}
+
+void throw_catch_throw() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_throw' which should not throw exceptions
+  try {
+    throw 1;
+  } catch(int &) {
+    throw 2;
+  }
+}
+
+void throw_catch_rethrow_the_rest(int n) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_rethrow_the_rest' which should not throw exceptions
+  try {
+    if (n) throw 1;
+    throw 1.1;
+  } catch(int &) {
+  } catch(...) {
+    throw;
+  }
+}
+
+class base {};
+class derived: public base {};
+
+void throw_derived_catch_base() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base' which should not throw exceptions
+  try {
+    throw derived();
+  } catch(base &) {
+  }
+}
+
+void try_nested_try(int n) noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
+  try {
+    try {
+      if (n) throw 1;
+      throw 1.1;
+    } catch(int &) {
+    }
+  } catch(double &) {
+  }
+}
+
+void bad_try_nested_try(int n) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_try_nested_try' which should not throw exceptions
+  try {
+    if (n) throw 1;
+    try {
+      throw 1.1;
+    } catch(int &) {
+    }
+  } catch(double &) {
+  }
+}
+
+void try_nested_catch() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_catch' which should not throw exceptions
+  try {
+    try {
+      throw 1;
+    } catch(int &) {
+      throw 1.1;
+    }
+  } catch(double &) {
+  }
+}
+
+void catch_nested_try() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'catch_nested_try' which should not throw exceptions
+  try {
+    throw 1;
+  } catch(int &) {
+    try {
+      throw 1;
+    } catch(int &) {
+    }
+  }
+}
+
+void bad_catch_nested_try() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'bad_catch_nested_try' which should not throw exceptions
+  try {
+    throw 1;
+  } catch(int &) {
+    try {
+      throw 1.1;
+    } catch(int &) {
+    }
+  } catch(double &) {
+  }
+}
+
+void implicit_int_thrower() {
+  throw 1;
+}
+
+void explicit_int_thrower() throw(int);
+
+void indirect_implicit() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_implicit' which should not throw exceptions
+  implicit_int_thrower();
+}
+
+void indirect_explicit() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_explicit' which should not throw exceptions
+  explicit_int_thrower();
+}
+
+void indirect_catch() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_catch' which should not throw exceptions
+  try {
+    implicit_int_thrower();
+  } catch(int&) {
+  }
+}
+
+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;
+}
+
+void swap(int&, int&) {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'swap' which should not throw exceptions
+  throw 1;
+}
+
+namespace std {
+class bad_alloc {};
+}
+
+void alloc() {
+  throw std::bad_alloc();
+}
+
+void allocator() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'allocator' which should not throw exceptions
+  alloc();
+}
+
+void enabled1() {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled1' which should not throw exceptions
+  throw 1;
+}
+
+void enabled2() {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled2' which should not throw exceptions
+  enabled1();
+}
+
+void enabled3() {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'enabled3' which should not throw exceptions
+  try {
+    enabled1();
+  } catch(...) {
+  }
+}
+
+class ignored1 {};
+class ignored2 {};
+
+void this_does_not_count() noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'this_does_not_count' which should not throw exceptions
+  throw ignored1();
+}
+
+void this_does_not_count_either(int n) noexcept {
+  // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'this_does_not_count_either' which should not throw exceptions
+  try {
+    throw 1;
+    if (n) throw ignored2();
+  } catch(int &) {
+  }
+}
+
+void this_counts(int n) noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'this_counts' which should not throw exceptions
+  if (n) throw 1;
+  throw ignored1();
+}
+
+int main() {
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'main' which should not throw exceptions
+  throw 1;
+  return 0;
+}




More information about the cfe-commits mailing list