Index: clang-tidy/safety/CMakeLists.txt =================================================================== --- clang-tidy/safety/CMakeLists.txt +++ clang-tidy/safety/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(clangTidySafetyModule NoAssemblerCheck.cpp + NoThrowInDestructorCheck.cpp SafetyTidyModule.cpp LINK_LIBS Index: clang-tidy/safety/NoThrowInDestructorCheck.h =================================================================== --- /dev/null +++ clang-tidy/safety/NoThrowInDestructorCheck.h @@ -0,0 +1,35 @@ +//===--- no-throw-in-destructorCheck.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_SAFETY_NO_THROW_IN_DESTRUCTOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SAFETY_NO_THROW_IN_DESTRUCTOR_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace safety { + +/// This checks warns for throw statements in destructors. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/safety-no-throw-in-destructor.html +class NoThrowInDestructorCheck : public ClangTidyCheck { +public: + NoThrowInDestructorCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace safety +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SAFETY_NO_THROW_IN_DESTRUCTOR_H Index: clang-tidy/safety/NoThrowInDestructorCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/safety/NoThrowInDestructorCheck.cpp @@ -0,0 +1,34 @@ +//===--- no-throw-in-destructorCheck.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 "NoThrowInDestructorCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace safety { + +void NoThrowInDestructorCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxThrowExpr(hasAncestor(cxxDestructorDecl())) + .bind("throw"), + this); +} + +void NoThrowInDestructorCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedThrow = Result.Nodes.getNodeAs("throw"); + diag(MatchedThrow->getLocStart(), "throw expression in destructor detected"); +} + +} // namespace safety +} // namespace tidy +} // namespace clang Index: clang-tidy/safety/SafetyTidyModule.cpp =================================================================== --- clang-tidy/safety/SafetyTidyModule.cpp +++ clang-tidy/safety/SafetyTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "NoAssemblerCheck.h" +#include "NoThrowInDestructorCheck.h" namespace clang { namespace tidy { @@ -21,6 +22,8 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "safety-no-assembler"); + CheckFactories.registerCheck( + "safety-no-throw-in-destructor"); } }; Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -155,4 +155,5 @@ readability-simplify-boolean-expr readability-static-definition-in-anonymous-namespace readability-uniqueptr-delete-release + safety-no-throw-in-destructor safety-no-assembler Index: docs/clang-tidy/checks/safety-no-throw-in-destructor.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/safety-no-throw-in-destructor.rst @@ -0,0 +1,8 @@ +.. title:: clang-tidy - safety-no-throw-in-destructor + +safety-no-throw-in-destructor +============================= + +This check will raise warnings for all throw expression occuring in destructors. +When an exception is handled and during stackunwinding another exception occurs, the program will crash. +This is the reason why destructors should be noexcept and never cause an exception. Index: test/clang-tidy/safety-no-throw-in-destructor.cpp =================================================================== --- /dev/null +++ test/clang-tidy/safety-no-throw-in-destructor.cpp @@ -0,0 +1,56 @@ +// RUN: %check_clang_tidy %s safety-no-throw-in-destructor %t + +namespace std { +struct runtime_error {}; +} + +struct GoodInlineDestructor { + ~GoodInlineDestructor() { + int i = 1; // Ok + } +}; + +struct GoodDestructor { + ~GoodDestructor(); +}; + +GoodDestructor::~GoodDestructor() { + int i = 1; + float j = 0.5f; + // Ok +} + +struct BadDestructor { + ~BadDestructor(); +}; + +BadDestructor::~BadDestructor() { + throw std::runtime_error(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: throw expression in destructor detected +} + +struct BadInlineDestructor { + ~BadInlineDestructor() { throw std::runtime_error(); } + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: throw expression in destructor detected +}; + +struct ComplicatedInline { + ~ComplicatedInline() { + int i = 0; + float j = 0.5f; + char string[] = "Hallo welt"; + // Ok + } +}; + +struct ComplicatedBad { + ~ComplicatedBad() { + int i = 15; + if (i == 15) + throw std::runtime_error(); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: throw expression in destructor detected + else + throw std::runtime_error(); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: throw expression in destructor detected + } +};