1
- // ===--- ExceptionEscapeCheck.cpp - clang-tidy- ----------------------------===//
1
+ // ===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===//
2
2
//
3
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
4
// See https://llvm.org/LICENSE.txt for license information.
10
10
11
11
#include " clang/AST/ASTContext.h"
12
12
#include " clang/ASTMatchers/ASTMatchFinder.h"
13
-
14
13
#include " llvm/ADT/SmallSet.h"
15
14
#include " llvm/ADT/StringSet.h"
16
15
17
16
using namespace clang ::ast_matchers;
18
17
19
- namespace {
20
- typedef llvm::SmallVector<const clang::Type *, 8 > TypeVec;
21
- } // namespace
22
-
23
18
namespace clang {
24
-
25
- static bool isBaseOf (const Type *DerivedType, const Type *BaseType) {
26
- const auto *DerivedClass = DerivedType->getAsCXXRecordDecl ();
27
- const auto *BaseClass = BaseType->getAsCXXRecordDecl ();
28
- if (!DerivedClass || !BaseClass)
29
- return false ;
30
-
31
- return !DerivedClass->forallBases (
32
- [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
33
- }
34
-
35
- static const TypeVec
36
- throwsException (const Stmt *St, const TypeVec &Caught,
37
- llvm::SmallSet<const FunctionDecl *, 32 > &CallStack);
38
-
39
- static const TypeVec
40
- throwsException (const FunctionDecl *Func,
41
- llvm::SmallSet<const FunctionDecl *, 32 > &CallStack) {
42
- if (CallStack.count (Func))
43
- return TypeVec ();
44
-
45
- if (const Stmt *Body = Func->getBody ()) {
46
- CallStack.insert (Func);
47
- const TypeVec Result = throwsException (Body, TypeVec (), CallStack);
48
- CallStack.erase (Func);
49
- return Result;
50
- }
51
-
52
- TypeVec Result;
53
- if (const auto *FPT = Func->getType ()->getAs <FunctionProtoType>()) {
54
- for (const QualType Ex : FPT->exceptions ()) {
55
- Result.push_back (Ex.getTypePtr ());
56
- }
57
- }
58
- return Result;
59
- }
60
-
61
- static const TypeVec
62
- throwsException (const Stmt *St, const TypeVec &Caught,
63
- llvm::SmallSet<const FunctionDecl *, 32 > &CallStack) {
64
- TypeVec Results;
65
-
66
- if (!St)
67
- return Results;
68
-
69
- if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
70
- if (const auto *ThrownExpr = Throw->getSubExpr ()) {
71
- const auto *ThrownType =
72
- ThrownExpr->getType ()->getUnqualifiedDesugaredType ();
73
- if (ThrownType->isReferenceType ()) {
74
- ThrownType = ThrownType->castAs <ReferenceType>()
75
- ->getPointeeType ()
76
- ->getUnqualifiedDesugaredType ();
77
- }
78
- if (const auto *TD = ThrownType->getAsTagDecl ()) {
79
- if (TD->getDeclName ().isIdentifier () && TD->getName () == " bad_alloc"
80
- && TD->isInStdNamespace ())
81
- return Results;
82
- }
83
- Results.push_back (ThrownExpr->getType ()->getUnqualifiedDesugaredType ());
84
- } else {
85
- Results.append (Caught.begin (), Caught.end ());
86
- }
87
- } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
88
- TypeVec Uncaught = throwsException (Try->getTryBlock (), Caught, CallStack);
89
- for (unsigned i = 0 ; i < Try->getNumHandlers (); ++i) {
90
- const CXXCatchStmt *Catch = Try->getHandler (i);
91
- if (!Catch->getExceptionDecl ()) {
92
- const TypeVec Rethrown =
93
- throwsException (Catch->getHandlerBlock (), Uncaught, CallStack);
94
- Results.append (Rethrown.begin (), Rethrown.end ());
95
- Uncaught.clear ();
96
- } else {
97
- const auto *CaughtType =
98
- Catch->getCaughtType ()->getUnqualifiedDesugaredType ();
99
- if (CaughtType->isReferenceType ()) {
100
- CaughtType = CaughtType->castAs <ReferenceType>()
101
- ->getPointeeType ()
102
- ->getUnqualifiedDesugaredType ();
103
- }
104
- auto NewEnd =
105
- llvm::remove_if (Uncaught, [&CaughtType](const Type *ThrownType) {
106
- return ThrownType == CaughtType ||
107
- isBaseOf (ThrownType, CaughtType);
108
- });
109
- if (NewEnd != Uncaught.end ()) {
110
- Uncaught.erase (NewEnd, Uncaught.end ());
111
- const TypeVec Rethrown = throwsException (
112
- Catch->getHandlerBlock (), TypeVec (1 , CaughtType), CallStack);
113
- Results.append (Rethrown.begin (), Rethrown.end ());
114
- }
115
- }
116
- }
117
- Results.append (Uncaught.begin (), Uncaught.end ());
118
- } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
119
- if (const FunctionDecl *Func = Call->getDirectCallee ()) {
120
- TypeVec Excs = throwsException (Func, CallStack);
121
- Results.append (Excs.begin (), Excs.end ());
122
- }
123
- } else {
124
- for (const Stmt *Child : St->children ()) {
125
- TypeVec Excs = throwsException (Child, Caught, CallStack);
126
- Results.append (Excs.begin (), Excs.end ());
127
- }
128
- }
129
- return Results;
130
- }
131
-
132
- static const TypeVec throwsException (const FunctionDecl *Func) {
133
- llvm::SmallSet<const FunctionDecl *, 32 > CallStack;
134
- return throwsException (Func, CallStack);
135
- }
136
-
137
- namespace ast_matchers {
138
- AST_MATCHER_P (FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
139
- TypeVec ExceptionList = throwsException (&Node);
140
- auto NewEnd = llvm::remove_if (
141
- ExceptionList, [this , Finder, Builder](const Type *Exception) {
142
- return !InnerMatcher.matches (*Exception, Finder, Builder);
143
- });
144
- ExceptionList.erase (NewEnd, ExceptionList.end ());
145
- return ExceptionList.size ();
146
- }
147
-
148
- AST_MATCHER_P (Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) {
149
- if (const auto *TD = Node.getAsTagDecl ()) {
150
- if (TD->getDeclName ().isIdentifier ())
151
- return IgnoredExceptions.count (TD->getName ()) > 0 ;
152
- }
153
- return false ;
154
- }
155
-
19
+ namespace {
156
20
AST_MATCHER_P (FunctionDecl, isEnabled, llvm::StringSet<>,
157
21
FunctionsThatShouldNotThrow) {
158
22
return FunctionsThatShouldNotThrow.count (Node.getNameAsString ()) > 0 ;
159
23
}
160
- } // namespace ast_matchers
24
+ } // namespace
161
25
162
26
namespace tidy {
163
27
namespace bugprone {
164
-
165
28
ExceptionEscapeCheck::ExceptionEscapeCheck (StringRef Name,
166
29
ClangTidyContext *Context)
167
30
: ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
@@ -173,9 +36,12 @@ ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
173
36
.split (FunctionsThatShouldNotThrowVec, " ," , -1 , false );
174
37
FunctionsThatShouldNotThrow.insert (FunctionsThatShouldNotThrowVec.begin (),
175
38
FunctionsThatShouldNotThrowVec.end ());
39
+
40
+ llvm::StringSet<> IgnoredExceptions;
176
41
StringRef (RawIgnoredExceptions).split (IgnoredExceptionsVec, " ," , -1 , false );
177
42
IgnoredExceptions.insert (IgnoredExceptionsVec.begin (),
178
43
IgnoredExceptionsVec.end ());
44
+ Tracer.ignoreExceptions (std::move (IgnoredExceptions));
179
45
}
180
46
181
47
void ExceptionEscapeCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
@@ -193,22 +59,25 @@ void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
193
59
cxxConstructorDecl (isMoveConstructor ()),
194
60
cxxMethodDecl (isMoveAssignmentOperator ()),
195
61
hasName (" main" ), hasName (" swap" ),
196
- isEnabled (FunctionsThatShouldNotThrow)),
197
- throws (unless (isIgnored (IgnoredExceptions))))
62
+ isEnabled (FunctionsThatShouldNotThrow)))
198
63
.bind (" thrower" ),
199
64
this );
200
65
}
201
66
202
67
void ExceptionEscapeCheck::check (const MatchFinder::MatchResult &Result) {
203
- const FunctionDecl *MatchedDecl =
204
- Result. Nodes . getNodeAs <FunctionDecl>( " thrower " );
68
+ const auto *MatchedDecl = Result. Nodes . getNodeAs <FunctionDecl>( " thrower " );
69
+
205
70
if (!MatchedDecl)
206
71
return ;
207
72
208
- // FIXME: We should provide more information about the exact location where
209
- // the exception is thrown, maybe the full path the exception escapes
210
- diag (MatchedDecl->getLocation (), " an exception may be thrown in function %0 "
211
- " which should not throw exceptions" ) << MatchedDecl;
73
+ if (Tracer.throwsException (MatchedDecl))
74
+ // FIXME: We should provide more information about the exact location where
75
+ // the exception is thrown, maybe the full path the exception escapes
76
+ diag (MatchedDecl->getLocation (),
77
+ " an exception may be thrown in function %0 "
78
+
79
+ " which should not throw exceptions" )
80
+ << MatchedDecl;
212
81
}
213
82
214
83
} // namespace bugprone
0 commit comments