aaron.ballman updated this revision to Diff 40078.
aaron.ballman marked an inline comment as done.
aaron.ballman added a comment.

Addressing review comments.


http://reviews.llvm.org/D14619

Files:
  clang-tidy/cert/CERTTidyModule.cpp
  clang-tidy/cert/CMakeLists.txt
  clang-tidy/cert/ThrownExceptionTypeCheck.cpp
  clang-tidy/cert/ThrownExceptionTypeCheck.h
  docs/clang-tidy/checks/cert-thrown-exception-type.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/cert-throw-exception-type.cpp

Index: test/clang-tidy/cert-throw-exception-type.cpp
===================================================================
--- test/clang-tidy/cert-throw-exception-type.cpp
+++ test/clang-tidy/cert-throw-exception-type.cpp
@@ -0,0 +1,112 @@
+// RUN: %check_clang_tidy %s cert-err60-cpp %t -- -- -std=c++11 -fcxx-exceptions
+
+struct S {};
+struct T : S {};
+struct U {
+  U() = default;
+  U(const U&) = default;
+};
+
+struct V {
+  V() = default;
+  V(const V&) noexcept;
+};
+
+struct W {
+  W() = default;
+  W(const W&) noexcept(false);
+};
+
+struct X {
+  X() = default;
+  X(const X&) {}
+};
+
+struct Y {
+  Y() = default;
+  Y(const Y&) throw();
+};
+
+struct Z {
+  Z() = default;
+  Z(const Z&) throw(int);
+};
+
+void g() noexcept(false);
+
+struct A {
+  A() = default;
+  A(const A&) noexcept(noexcept(g()));
+};
+
+struct B {
+  B() = default;
+  B(const B&) = default;
+  B(const A&) noexcept(false);
+};
+
+class C {
+  W M; // W is not no-throw copy constructible
+public:
+  C() = default;
+  C(const C&) = default;
+};
+
+struct D {
+  D() = default;
+  D(const D&) noexcept(false);
+  D(D&) noexcept(true);
+};
+
+struct E {
+  E() = default;
+  E(E&) noexcept(true);
+  E(const E&) noexcept(false);
+};
+
+struct Allocates {
+  int *x;
+  Allocates() : x(new int(0)) {}
+  Allocates(const Allocates &other) : x(new int(*other.x)) {}
+};
+
+struct OptionallyAllocates {
+  int *x;
+  OptionallyAllocates() : x(new int(0)) {}
+  OptionallyAllocates(const Allocates &other) noexcept(true) {
+    try {
+      x = new int(*other.x);
+    } catch (...) {
+      x = nullptr;
+    }
+  }
+};
+
+void f() {
+  throw 12; // ok
+  throw "test"; // ok
+  throw S(); // ok
+  throw T(); // ok
+  throw U(); // ok
+  throw V(); // ok
+  throw W(); // match, noexcept(false)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible [cert-err60-cpp]
+  throw X(); // match, no noexcept clause, nontrivial
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw Y(); // ok
+  throw Z(); // match, throw(int)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw A(); // match, noexcept(false)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw B(); // ok
+  throw C(); // match, C has a member variable that makes it throwing on copy
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw D(); // match, has throwing copy constructor
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw E(); // match, has throwing copy constructor
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw Allocates(); // match, copy constructor throws
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw OptionallyAllocates(); // ok
+
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -3,6 +3,7 @@
 
 .. toctree::
    cert-setlongjmp
+   cert-thrown-exception-type
    cert-variadic-function-def
    cppcoreguidelines-pro-bounds-array-to-pointer-decay
    cppcoreguidelines-pro-bounds-pointer-arithmetic
Index: docs/clang-tidy/checks/cert-thrown-exception-type.rst
===================================================================
--- docs/clang-tidy/checks/cert-thrown-exception-type.rst
+++ docs/clang-tidy/checks/cert-thrown-exception-type.rst
@@ -0,0 +1,9 @@
+cert-err60-cpp
+==============
+
+This check flags all throw expressions where the exception object is not nothrow
+copy constructible.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR60-CPP. Exception objects must be nothrow copy constructible
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible>`_.
Index: clang-tidy/cert/ThrownExceptionTypeCheck.h
===================================================================
--- clang-tidy/cert/ThrownExceptionTypeCheck.h
+++ clang-tidy/cert/ThrownExceptionTypeCheck.h
@@ -0,0 +1,34 @@
+//===--- ThrownExceptionTypeCheck.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_CERT_THROWNEXCEPTIONTYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+
+/// Checks whether a thrown object is nothrow copy constructible.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-thrown-exception-type.html
+class ThrownExceptionTypeCheck : public ClangTidyCheck {
+public:
+  ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
+
Index: clang-tidy/cert/ThrownExceptionTypeCheck.cpp
===================================================================
--- clang-tidy/cert/ThrownExceptionTypeCheck.cpp
+++ clang-tidy/cert/ThrownExceptionTypeCheck.cpp
@@ -0,0 +1,60 @@
+//===--- ThrownExceptionTypeCheck.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 "ThrownExceptionTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+AST_MATCHER(CXXConstructorDecl, isNoThrowCopyConstructible) {
+  if (!Node.isCopyConstructor())
+    return false;
+
+  if (Node.isTrivial())
+    return true;
+
+  if (const auto *FnTy = Node.getType()->getAs<FunctionProtoType>()) {
+    // If any of the copy constructors is throwing, we will assume that it
+    // is plausible that the constructor may be selected by the throw and
+    // diagnose. TODO: we could work a lot harder to only consider the
+    // copy constructor selected by the throw expression and diagnose only
+    // if that one is nonthrowing. However, the FP rate from this is likely
+    // to be quite low.
+    return FnTy->isNothrow(Node.getASTContext());
+  }
+  return true;
+}
+} // end namespace
+
+namespace tidy {
+void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cxxThrowExpr(
+          has(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
+              isCopyConstructor(), unless(isNoThrowCopyConstructible()))))
+          .bind("expr"))),
+      this);
+
+}
+
+void ThrownExceptionTypeCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+  diag(E->getExprLoc(),
+       "thrown exception type is not nothrow copy constructible");
+}
+
+} // namespace tidy
+} // namespace clang
+
Index: clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tidy/cert/CMakeLists.txt
+++ clang-tidy/cert/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangTidyCERTModule
   CERTTidyModule.cpp
   SetLongJmpCheck.cpp
+  ThrownExceptionTypeCheck.cpp
   VariadicFunctionDefCheck.cpp
 
   LINK_LIBS
Index: clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tidy/cert/CERTTidyModule.cpp
+++ clang-tidy/cert/CERTTidyModule.cpp
@@ -17,6 +17,7 @@
 #include "../misc/StaticAssertCheck.h"
 #include "../misc/ThrowByValueCatchByReferenceCheck.h"
 #include "SetLongJmpCheck.h"
+#include "ThrownExceptionTypeCheck.h"
 #include "VariadicFunctionDefCheck.h"
 
 namespace clang {
@@ -40,6 +41,8 @@
     // ERR
     CheckFactories.registerCheck<SetLongJmpCheck>(
         "cert-err52-cpp");
+    CheckFactories.registerCheck<ThrownExceptionTypeCheck>(
+        "cert-err60-cpp");
     CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
         "cert-err61-cpp");
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to