clang-tools 8.0.0

ExceptionEscapeCheck.cpp
Go to the documentation of this file.
1 //===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "ExceptionEscapeCheck.h"
11
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14
15 #include "llvm/ADT/SmallSet.h"
16 #include "llvm/ADT/StringSet.h"
17
18 using namespace clang::ast_matchers;
19
20 namespace {
21 typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
22 } // namespace
23
24 namespace clang {
25
26 static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
27 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
28 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
29 if (!DerivedClass || !BaseClass)
30 return false;
31
32 return !DerivedClass->forallBases(
33 [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
34 }
35
36 static const TypeVec
37 throwsException(const Stmt *St, const TypeVec &Caught,
38 llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
39
40 static const TypeVec
41 throwsException(const FunctionDecl *Func,
42 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
43 if (CallStack.count(Func))
44 return TypeVec();
45
46 if (const Stmt *Body = Func->getBody()) {
47 CallStack.insert(Func);
48 const TypeVec Result = throwsException(Body, TypeVec(), CallStack);
49 CallStack.erase(Func);
50 return Result;
51 }
52
53 TypeVec Result;
54 if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
55 for (const QualType Ex : FPT->exceptions()) {
56 Result.push_back(Ex.getTypePtr());
57 }
58 }
59 return Result;
60 }
61
62 static const TypeVec
63 throwsException(const Stmt *St, const TypeVec &Caught,
64 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
65 TypeVec Results;
66
67 if (!St)
68 return Results;
69
70 if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
71 if (const auto *ThrownExpr = Throw->getSubExpr()) {
72 const auto *ThrownType =
73 ThrownExpr->getType()->getUnqualifiedDesugaredType();
74 if (ThrownType->isReferenceType()) {
75 ThrownType = ThrownType->castAs<ReferenceType>()
76 ->getPointeeType()
77 ->getUnqualifiedDesugaredType();
78 }
79 if (const auto *TD = ThrownType->getAsTagDecl()) {
80 if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc"
81 && TD->isInStdNamespace())
82 return Results;
83 }
84 Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
85 } else {
86 Results.append(Caught.begin(), Caught.end());
87 }
88 } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
89 TypeVec Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack);
90 for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
91 const CXXCatchStmt *Catch = Try->getHandler(i);
92 if (!Catch->getExceptionDecl()) {
93 const TypeVec Rethrown =
94 throwsException(Catch->getHandlerBlock(), Uncaught, CallStack);
95 Results.append(Rethrown.begin(), Rethrown.end());
96 Uncaught.clear();
97 } else {
98 const auto *CaughtType =
99 Catch->getCaughtType()->getUnqualifiedDesugaredType();
100 if (CaughtType->isReferenceType()) {
101 CaughtType = CaughtType->castAs<ReferenceType>()
102 ->getPointeeType()
103 ->getUnqualifiedDesugaredType();
104 }
105 auto NewEnd =
106 llvm::remove_if(Uncaught, [&CaughtType](const Type *ThrownType) {
107 return ThrownType == CaughtType ||
108 isBaseOf(ThrownType, CaughtType);
109 });
110 if (NewEnd != Uncaught.end()) {
111 Uncaught.erase(NewEnd, Uncaught.end());
112 const TypeVec Rethrown = throwsException(
113 Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack);
114 Results.append(Rethrown.begin(), Rethrown.end());
115 }
116 }
117 }
118 Results.append(Uncaught.begin(), Uncaught.end());
119 } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
120 if (const FunctionDecl *Func = Call->getDirectCallee()) {
121 TypeVec Excs = throwsException(Func, CallStack);
122 Results.append(Excs.begin(), Excs.end());
123 }
124 } else {
125 for (const Stmt *Child : St->children()) {
126 TypeVec Excs = throwsException(Child, Caught, CallStack);
127 Results.append(Excs.begin(), Excs.end());
128 }
129 }
130 return Results;
131 }
132
133 static const TypeVec throwsException(const FunctionDecl *Func) {
134 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
135 return throwsException(Func, CallStack);
136 }
137
138 namespace ast_matchers {
139 AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
140 TypeVec ExceptionList = throwsException(&Node);
141 auto NewEnd = llvm::remove_if(
142 ExceptionList, [this, Finder, Builder](const Type *Exception) {
143 return !InnerMatcher.matches(*Exception, Finder, Builder);
144 });
145 ExceptionList.erase(NewEnd, ExceptionList.end());
146 return ExceptionList.size();
147 }
148
149 AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) {
150 if (const auto *TD = Node.getAsTagDecl()) {
151 if (TD->getDeclName().isIdentifier())
152 return IgnoredExceptions.count(TD->getName()) > 0;
153 }
154 return false;
155 }
156
157 AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
158 FunctionsThatShouldNotThrow) {
159 return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
160 }
161 } // namespace ast_matchers
162
163 namespace tidy {
164 namespace bugprone {
165
166 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
167 ClangTidyContext *Context)
168 : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
169 "FunctionsThatShouldNotThrow", "")),
170 RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
171 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
172 IgnoredExceptionsVec;
173 StringRef(RawFunctionsThatShouldNotThrow)
174 .split(FunctionsThatShouldNotThrowVec, ",", -1, false);
175 FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
176 FunctionsThatShouldNotThrowVec.end());
177 StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
178 IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
179 IgnoredExceptionsVec.end());
180 }
181
182 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
183 Options.store(Opts, "FunctionsThatShouldNotThrow",
184 RawFunctionsThatShouldNotThrow);
185 Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions);
186 }
187
188 void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
189 if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions)
190 return;
191
192 Finder->addMatcher(
193 functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(),
194 cxxConstructorDecl(isMoveConstructor()),
195 cxxMethodDecl(isMoveAssignmentOperator()),
196 hasName("main"), hasName("swap"),
197 isEnabled(FunctionsThatShouldNotThrow)),
198 throws(unless(isIgnored(IgnoredExceptions))))
199 .bind("thrower"),
200 this);
201 }
202
203 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
204 const FunctionDecl *MatchedDecl =
205 Result.Nodes.getNodeAs<FunctionDecl>("thrower");
206 if (!MatchedDecl)
207 return;
208
209 // FIXME: We should provide more information about the exact location where
210 // the exception is thrown, maybe the full path the exception escapes
211 diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 "
212 "which should not throw exceptions") << MatchedDecl;
213 }
214
215 } // namespace bugprone
216 } // namespace tidy
217 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::isBaseOf
static bool isBaseOf(const Type *DerivedType, const Type *BaseType)
Definition: ExceptionEscapeCheck.cpp:26
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:702
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::bugprone::ExceptionEscapeCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ExceptionEscapeCheck.cpp:203
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
clang::tidy::bugprone::ExceptionEscapeCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: ExceptionEscapeCheck.cpp:182
ExceptionEscapeCheck.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:98
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::ast_matchers::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher< Type >, InnerMatcher)
Definition: ExceptionEscapeCheck.cpp:139
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:100
clang::throwsException
static const TypeVec throwsException(const Stmt *St, const TypeVec &Caught, llvm::SmallSet< const FunctionDecl *, 32 > &CallStack)
Definition: ExceptionEscapeCheck.cpp:63
clang::tidy::bugprone::ExceptionEscapeCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: ExceptionEscapeCheck.cpp:188
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438