summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy
diff options
context:
space:
mode:
authorAdam Balogh <adam.balogh@ericsson.com>2018-07-13 13:09:40 +0000
committerAdam Balogh <adam.balogh@ericsson.com>2018-07-13 13:09:40 +0000
commite0e5b4cf2e8e2a878fb3d75c32bfda8905431de6 (patch)
treef0e488e7affa04fc93fcc0b18df91162265d5294 /clang-tools-extra/clang-tidy
parentd47bde0d7a8bff7ef1526cc13b05b1d633550e32 (diff)
downloadbcm5719-llvm-e0e5b4cf2e8e2a878fb3d75c32bfda8905431de6.tar.gz
bcm5719-llvm-e0e5b4cf2e8e2a878fb3d75c32bfda8905431de6.zip
[clang-tidy] Exception Escape Checker
Finds functions which may throw an exception directly or indirectly, but they should not: 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. Differential Revision: https://reviews.llvm.org/D33537 llvm-svn: 336997
Diffstat (limited to 'clang-tools-extra/clang-tidy')
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp3
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp214
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.h47
4 files changed, 265 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 86eb9d1ba5e..09a252b30f1 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -16,6 +16,7 @@
#include "BoolPointerImplicitConversionCheck.h"
#include "CopyConstructorInitCheck.h"
#include "DanglingHandleCheck.h"
+#include "ExceptionEscapeCheck.h"
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
#include "ForwardingReferenceOverloadCheck.h"
@@ -67,6 +68,8 @@ public:
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
+ CheckFactories.registerCheck<ExceptionEscapeCheck>(
+ "bugprone-exception-escape");
CheckFactories.registerCheck<FoldInitTypeCheck>(
"bugprone-fold-init-type");
CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 3cce01312ec..b20997cc40b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_library(clangTidyBugproneModule
BugproneTidyModule.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
+ ExceptionEscapeCheck.cpp
FoldInitTypeCheck.cpp
ForwardDeclarationNamespaceCheck.cpp
ForwardingReferenceOverloadCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
new file mode 100644
index 00000000000..a5e54c0a182
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -0,0 +1,214 @@
+//===--- 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"
+#include "llvm/ADT/StringSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace {
+typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
+} // namespace
+
+namespace clang {
+
+static 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; });
+}
+
+static const TypeVec
+throwsException(const Stmt *St, const TypeVec &Caught,
+ llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
+
+static const TypeVec
+throwsException(const FunctionDecl *Func,
+ llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+ if (CallStack.count(Func))
+ return TypeVec();
+
+ if (const Stmt *Body = Func->getBody()) {
+ CallStack.insert(Func);
+ const TypeVec Result = throwsException(Body, TypeVec(), CallStack);
+ CallStack.erase(Func);
+ return Result;
+ }
+
+ TypeVec Result;
+ if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
+ for (const QualType Ex : FPT->exceptions()) {
+ Result.push_back(Ex.getTypePtr());
+ }
+ }
+ return Result;
+}
+
+static const TypeVec
+throwsException(const Stmt *St, const TypeVec &Caught,
+ llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
+ 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->getDeclName().isIdentifier() && TD->getName() == "bad_alloc"
+ && TD->isInStdNamespace())
+ return Results;
+ }
+ Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
+ } else {
+ Results.append(Caught.begin(), Caught.end());
+ }
+ } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
+ TypeVec Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack);
+ for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
+ const CXXCatchStmt *Catch = Try->getHandler(i);
+ if (!Catch->getExceptionDecl()) {
+ const TypeVec Rethrown =
+ throwsException(Catch->getHandlerBlock(), Uncaught, CallStack);
+ 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 =
+ llvm::remove_if(Uncaught, [&CaughtType](const Type *ThrownType) {
+ return ThrownType == CaughtType ||
+ isBaseOf(ThrownType, CaughtType);
+ });
+ if (NewEnd != Uncaught.end()) {
+ Uncaught.erase(NewEnd, Uncaught.end());
+ const TypeVec Rethrown = throwsException(
+ Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack);
+ Results.append(Rethrown.begin(), Rethrown.end());
+ }
+ }
+ }
+ Results.append(Uncaught.begin(), Uncaught.end());
+ } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
+ if (const FunctionDecl *Func = Call->getDirectCallee()) {
+ TypeVec Excs = throwsException(Func, CallStack);
+ Results.append(Excs.begin(), Excs.end());
+ }
+ } else {
+ for (const Stmt *Child : St->children()) {
+ TypeVec Excs = throwsException(Child, Caught, CallStack);
+ Results.append(Excs.begin(), Excs.end());
+ }
+ }
+ return Results;
+}
+
+static const TypeVec throwsException(const FunctionDecl *Func) {
+ llvm::SmallSet<const FunctionDecl *, 32> CallStack;
+ return throwsException(Func, CallStack);
+}
+
+namespace ast_matchers {
+AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
+ TypeVec ExceptionList = throwsException(&Node);
+ auto NewEnd = llvm::remove_if(
+ ExceptionList, [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, llvm::StringSet<>, IgnoredExceptions) {
+ if (const auto *TD = Node.getAsTagDecl()) {
+ if (TD->getDeclName().isIdentifier())
+ return IgnoredExceptions.count(TD->getName()) > 0;
+ }
+ return false;
+}
+
+AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
+ FunctionsThatShouldNotThrow) {
+ return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
+}
+} // namespace ast_matchers
+
+namespace tidy {
+namespace bugprone {
+
+ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
+ "FunctionsThatShouldNotThrow", "")),
+ RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
+ llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
+ IgnoredExceptionsVec;
+ StringRef(RawFunctionsThatShouldNotThrow)
+ .split(FunctionsThatShouldNotThrowVec, ",", -1, false);
+ FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
+ FunctionsThatShouldNotThrowVec.end());
+ StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
+ IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
+ IgnoredExceptionsVec.end());
+}
+
+void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "FunctionsThatShouldNotThrow",
+ RawFunctionsThatShouldNotThrow);
+ 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(FunctionsThatShouldNotThrow))))
+ .bind("thrower"),
+ this);
+}
+
+void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
+ const FunctionDecl *MatchedDecl =
+ Result.Nodes.getNodeAs<FunctionDecl>("thrower");
+ if (!MatchedDecl)
+ return;
+
+ // FIXME: We should provide more information about the exact location where
+ // the exception is thrown, maybe the full path the exception escapes
+ diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 "
+ "which should not throw exceptions") << MatchedDecl;
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.h b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.h
new file mode 100644
index 00000000000..d690022aa97
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/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_BUGPRONE_EXCEPTION_ESCAPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/StringSet.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// 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/bugprone-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 RawFunctionsThatShouldNotThrow;
+ std::string RawIgnoredExceptions;
+
+ llvm::StringSet<> FunctionsThatShouldNotThrow;
+ llvm::StringSet<> IgnoredExceptions;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
OpenPOWER on IntegriCloud