diff options
7 files changed, 210 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index a943edf93b5..a79774499eb 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/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 @@ public: // ERR CheckFactories.registerCheck<SetLongJmpCheck>( "cert-err52-cpp"); + CheckFactories.registerCheck<ThrownExceptionTypeCheck>( + "cert-err60-cpp"); CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>( "cert-err61-cpp"); diff --git a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt index cf252244855..8b61ed493a9 100644 --- a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyCERTModule CERTTidyModule.cpp SetLongJmpCheck.cpp + ThrownExceptionTypeCheck.cpp VariadicFunctionDefCheck.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/cert/ThrownExceptionTypeCheck.cpp b/clang-tools-extra/clang-tidy/cert/ThrownExceptionTypeCheck.cpp new file mode 100644 index 00000000000..260f9a1c313 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cert/ThrownExceptionTypeCheck.cpp @@ -0,0 +1,50 @@ +//===--- 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, isNoThrowCopyConstructor) { + if (!Node.isCopyConstructor()) + return false; + + if (const auto *FnTy = Node.getType()->getAs<FunctionProtoType>()) + return FnTy->isNothrow(Node.getASTContext()); + llvm_unreachable("Copy constructor with no prototype"); +} +} // end namespace + +namespace tidy { +void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + cxxThrowExpr( + has(cxxConstructExpr(hasDeclaration(cxxConstructorDecl( + isCopyConstructor(), unless(isNoThrowCopyConstructor())))) + .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 + diff --git a/clang-tools-extra/clang-tidy/cert/ThrownExceptionTypeCheck.h b/clang-tools-extra/clang-tidy/cert/ThrownExceptionTypeCheck.h new file mode 100644 index 00000000000..e4c27f3552c --- /dev/null +++ b/clang-tools-extra/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 + diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-thrown-exception-type.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-thrown-exception-type.rst new file mode 100644 index 00000000000..ef05e4f9f1b --- /dev/null +++ b/clang-tools-extra/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>`_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b492b927c31..49117797689 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -3,6 +3,7 @@ List of clang-tidy Checks .. toctree:: cert-setlongjmp + cert-thrown-exception-type cert-variadic-function-def cppcoreguidelines-pro-bounds-array-to-pointer-decay cppcoreguidelines-pro-bounds-pointer-arithmetic diff --git a/clang-tools-extra/test/clang-tidy/cert-throw-exception-type.cpp b/clang-tools-extra/test/clang-tidy/cert-throw-exception-type.cpp new file mode 100644 index 00000000000..c2ae171be58 --- /dev/null +++ b/clang-tools-extra/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 + +} |