baloghadamsoftware updated this revision to Diff 100382.
baloghadamsoftware added a comment.
Ignoring bad_alloc.
https://reviews.llvm.org/D33537
Files:
clang-tidy/misc/CMakeLists.txt
clang-tidy/misc/ExceptionEscapeCheck.cpp
clang-tidy/misc/ExceptionEscapeCheck.h
clang-tidy/misc/MiscTidyModule.cpp
docs/ReleaseNotes.rst
docs/clang-tidy/checks/list.rst
docs/clang-tidy/checks/misc-exception-escape.rst
test/clang-tidy/misc-exception-escape.cpp
Index: test/clang-tidy/misc-exception-escape.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-exception-escape.cpp
@@ -0,0 +1,256 @@
+// RUN: %check_clang_tidy %s misc-exception-escape %t -- -extra-arg=-std=c++11 -config="{CheckOptions: [{key: misc-exception-escape.IgnoredExceptions, value: 'ignored1,ignored2'}, {key: misc-exception-escape.EnabledFunctions, value: 'enabled1,enabled2,enabled3'}]}" --
+
+struct throwing_destructor {
+ ~throwing_destructor() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function '~throwing_destructor' throws
+ throw 1;
+ }
+};
+
+struct throwing_move_constructor {
+ throwing_move_constructor(throwing_move_constructor&&) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'throwing_move_constructor' throws
+ throw 1;
+ }
+};
+
+struct throwing_move_assignment {
+ throwing_move_assignment& operator=(throwing_move_assignment&&) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: function 'operator=' throws
+ throw 1;
+ }
+};
+
+void throwing_noexcept() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throwing_noexcept' throws
+ throw 1;
+}
+
+void throwing_throw_nothing() throw() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throwing_throw_nothing' throws
+ throw 1;
+}
+
+void throw_and_catch() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'throw_and_catch' throws
+ try {
+ throw 1;
+ } catch(int &) {
+ }
+}
+
+void throw_and_catch_some() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throw_and_catch_some' throws
+ try {
+ throw 1;
+ throw 1.1;
+ } catch(int &) {
+ }
+}
+
+void throw_and_catch_each() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'throw_and_catch_each' throws
+ try {
+ throw 1;
+ throw 1.1;
+ } catch(int &) {
+ } catch(double &) {
+ }
+}
+
+void throw_and_catch_all() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'throw_and_catch_all' throws
+ try {
+ throw 1;
+ throw 1.1;
+ } catch(...) {
+ }
+}
+
+void throw_and_rethrow() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throw_and_rethrow' throws
+ try {
+ throw 1;
+ } catch(int &) {
+ throw;
+ }
+}
+
+void throw_catch_throw() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throw_catch_throw' throws
+ try {
+ throw 1;
+ } catch(int &) {
+ throw 2;
+ }
+}
+
+void throw_catch_rethrow_the_rest() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'throw_catch_rethrow_the_rest' throws
+ try {
+ 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: function 'throw_derived_catch_base' throws
+ try {
+ throw derived();
+ } catch(base &) {
+ }
+}
+
+void try_nested_try() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'try_nested_try' throws
+ try {
+ try {
+ throw 1;
+ throw 1.1;
+ } catch(int &) {
+ }
+ } catch(double &) {
+ }
+}
+
+void bad_try_nested_try() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bad_try_nested_try' throws
+ try {
+ throw 1;
+ try {
+ throw 1.1;
+ } catch(int &) {
+ }
+ } catch(double &) {
+ }
+}
+
+void try_nested_catch() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'try_nested_catch' throws
+ try {
+ try {
+ throw 1;
+ } catch(int &) {
+ throw 1.1;
+ }
+ } catch(double &) {
+ }
+}
+
+void catch_nested_try() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'catch_nested_try' throws
+ try {
+ throw 1;
+ } catch(int &) {
+ try {
+ throw 1;
+ } catch(int &) {
+ }
+ }
+}
+
+void bad_catch_nested_try() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bad_catch_nested_try' throws
+ 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: function 'indirect_implicit' throws
+ implicit_int_thrower();
+}
+
+void indirect_explicit() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'indirect_explicit' throws
+ explicit_int_thrower();
+}
+
+void indirect_catch() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'indirect_catch' throws
+ try {
+ implicit_int_thrower();
+ } catch(int&) {
+ }
+}
+
+void swap(int&, int&) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'swap' throws
+ throw 1;
+}
+
+class bad_alloc {};
+
+void alloc() {
+ throw bad_alloc();
+}
+
+void allocator() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'allocator' throws
+ alloc();
+}
+
+void enabled1() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'enabled1' throws
+ throw 1;
+}
+
+void enabled2() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'enabled2' throws
+ enabled1();
+}
+
+void enabled3() {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'enabled3' throws
+ try {
+ enabled1();
+ } catch(...) {
+ }
+}
+
+class ignored1 {};
+class ignored2 {};
+
+void this_does_not_count() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'this_does_not_count' throws
+ throw ignored1();
+}
+
+void this_does_not_count_either() noexcept {
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: function 'this_does_not_count_either' throws
+ try {
+ throw 1;
+ throw ignored2();
+ } catch(int &) {
+ }
+}
+
+void this_counts() noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'this_counts' throws
+ throw 1;
+ throw ignored1();
+}
+
+int main() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'main' throws
+ throw 1;
+ return 0;
+}
Index: docs/clang-tidy/checks/misc-exception-escape.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-exception-escape.rst
@@ -0,0 +1,35 @@
+.. title:: clang-tidy - misc-exception-escape
+
+misc-exception-escape
+=====================
+
+Finds functions which should not throw exceptions:
+* Destructors
+* Copy constructors
+* Copy 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:: EnabledFunctions
+
+ Comma separated list containing function names which should not throw. An
+ example for using this parameter is the function ``WinMain()`` in the
+ Windows API. Default vale is empty string.
+
+.. option:: IgnoredExceptions
+
+ Comma separated list containing type names which are not counted as thrown
+ exceptions in the check. Default value is empty string.
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -76,6 +76,7 @@
misc-bool-pointer-implicit-conversion
misc-dangling-handle
misc-definitions-in-headers
+ misc-exception-escape
misc-fold-init-type
misc-forward-declaration-namespace
misc-forwarding-reference-overload
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -120,6 +120,10 @@
Adds checks that implement the `High Integrity C++ Coding Standard <http://www.codingstandard.com/section/index/>`_ and other safety
standards. Many checks are aliased to other modules.
+- New `misc-exception-escape
+ <http://clang.llvm.org/extra/clang-tidy/checks/misc-exception-escape.html>`_ check
+
+
Improvements to include-fixer
-----------------------------
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -15,6 +15,7 @@
#include "BoolPointerImplicitConversionCheck.h"
#include "DanglingHandleCheck.h"
#include "DefinitionsInHeadersCheck.h"
+#include "ExceptionEscapeCheck.h"
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
#include "ForwardingReferenceOverloadCheck.h"
@@ -66,6 +67,8 @@
CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
CheckFactories.registerCheck<AssertSideEffectCheck>(
"misc-assert-side-effect");
+ CheckFactories.registerCheck<ExceptionEscapeCheck>(
+ "misc-exception-escape");
CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
"misc-forwarding-reference-overload");
CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
Index: clang-tidy/misc/ExceptionEscapeCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/ExceptionEscapeCheck.h
@@ -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_MISC_EXCEPTION_ESCAPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_EXCEPTION_ESCAPE_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/SmallSet.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// 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/misc-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 RawEnabledFunctions;
+ std::string RawIgnoredExceptions;
+
+ llvm::SmallSet<StringRef, 8> EnabledFunctions;
+ llvm::SmallSet<StringRef, 8> IgnoredExceptions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_EXCEPTION_ESCAPE_H
Index: clang-tidy/misc/ExceptionEscapeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/ExceptionEscapeCheck.cpp
@@ -0,0 +1,203 @@
+//===--- 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"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace {
+typedef llvm::SmallVector<const Type *, 8> TypeVec;
+typedef llvm::SmallSet<StringRef, 8> StringSet;
+
+const TypeVec _throws(const FunctionDecl *Func);
+const TypeVec _throws(const Stmt *St, const TypeVec &Caught);
+} // namespace
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
+ auto ExceptionList = _throws(&Node);
+ auto *NewEnd = std::remove_if(ExceptionList.begin(), ExceptionList.end(),
+ [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, StringSet, IgnoredExceptions) {
+ if (const auto *TD = Node.getAsTagDecl()) {
+ return IgnoredExceptions.count(TD->getNameAsString()) > 0;
+ }
+ return false;
+}
+
+AST_MATCHER_P(FunctionDecl, isEnabled, StringSet, EnabledFunctions) {
+ return EnabledFunctions.count(Node.getNameAsString()) > 0;
+ return false;
+}
+} // namespace ast_matchers
+
+namespace tidy {
+namespace misc {
+
+ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ RawEnabledFunctions(Options.get("EnabledFunctions", "")),
+ RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
+ llvm::SmallVector<StringRef, 8> EnabledFunctionsVec, IgnoredExceptionsVec;
+ StringRef(RawEnabledFunctions).split(EnabledFunctionsVec, ",", -1, false);
+ EnabledFunctions.insert(EnabledFunctionsVec.begin(),
+ EnabledFunctionsVec.end());
+ StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
+ IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
+ IgnoredExceptionsVec.end());
+}
+
+void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "EnabledFunctions", RawEnabledFunctions);
+ 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(EnabledFunctions))))
+ .bind("thrower"),
+ this);
+}
+
+void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("thrower");
+ if (!MatchedDecl)
+ return;
+
+ diag(MatchedDecl->getLocation(), "function %0 throws") << MatchedDecl;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+namespace {
+
+bool isBaseOf(const Type *DerivedType, const Type *BaseType);
+
+const TypeVec _throws(const FunctionDecl *Func) {
+ static thread_local llvm::SmallSet<const FunctionDecl *, 32> CallStack;
+
+ if (CallStack.count(Func))
+ return TypeVec();
+
+ if (const auto *Body = Func->getBody()) {
+ CallStack.insert(Func);
+ const auto Result = _throws(Body, TypeVec());
+ CallStack.erase(Func);
+ return Result;
+ } else {
+ TypeVec Result;
+ if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
+ for (const auto Ex : FPT->exceptions()) {
+ Result.push_back(&*Ex);
+ }
+ }
+ return Result;
+ }
+}
+
+const TypeVec _throws(const Stmt *St, const TypeVec &Caught) {
+ 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->getNameAsString() == "bad_alloc")
+ return Results;
+ }
+ Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
+ } else {
+ Results.append(Caught.begin(), Caught.end());
+ }
+ } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
+ auto Uncaught = _throws(Try->getTryBlock(), Caught);
+ for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
+ const auto *Catch = Try->getHandler(i);
+ if (!Catch->getExceptionDecl()) {
+ const auto Rethrown = _throws(Catch->getHandlerBlock(), Uncaught);
+ 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 = std::remove_if(Uncaught.begin(), Uncaught.end(),
+ [&CaughtType](const Type *ThrownType) {
+ return ThrownType == CaughtType ||
+ isBaseOf(ThrownType, CaughtType);
+ });
+ if (NewEnd != Uncaught.end()) {
+ const auto Rethrown =
+ _throws(Catch->getHandlerBlock(), TypeVec(1, CaughtType));
+ Results.append(Rethrown.begin(), Rethrown.end());
+ Uncaught.erase(NewEnd, Uncaught.end());
+ }
+ }
+ }
+ Results.append(Uncaught.begin(), Uncaught.end());
+ } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
+ if (const auto *Func = Call->getDirectCallee()) {
+ auto Excs = _throws(Func);
+ Results.append(Excs.begin(), Excs.end());
+ }
+ } else {
+ for (const auto *Child : St->children()) {
+ auto Excs = _throws(Child, Caught);
+ Results.append(Excs.begin(), Excs.end());
+ }
+ }
+ return Results;
+}
+
+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; });
+}
+
+} // namespace
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -3,6 +3,7 @@
add_clang_library(clangTidyMiscModule
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
+ ExceptionEscapeCheck.cpp
ForwardingReferenceOverloadCheck.cpp
MisplacedConstCheck.cpp
UnconventionalAssignOperatorCheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits