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 "StaticObjectExceptionCheck.h" #include "ThrownExceptionTypeCheck.h" #include "VariadicFunctionDefCheck.h" @@ -41,6 +42,8 @@ // ERR CheckFactories.registerCheck( "cert-err52-cpp"); + CheckFactories.registerCheck( + "cert-err58-cpp"); CheckFactories.registerCheck( "cert-err60-cpp"); CheckFactories.registerCheck( 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 + StaticObjectExceptionCheck.cpp ThrownExceptionTypeCheck.cpp VariadicFunctionDefCheck.cpp @@ -14,4 +15,5 @@ clangTidy clangTidyGoogleModule clangTidyMiscModule + clangTidyUtils ) Index: clang-tidy/cert/StaticObjectExceptionCheck.h =================================================================== --- clang-tidy/cert/StaticObjectExceptionCheck.h +++ clang-tidy/cert/StaticObjectExceptionCheck.h @@ -0,0 +1,35 @@ +//===--- StaticObjectExceptionCheck.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_ERR58_CPP_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// Checks whether the constructor for a static or thread_local object will +/// throw. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cert-static-object-exception.html +class StaticObjectExceptionCheck : public ClangTidyCheck { +public: + StaticObjectExceptionCheck(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_ERR58_CPP_H + Index: clang-tidy/cert/StaticObjectExceptionCheck.cpp =================================================================== --- clang-tidy/cert/StaticObjectExceptionCheck.cpp +++ clang-tidy/cert/StaticObjectExceptionCheck.cpp @@ -0,0 +1,49 @@ +//===--- StaticObjectExceptionCheck.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 "StaticObjectExceptionCheck.h" +#include "../utils/Matchers.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // Match any static or thread_local variable declaration that is initialized + // with a constructor that can throw. + Finder->addMatcher( + varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()), + hasInitializer(cxxConstructExpr(hasDeclaration( + cxxConstructorDecl(unless(matchers::isNoThrow())) + .bind("ctor"))))) + .bind("var"), + this); +} + +void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) { + const auto *VD = Result.Nodes.getNodeAs("var"); + const auto *Ctor = Result.Nodes.getNodeAs("ctor"); + + diag(VD->getLocation(), + "construction of %0 with %select{static|thread_local}1 storage " + "duration may throw an exception that cannot be caught") + << VD << (VD->getStorageDuration() == SD_Static ? 0 : 1); + diag(Ctor->getLocation(), "possibly throwing constructor declared here", + DiagnosticIDs::Note); +} + +} // namespace tidy +} // namespace clang + Index: clang-tidy/cert/ThrownExceptionTypeCheck.cpp =================================================================== --- clang-tidy/cert/ThrownExceptionTypeCheck.cpp +++ clang-tidy/cert/ThrownExceptionTypeCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ThrownExceptionTypeCheck.h" +#include "../utils/Matchers.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -14,20 +15,6 @@ using namespace clang::ast_matchers; namespace clang { -namespace { -AST_MATCHER(CXXConstructorDecl, isNoThrowCopyConstructor) { - if (!Node.isCopyConstructor()) - return false; - - const auto *FnTy = Node.getType()->getAs(); - // Assume the best for any unresolved exception specification. - if (isUnresolvedExceptionSpec(FnTy->getExceptionSpecType())) - return true; - - return FnTy->isNothrow(Node.getASTContext()); -} -} // end namespace - namespace tidy { void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) @@ -36,7 +23,7 @@ Finder->addMatcher( cxxThrowExpr( has(cxxConstructExpr(hasDeclaration(cxxConstructorDecl( - isCopyConstructor(), unless(isNoThrowCopyConstructor())))) + isCopyConstructor(), unless(matchers::isNoThrow())))) .bind("expr"))), this); } Index: clang-tidy/utils/Matchers.h =================================================================== --- clang-tidy/utils/Matchers.h +++ clang-tidy/utils/Matchers.h @@ -23,6 +23,22 @@ return IsExpensive && *IsExpensive; } +AST_MATCHER(FunctionDecl, isNoThrow) { + const auto *FnTy = Node.getType()->getAs(); + + // If the function does not have a prototype, then it is assumed to be a + // throwing function (as it would if the function did not have any exception + // specification). + if (!FnTy) + return false; + + // Assume the best for any unresolved exception specification. + if (isUnresolvedExceptionSpec(FnTy->getExceptionSpecType())) + return true; + + return FnTy->isNothrow(Node.getASTContext()); +} + } // namespace matchers } // namespace tidy } // namespace clang Index: docs/clang-tidy/checks/cert-static-object-exception.rst =================================================================== --- docs/clang-tidy/checks/cert-static-object-exception.rst +++ docs/clang-tidy/checks/cert-static-object-exception.rst @@ -0,0 +1,9 @@ +cert-err58-cpp +============== + +This check flags all static or thread_local variable declarations where the +constructor for the object may throw an exception. + +This check corresponds to the CERT C++ Coding Standard rule +`ERR58-CPP. Constructors of objects with static or thread storage duration must not throw exceptions +`_. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -1,8 +1,9 @@ List of clang-tidy Checks ========================= -.. toctree:: +.. toctree:: cert-setlongjmp + cert-static-object-exception cert-thrown-exception-type cert-variadic-function-def cppcoreguidelines-pro-bounds-array-to-pointer-decay Index: test/clang-tidy/cert-static-object-exception.cpp =================================================================== --- test/clang-tidy/cert-static-object-exception.cpp +++ test/clang-tidy/cert-static-object-exception.cpp @@ -0,0 +1,52 @@ +// RUN: %check_clang_tidy %s cert-err58-cpp %t + +struct S { + S() noexcept(false); +}; + +struct T { + T() noexcept; +}; + +struct U { + U() {} +}; + +struct V { + explicit V(const char *) {} // Can throw +}; + + +S s; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp] +// CHECK-MESSAGES: 4:3: note: possibly throwing constructor declared here +T t; // ok +U u; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 'u' with static storage duration may throw an exception that cannot be caught +// CHECK-MESSAGES: 12:3: note: possibly throwing constructor declared here +V v("v"); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 'v' with static storage duration may throw an exception that cannot be caught +// CHECK-MESSAGES: 16:12: note: possibly throwing constructor declared here + +void f(S s1, T t1, U u1, V v1) { // ok, ok, ok, ok + S s2; // ok + T t2; // ok + U u2; // ok + V v2("v"); // ok + + thread_local S s3; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 's3' with thread_local storage duration may throw an exception that cannot be caught + thread_local T t3; // ok + thread_local U u3; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 'u3' with thread_local storage duration may throw an exception that cannot be caught + thread_local V v3("v"); + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 'v3' with thread_local storage duration may throw an exception that cannot be caught + + static S s4; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 's4' with static storage duration may throw an exception that cannot be caught + static T t4; // ok + static U u4; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 'u4' with static storage duration may throw an exception that cannot be caught + static V v4("v"); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 'v4' with static storage duration may throw an exception that cannot be caught +}