File: | build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp |
Warning: | line 158, column 9 Value stored to 'ThrownType' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "ExceptionAnalyzer.h" |
10 | |
11 | namespace clang { |
12 | namespace tidy { |
13 | namespace utils { |
14 | |
15 | void ExceptionAnalyzer::ExceptionInfo::registerException( |
16 | const Type *ExceptionType) { |
17 | assert(ExceptionType != nullptr && "Only valid types are accepted")(static_cast <bool> (ExceptionType != nullptr && "Only valid types are accepted") ? void (0) : __assert_fail ( "ExceptionType != nullptr && \"Only valid types are accepted\"" , "clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp", 17, __extension__ __PRETTY_FUNCTION__)); |
18 | Behaviour = State::Throwing; |
19 | ThrownExceptions.insert(ExceptionType); |
20 | } |
21 | |
22 | void ExceptionAnalyzer::ExceptionInfo::registerExceptions( |
23 | const Throwables &Exceptions) { |
24 | if (Exceptions.size() == 0) |
25 | return; |
26 | Behaviour = State::Throwing; |
27 | ThrownExceptions.insert(Exceptions.begin(), Exceptions.end()); |
28 | } |
29 | |
30 | ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge( |
31 | const ExceptionAnalyzer::ExceptionInfo &Other) { |
32 | // Only the following two cases require an update to the local |
33 | // 'Behaviour'. If the local entity is already throwing there will be no |
34 | // change and if the other entity is throwing the merged entity will throw |
35 | // as well. |
36 | // If one of both entities is 'Unknown' and the other one does not throw |
37 | // the merged entity is 'Unknown' as well. |
38 | if (Other.Behaviour == State::Throwing) |
39 | Behaviour = State::Throwing; |
40 | else if (Other.Behaviour == State::Unknown && Behaviour == State::NotThrowing) |
41 | Behaviour = State::Unknown; |
42 | |
43 | ContainsUnknown = ContainsUnknown || Other.ContainsUnknown; |
44 | ThrownExceptions.insert(Other.ThrownExceptions.begin(), |
45 | Other.ThrownExceptions.end()); |
46 | return *this; |
47 | } |
48 | |
49 | static bool isBaseOf(const Type *DerivedType, const Type *BaseType) { |
50 | const auto *DerivedClass = DerivedType->getAsCXXRecordDecl(); |
51 | const auto *BaseClass = BaseType->getAsCXXRecordDecl(); |
52 | if (!DerivedClass || !BaseClass) |
53 | return false; |
54 | |
55 | return !DerivedClass->forallBases( |
56 | [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; }); |
57 | } |
58 | |
59 | bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(const Type *BaseClass) { |
60 | llvm::SmallVector<const Type *, 8> TypesToDelete; |
61 | for (const Type *T : ThrownExceptions) { |
62 | if (T == BaseClass || isBaseOf(T, BaseClass)) |
63 | TypesToDelete.push_back(T); |
64 | } |
65 | |
66 | for (const Type *T : TypesToDelete) |
67 | ThrownExceptions.erase(T); |
68 | |
69 | reevaluateBehaviour(); |
70 | return TypesToDelete.size() > 0; |
71 | } |
72 | |
73 | ExceptionAnalyzer::ExceptionInfo & |
74 | ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions( |
75 | const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) { |
76 | llvm::SmallVector<const Type *, 8> TypesToDelete; |
77 | // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible. |
78 | // Therefore this slightly hacky implementation is required. |
79 | for (const Type *T : ThrownExceptions) { |
80 | if (const auto *TD = T->getAsTagDecl()) { |
81 | if (TD->getDeclName().isIdentifier()) { |
82 | if ((IgnoreBadAlloc && |
83 | (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) || |
84 | (IgnoredTypes.count(TD->getName()) > 0)) |
85 | TypesToDelete.push_back(T); |
86 | } |
87 | } |
88 | } |
89 | for (const Type *T : TypesToDelete) |
90 | ThrownExceptions.erase(T); |
91 | |
92 | reevaluateBehaviour(); |
93 | return *this; |
94 | } |
95 | |
96 | void ExceptionAnalyzer::ExceptionInfo::clear() { |
97 | Behaviour = State::NotThrowing; |
98 | ContainsUnknown = false; |
99 | ThrownExceptions.clear(); |
100 | } |
101 | |
102 | void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() { |
103 | if (ThrownExceptions.size() == 0) |
104 | if (ContainsUnknown) |
105 | Behaviour = State::Unknown; |
106 | else |
107 | Behaviour = State::NotThrowing; |
108 | else |
109 | Behaviour = State::Throwing; |
110 | } |
111 | |
112 | ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException( |
113 | const FunctionDecl *Func, |
114 | llvm::SmallSet<const FunctionDecl *, 32> &CallStack) { |
115 | if (CallStack.count(Func)) |
116 | return ExceptionInfo::createNonThrowing(); |
117 | |
118 | if (const Stmt *Body = Func->getBody()) { |
119 | CallStack.insert(Func); |
120 | ExceptionInfo Result = |
121 | throwsException(Body, ExceptionInfo::Throwables(), CallStack); |
122 | |
123 | // For a constructor, we also have to check the initializers. |
124 | if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) { |
125 | for (const CXXCtorInitializer *Init : Ctor->inits()) { |
126 | ExceptionInfo Excs = throwsException( |
127 | Init->getInit(), ExceptionInfo::Throwables(), CallStack); |
128 | Result.merge(Excs); |
129 | } |
130 | } |
131 | |
132 | CallStack.erase(Func); |
133 | return Result; |
134 | } |
135 | |
136 | auto Result = ExceptionInfo::createUnknown(); |
137 | if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) { |
138 | for (const QualType &Ex : FPT->exceptions()) |
139 | Result.registerException(Ex.getTypePtr()); |
140 | } |
141 | return Result; |
142 | } |
143 | |
144 | /// Analyzes a single statement on it's throwing behaviour. This is in principle |
145 | /// possible except some 'Unknown' functions are called. |
146 | ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException( |
147 | const Stmt *St, const ExceptionInfo::Throwables &Caught, |
148 | llvm::SmallSet<const FunctionDecl *, 32> &CallStack) { |
149 | auto Results = ExceptionInfo::createNonThrowing(); |
150 | if (!St) |
151 | return Results; |
152 | |
153 | if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) { |
154 | if (const auto *ThrownExpr = Throw->getSubExpr()) { |
155 | const auto *ThrownType = |
156 | ThrownExpr->getType()->getUnqualifiedDesugaredType(); |
157 | if (ThrownType->isReferenceType()) |
158 | ThrownType = ThrownType->castAs<ReferenceType>() |
Value stored to 'ThrownType' is never read | |
159 | ->getPointeeType() |
160 | ->getUnqualifiedDesugaredType(); |
161 | Results.registerException( |
162 | ThrownExpr->getType()->getUnqualifiedDesugaredType()); |
163 | } else |
164 | // A rethrow of a caught exception happens which makes it possible |
165 | // to throw all exception that are caught in the 'catch' clause of |
166 | // the parent try-catch block. |
167 | Results.registerExceptions(Caught); |
168 | } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) { |
169 | ExceptionInfo Uncaught = |
170 | throwsException(Try->getTryBlock(), Caught, CallStack); |
171 | for (unsigned I = 0; I < Try->getNumHandlers(); ++I) { |
172 | const CXXCatchStmt *Catch = Try->getHandler(I); |
173 | |
174 | // Everything is catched through 'catch(...)'. |
175 | if (!Catch->getExceptionDecl()) { |
176 | ExceptionInfo Rethrown = throwsException( |
177 | Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack); |
178 | Results.merge(Rethrown); |
179 | Uncaught.clear(); |
180 | } else { |
181 | const auto *CaughtType = |
182 | Catch->getCaughtType()->getUnqualifiedDesugaredType(); |
183 | if (CaughtType->isReferenceType()) { |
184 | CaughtType = CaughtType->castAs<ReferenceType>() |
185 | ->getPointeeType() |
186 | ->getUnqualifiedDesugaredType(); |
187 | } |
188 | |
189 | // If the caught exception will catch multiple previously potential |
190 | // thrown types (because it's sensitive to inheritance) the throwing |
191 | // situation changes. First of all filter the exception types and |
192 | // analyze if the baseclass-exception is rethrown. |
193 | if (Uncaught.filterByCatch(CaughtType)) { |
194 | ExceptionInfo::Throwables CaughtExceptions; |
195 | CaughtExceptions.insert(CaughtType); |
196 | ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(), |
197 | CaughtExceptions, CallStack); |
198 | Results.merge(Rethrown); |
199 | } |
200 | } |
201 | } |
202 | Results.merge(Uncaught); |
203 | } else if (const auto *Call = dyn_cast<CallExpr>(St)) { |
204 | if (const FunctionDecl *Func = Call->getDirectCallee()) { |
205 | ExceptionInfo Excs = throwsException(Func, CallStack); |
206 | Results.merge(Excs); |
207 | } |
208 | } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) { |
209 | ExceptionInfo Excs = |
210 | throwsException(Construct->getConstructor(), CallStack); |
211 | Results.merge(Excs); |
212 | } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) { |
213 | ExceptionInfo Excs = |
214 | throwsException(DefaultInit->getExpr(), Caught, CallStack); |
215 | Results.merge(Excs); |
216 | } else { |
217 | for (const Stmt *Child : St->children()) { |
218 | ExceptionInfo Excs = throwsException(Child, Caught, CallStack); |
219 | Results.merge(Excs); |
220 | } |
221 | } |
222 | return Results; |
223 | } |
224 | |
225 | ExceptionAnalyzer::ExceptionInfo |
226 | ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) { |
227 | ExceptionInfo ExceptionList; |
228 | |
229 | // Check if the function has already been analyzed and reuse that result. |
230 | if (FunctionCache.count(Func) == 0) { |
231 | llvm::SmallSet<const FunctionDecl *, 32> CallStack; |
232 | ExceptionList = throwsException(Func, CallStack); |
233 | |
234 | // Cache the result of the analysis. This is done prior to filtering |
235 | // because it is best to keep as much information as possible. |
236 | // The results here might be relevant to different analysis passes |
237 | // with different needs as well. |
238 | FunctionCache.insert(std::make_pair(Func, ExceptionList)); |
239 | } else |
240 | ExceptionList = FunctionCache[Func]; |
241 | |
242 | return ExceptionList; |
243 | } |
244 | |
245 | ExceptionAnalyzer::ExceptionInfo |
246 | ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) { |
247 | llvm::SmallSet<const FunctionDecl *, 32> CallStack; |
248 | return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack); |
249 | } |
250 | |
251 | template <typename T> |
252 | ExceptionAnalyzer::ExceptionInfo |
253 | ExceptionAnalyzer::analyzeDispatch(const T *Node) { |
254 | ExceptionInfo ExceptionList = analyzeImpl(Node); |
255 | |
256 | if (ExceptionList.getBehaviour() == State::NotThrowing || |
257 | ExceptionList.getBehaviour() == State::Unknown) |
258 | return ExceptionList; |
259 | |
260 | // Remove all ignored exceptions from the list of exceptions that can be |
261 | // thrown. |
262 | ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc); |
263 | |
264 | return ExceptionList; |
265 | } |
266 | |
267 | ExceptionAnalyzer::ExceptionInfo |
268 | ExceptionAnalyzer::analyze(const FunctionDecl *Func) { |
269 | return analyzeDispatch(Func); |
270 | } |
271 | |
272 | ExceptionAnalyzer::ExceptionInfo |
273 | ExceptionAnalyzer::analyze(const Stmt *Stmt) { |
274 | return analyzeDispatch(Stmt); |
275 | } |
276 | |
277 | } // namespace utils |
278 | } // namespace tidy |
279 | |
280 | } // namespace clang |