xref: /llvm-project/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp (revision fd2e0483de089fb1459bf440d74e5b4e648a429f)
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::tidy::utils {
12 
13 void ExceptionAnalyzer::ExceptionInfo::registerException(
14     const Type *ExceptionType) {
15   assert(ExceptionType != nullptr && "Only valid types are accepted");
16   Behaviour = State::Throwing;
17   ThrownExceptions.insert(ExceptionType);
18 }
19 
20 void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
21     const Throwables &Exceptions) {
22   if (Exceptions.empty())
23     return;
24   Behaviour = State::Throwing;
25   ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
26 }
27 
28 ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge(
29     const ExceptionAnalyzer::ExceptionInfo &Other) {
30   // Only the following two cases require an update to the local
31   // 'Behaviour'. If the local entity is already throwing there will be no
32   // change and if the other entity is throwing the merged entity will throw
33   // as well.
34   // If one of both entities is 'Unknown' and the other one does not throw
35   // the merged entity is 'Unknown' as well.
36   if (Other.Behaviour == State::Throwing)
37     Behaviour = State::Throwing;
38   else if (Other.Behaviour == State::Unknown && Behaviour == State::NotThrowing)
39     Behaviour = State::Unknown;
40 
41   ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
42   ThrownExceptions.insert(Other.ThrownExceptions.begin(),
43                           Other.ThrownExceptions.end());
44   return *this;
45 }
46 
47 // FIXME: This could be ported to clang later.
48 namespace {
49 
50 bool isUnambiguousPublicBaseClass(const Type *DerivedType,
51                                   const Type *BaseType) {
52   const auto *DerivedClass =
53       DerivedType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
54   const auto *BaseClass =
55       BaseType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
56   if (!DerivedClass || !BaseClass)
57     return false;
58 
59   CXXBasePaths Paths;
60   Paths.setOrigin(DerivedClass);
61 
62   bool IsPublicBaseClass = false;
63   DerivedClass->lookupInBases(
64       [&BaseClass, &IsPublicBaseClass](const CXXBaseSpecifier *BS,
65                                        CXXBasePath &) {
66         if (BS->getType()
67                     ->getCanonicalTypeUnqualified()
68                     ->getAsCXXRecordDecl() == BaseClass &&
69             BS->getAccessSpecifier() == AS_public) {
70           IsPublicBaseClass = true;
71           return true;
72         }
73 
74         return false;
75       },
76       Paths);
77 
78   return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
79          IsPublicBaseClass;
80 }
81 
82 inline bool isPointerOrPointerToMember(const Type *T) {
83   return T->isPointerType() || T->isMemberPointerType();
84 }
85 
86 std::optional<QualType> getPointeeOrArrayElementQualType(QualType T) {
87   if (T->isAnyPointerType() || T->isMemberPointerType())
88     return T->getPointeeType();
89 
90   if (T->isArrayType())
91     return T->getAsArrayTypeUnsafe()->getElementType();
92 
93   return std::nullopt;
94 }
95 
96 bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
97   const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
98   const auto *BaseClass = BaseType->getAsCXXRecordDecl();
99   if (!DerivedClass || !BaseClass)
100     return false;
101 
102   return !DerivedClass->forallBases(
103       [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
104 }
105 
106 // Check if T1 is more or Equally qualified than T2.
107 bool moreOrEquallyQualified(QualType T1, QualType T2) {
108   return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
109          T1.getQualifiers() == T2.getQualifiers();
110 }
111 
112 bool isStandardPointerConvertible(QualType From, QualType To) {
113   assert((From->isPointerType() || From->isMemberPointerType()) &&
114          (To->isPointerType() || To->isMemberPointerType()) &&
115          "Pointer conversion should be performed on pointer types only.");
116 
117   if (!moreOrEquallyQualified(To->getPointeeType(), From->getPointeeType()))
118     return false;
119 
120   // (1)
121   // A null pointer constant can be converted to a pointer type ...
122   // The conversion of a null pointer constant to a pointer to cv-qualified type
123   // is a single conversion, and not the sequence of a pointer conversion
124   // followed by a qualification conversion. A null pointer constant of integral
125   // type can be converted to a prvalue of type std::nullptr_t
126   if (To->isPointerType() && From->isNullPtrType())
127     return true;
128 
129   // (2)
130   // A prvalue of type “pointer to cv T”, where T is an object type, can be
131   // converted to a prvalue of type “pointer to cv void”.
132   if (To->isVoidPointerType() && From->isObjectPointerType())
133     return true;
134 
135   // (3)
136   // A prvalue of type “pointer to cv D”, where D is a complete class type, can
137   // be converted to a prvalue of type “pointer to cv B”, where B is a base
138   // class of D. If B is an inaccessible or ambiguous base class of D, a program
139   // that necessitates this conversion is ill-formed.
140   if (const auto *RD = From->getPointeeCXXRecordDecl()) {
141     if (RD->isCompleteDefinition() &&
142         isBaseOf(From->getPointeeType().getTypePtr(),
143                  To->getPointeeType().getTypePtr())) {
144       // If B is an inaccessible or ambiguous base class of D, a program
145       // that necessitates this conversion is ill-formed
146       return isUnambiguousPublicBaseClass(From->getPointeeType().getTypePtr(),
147                                           To->getPointeeType().getTypePtr());
148     }
149   }
150 
151   return false;
152 }
153 
154 bool isFunctionPointerConvertible(QualType From, QualType To) {
155   if (!From->isFunctionPointerType() && !From->isFunctionType() &&
156       !From->isMemberFunctionPointerType())
157     return false;
158 
159   if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
160     return false;
161 
162   if (To->isFunctionPointerType()) {
163     if (From->isFunctionPointerType())
164       return To->getPointeeType() == From->getPointeeType();
165 
166     if (From->isFunctionType())
167       return To->getPointeeType() == From;
168 
169     return false;
170   }
171 
172   if (To->isMemberFunctionPointerType()) {
173     if (!From->isMemberFunctionPointerType())
174       return false;
175 
176     const auto *FromMember = cast<MemberPointerType>(From);
177     const auto *ToMember = cast<MemberPointerType>(To);
178 
179     // Note: converting Derived::* to Base::* is a different kind of conversion,
180     // called Pointer-to-member conversion.
181     return FromMember->getClass() == ToMember->getClass() &&
182            FromMember->getPointeeType() == ToMember->getPointeeType();
183   }
184 
185   return false;
186 }
187 
188 // Checks if From is qualification convertible to To based on the current
189 // LangOpts. If From is any array, we perform the array to pointer conversion
190 // first. The function only performs checks based on C++ rules, which can differ
191 // from the C rules.
192 //
193 // The function should only be called in C++ mode.
194 bool isQualificationConvertiblePointer(QualType From, QualType To,
195                                        LangOptions LangOpts) {
196 
197   // [N4659 7.5 (1)]
198   // A cv-decomposition of a type T is a sequence of cv_i and P_i such that T is
199   //    cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U” for n > 0,
200   // where each cv_i is a set of cv-qualifiers, and each P_i is “pointer to”,
201   // “pointer to member of class C_i of type”, “array of N_i”, or
202   // “array of unknown bound of”.
203   //
204   // If P_i designates an array, the cv-qualifiers cv_i+1 on the element type
205   // are also taken as the cv-qualifiers cvi of the array.
206   //
207   // The n-tuple of cv-qualifiers after the first one in the longest
208   // cv-decomposition of T, that is, cv_1, cv_2, ... , cv_n, is called the
209   // cv-qualification signature of T.
210 
211   auto isValidP_i = [](QualType P) {
212     return P->isPointerType() || P->isMemberPointerType() ||
213            P->isConstantArrayType() || P->isIncompleteArrayType();
214   };
215 
216   auto isSameP_i = [](QualType P1, QualType P2) {
217     if (P1->isPointerType())
218       return P2->isPointerType();
219 
220     if (P1->isMemberPointerType())
221       return P2->isMemberPointerType() &&
222              P1->getAs<MemberPointerType>()->getClass() ==
223                  P2->getAs<MemberPointerType>()->getClass();
224 
225     if (P1->isConstantArrayType())
226       return P2->isConstantArrayType() &&
227              cast<ConstantArrayType>(P1)->getSize() ==
228                  cast<ConstantArrayType>(P2)->getSize();
229 
230     if (P1->isIncompleteArrayType())
231       return P2->isIncompleteArrayType();
232 
233     return false;
234   };
235 
236   // (2)
237   // Two types From and To are similar if they have cv-decompositions with the
238   // same n such that corresponding P_i components are the same [(added by
239   // N4849 7.3.5) or one is “array of N_i” and the other is “array of unknown
240   // bound of”], and the types denoted by U are the same.
241   //
242   // (3)
243   // A prvalue expression of type From can be converted to type To if the
244   // following conditions are satisfied:
245   //  - From and To are similar
246   //  - For every i > 0, if const is in cv_i of From then const is in cv_i of
247   //  To, and similarly for volatile.
248   //  - [(derived from addition by N4849 7.3.5) If P_i of From is “array of
249   //  unknown bound of”, P_i of To is “array of unknown bound of”.]
250   //  - If the cv_i of From and cv_i of To are different, then const is in every
251   //  cv_k of To for 0 < k < i.
252 
253   int I = 0;
254   bool ConstUntilI = true;
255   auto SatisfiesCVRules = [&I, &ConstUntilI](const QualType &From,
256                                              const QualType &To) {
257     if (I > 1) {
258       if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
259         return false;
260     }
261 
262     if (I > 0) {
263       if (From.isConstQualified() && !To.isConstQualified())
264         return false;
265 
266       if (From.isVolatileQualified() && !To.isVolatileQualified())
267         return false;
268 
269       ConstUntilI = To.isConstQualified();
270     }
271 
272     return true;
273   };
274 
275   while (isValidP_i(From) && isValidP_i(To)) {
276     // Remove every sugar.
277     From = From.getCanonicalType();
278     To = To.getCanonicalType();
279 
280     if (!SatisfiesCVRules(From, To))
281       return false;
282 
283     if (!isSameP_i(From, To)) {
284       if (LangOpts.CPlusPlus20) {
285         if (From->isConstantArrayType() && !To->isIncompleteArrayType())
286           return false;
287 
288         if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
289           return false;
290 
291       } else {
292         return false;
293       }
294     }
295 
296     ++I;
297     std::optional<QualType> FromPointeeOrElem =
298         getPointeeOrArrayElementQualType(From);
299     std::optional<QualType> ToPointeeOrElem =
300         getPointeeOrArrayElementQualType(To);
301 
302     assert(FromPointeeOrElem &&
303            "From pointer or array has no pointee or element!");
304     assert(ToPointeeOrElem && "To pointer or array has no pointee or element!");
305 
306     From = *FromPointeeOrElem;
307     To = *ToPointeeOrElem;
308   }
309 
310   // In this case the length (n) of From and To are not the same.
311   if (isValidP_i(From) || isValidP_i(To))
312     return false;
313 
314   // We hit U.
315   if (!SatisfiesCVRules(From, To))
316     return false;
317 
318   return From.getTypePtr() == To.getTypePtr();
319 }
320 } // namespace
321 
322 static bool canThrow(const FunctionDecl *Func) {
323   // consteval specifies that every call to the function must produce a
324   // compile-time constant, which cannot evaluate a throw expression without
325   // producing a compilation error.
326   if (Func->isConsteval())
327     return false;
328 
329   const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
330   if (!FunProto)
331     return true;
332 
333   switch (FunProto->canThrow()) {
334   case CT_Cannot:
335     return false;
336   case CT_Dependent: {
337     const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
338     if (!NoexceptExpr)
339       return true; // no noexept - can throw
340 
341     if (NoexceptExpr->isValueDependent())
342       return true; // depend on template - some instance can throw
343 
344     bool Result = false;
345     if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
346                                                   /*InConstantContext=*/true))
347       return true;  // complex X condition in noexcept(X), cannot validate,
348                     // assume that may throw
349     return !Result; // noexcept(false) - can throw
350   }
351   default:
352     return true;
353   };
354 }
355 
356 bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
357     const Type *HandlerTy, const ASTContext &Context) {
358   llvm::SmallVector<const Type *, 8> TypesToDelete;
359   for (const Type *ExceptionTy : ThrownExceptions) {
360     CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
361     CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
362 
363     // The handler is of type cv T or cv T& and E and T are the same type
364     // (ignoring the top-level cv-qualifiers) ...
365     if (ExceptionCanTy == HandlerCanTy) {
366       TypesToDelete.push_back(ExceptionTy);
367     }
368 
369     // The handler is of type cv T or cv T& and T is an unambiguous public base
370     // class of E ...
371     else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
372                                           HandlerCanTy->getTypePtr())) {
373       TypesToDelete.push_back(ExceptionTy);
374     }
375 
376     if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
377         (HandlerCanTy->getTypeClass() == Type::LValueReference &&
378          !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
379       continue;
380     // The handler is of type cv T or const T& where T is a pointer or
381     // pointer-to-member type and E is a pointer or pointer-to-member type that
382     // can be converted to T by one or more of ...
383     if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
384         isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
385       // A standard pointer conversion not involving conversions to pointers to
386       // private or protected or ambiguous classes ...
387       if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
388         TypesToDelete.push_back(ExceptionTy);
389       }
390       // A function pointer conversion ...
391       else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
392         TypesToDelete.push_back(ExceptionTy);
393       }
394       // A a qualification conversion ...
395       else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
396                                                  Context.getLangOpts())) {
397         TypesToDelete.push_back(ExceptionTy);
398       }
399     }
400 
401     // The handler is of type cv T or const T& where T is a pointer or
402     // pointer-to-member type and E is std::nullptr_t.
403     else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
404              ExceptionCanTy->isNullPtrType()) {
405       TypesToDelete.push_back(ExceptionTy);
406     }
407   }
408 
409   for (const Type *T : TypesToDelete)
410     ThrownExceptions.erase(T);
411 
412   reevaluateBehaviour();
413   return !TypesToDelete.empty();
414 }
415 
416 ExceptionAnalyzer::ExceptionInfo &
417 ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
418     const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) {
419   llvm::SmallVector<const Type *, 8> TypesToDelete;
420   // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible.
421   // Therefore this slightly hacky implementation is required.
422   for (const Type *T : ThrownExceptions) {
423     if (const auto *TD = T->getAsTagDecl()) {
424       if (TD->getDeclName().isIdentifier()) {
425         if ((IgnoreBadAlloc &&
426              (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
427             (IgnoredTypes.contains(TD->getName())))
428           TypesToDelete.push_back(T);
429       }
430     }
431   }
432   for (const Type *T : TypesToDelete)
433     ThrownExceptions.erase(T);
434 
435   reevaluateBehaviour();
436   return *this;
437 }
438 
439 void ExceptionAnalyzer::ExceptionInfo::clear() {
440   Behaviour = State::NotThrowing;
441   ContainsUnknown = false;
442   ThrownExceptions.clear();
443 }
444 
445 void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
446   if (ThrownExceptions.empty())
447     if (ContainsUnknown)
448       Behaviour = State::Unknown;
449     else
450       Behaviour = State::NotThrowing;
451   else
452     Behaviour = State::Throwing;
453 }
454 
455 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
456     const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
457     llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
458   if (!Func || CallStack.contains(Func) ||
459       (!CallStack.empty() && !canThrow(Func)))
460     return ExceptionInfo::createNonThrowing();
461 
462   if (const Stmt *Body = Func->getBody()) {
463     CallStack.insert(Func);
464     ExceptionInfo Result = throwsException(Body, Caught, CallStack);
465 
466     // For a constructor, we also have to check the initializers.
467     if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
468       for (const CXXCtorInitializer *Init : Ctor->inits()) {
469         ExceptionInfo Excs =
470             throwsException(Init->getInit(), Caught, CallStack);
471         Result.merge(Excs);
472       }
473     }
474 
475     CallStack.erase(Func);
476     return Result;
477   }
478 
479   auto Result = ExceptionInfo::createUnknown();
480   if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
481     for (const QualType &Ex : FPT->exceptions())
482       Result.registerException(Ex.getTypePtr());
483   }
484   return Result;
485 }
486 
487 /// Analyzes a single statement on it's throwing behaviour. This is in principle
488 /// possible except some 'Unknown' functions are called.
489 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
490     const Stmt *St, const ExceptionInfo::Throwables &Caught,
491     llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
492   auto Results = ExceptionInfo::createNonThrowing();
493   if (!St)
494     return Results;
495 
496   if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
497     if (const auto *ThrownExpr = Throw->getSubExpr()) {
498       const auto *ThrownType =
499           ThrownExpr->getType()->getUnqualifiedDesugaredType();
500       if (ThrownType->isReferenceType())
501         ThrownType = ThrownType->castAs<ReferenceType>()
502                          ->getPointeeType()
503                          ->getUnqualifiedDesugaredType();
504       Results.registerException(
505           ThrownExpr->getType()->getUnqualifiedDesugaredType());
506     } else
507       // A rethrow of a caught exception happens which makes it possible
508       // to throw all exception that are caught in the 'catch' clause of
509       // the parent try-catch block.
510       Results.registerExceptions(Caught);
511   } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
512     ExceptionInfo Uncaught =
513         throwsException(Try->getTryBlock(), Caught, CallStack);
514     for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
515       const CXXCatchStmt *Catch = Try->getHandler(I);
516 
517       // Everything is caught through 'catch(...)'.
518       if (!Catch->getExceptionDecl()) {
519         ExceptionInfo Rethrown = throwsException(
520             Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
521         Results.merge(Rethrown);
522         Uncaught.clear();
523       } else {
524         const auto *CaughtType =
525             Catch->getCaughtType()->getUnqualifiedDesugaredType();
526         if (CaughtType->isReferenceType()) {
527           CaughtType = CaughtType->castAs<ReferenceType>()
528                            ->getPointeeType()
529                            ->getUnqualifiedDesugaredType();
530         }
531 
532         // If the caught exception will catch multiple previously potential
533         // thrown types (because it's sensitive to inheritance) the throwing
534         // situation changes. First of all filter the exception types and
535         // analyze if the baseclass-exception is rethrown.
536         if (Uncaught.filterByCatch(
537                 CaughtType, Catch->getExceptionDecl()->getASTContext())) {
538           ExceptionInfo::Throwables CaughtExceptions;
539           CaughtExceptions.insert(CaughtType);
540           ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
541                                                    CaughtExceptions, CallStack);
542           Results.merge(Rethrown);
543         }
544       }
545     }
546     Results.merge(Uncaught);
547   } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
548     if (const FunctionDecl *Func = Call->getDirectCallee()) {
549       ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
550       Results.merge(Excs);
551     }
552   } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
553     ExceptionInfo Excs =
554         throwsException(Construct->getConstructor(), Caught, CallStack);
555     Results.merge(Excs);
556   } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
557     ExceptionInfo Excs =
558         throwsException(DefaultInit->getExpr(), Caught, CallStack);
559     Results.merge(Excs);
560   } else if (const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
561     for (const Stmt *Child : Coro->childrenExclBody()) {
562       if (Child != Coro->getExceptionHandler()) {
563         ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
564         Results.merge(Excs);
565       }
566     }
567     ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
568     Results.merge(throwsException(Coro->getExceptionHandler(),
569                                   Excs.getExceptionTypes(), CallStack));
570     for (const Type *Throwable : Excs.getExceptionTypes()) {
571       if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
572         ExceptionInfo DestructorExcs =
573             throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
574         Results.merge(DestructorExcs);
575       }
576     }
577   } else {
578     for (const Stmt *Child : St->children()) {
579       ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
580       Results.merge(Excs);
581     }
582   }
583   return Results;
584 }
585 
586 ExceptionAnalyzer::ExceptionInfo
587 ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
588   ExceptionInfo ExceptionList;
589 
590   // Check if the function has already been analyzed and reuse that result.
591   const auto CacheEntry = FunctionCache.find(Func);
592   if (CacheEntry == FunctionCache.end()) {
593     llvm::SmallSet<const FunctionDecl *, 32> CallStack;
594     ExceptionList =
595         throwsException(Func, ExceptionInfo::Throwables(), CallStack);
596 
597     // Cache the result of the analysis. This is done prior to filtering
598     // because it is best to keep as much information as possible.
599     // The results here might be relevant to different analysis passes
600     // with different needs as well.
601     FunctionCache.try_emplace(Func, ExceptionList);
602   } else
603     ExceptionList = CacheEntry->getSecond();
604 
605   return ExceptionList;
606 }
607 
608 ExceptionAnalyzer::ExceptionInfo
609 ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
610   llvm::SmallSet<const FunctionDecl *, 32> CallStack;
611   return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
612 }
613 
614 template <typename T>
615 ExceptionAnalyzer::ExceptionInfo
616 ExceptionAnalyzer::analyzeDispatch(const T *Node) {
617   ExceptionInfo ExceptionList = analyzeImpl(Node);
618 
619   if (ExceptionList.getBehaviour() == State::NotThrowing ||
620       ExceptionList.getBehaviour() == State::Unknown)
621     return ExceptionList;
622 
623   // Remove all ignored exceptions from the list of exceptions that can be
624   // thrown.
625   ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
626 
627   return ExceptionList;
628 }
629 
630 ExceptionAnalyzer::ExceptionInfo
631 ExceptionAnalyzer::analyze(const FunctionDecl *Func) {
632   return analyzeDispatch(Func);
633 }
634 
635 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::analyze(const Stmt *Stmt) {
636   return analyzeDispatch(Stmt);
637 }
638 
639 } // namespace clang::tidy::utils
640