xref: /llvm-project/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp (revision fd2e0483de089fb1459bf440d74e5b4e648a429f)
19b127421SJonas Toth //===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===//
29b127421SJonas Toth //
39b127421SJonas Toth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49b127421SJonas Toth // See https://llvm.org/LICENSE.txt for license information.
59b127421SJonas Toth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69b127421SJonas Toth //
79b127421SJonas Toth //===----------------------------------------------------------------------===//
89b127421SJonas Toth 
99b127421SJonas Toth #include "ExceptionAnalyzer.h"
109b127421SJonas Toth 
117d2ea6c4SCarlos Galvez namespace clang::tidy::utils {
1232d5b252SJonas Toth 
1332d5b252SJonas Toth void ExceptionAnalyzer::ExceptionInfo::registerException(
1432d5b252SJonas Toth     const Type *ExceptionType) {
1532d5b252SJonas Toth   assert(ExceptionType != nullptr && "Only valid types are accepted");
1632d5b252SJonas Toth   Behaviour = State::Throwing;
1732d5b252SJonas Toth   ThrownExceptions.insert(ExceptionType);
1832d5b252SJonas Toth }
1932d5b252SJonas Toth 
2032d5b252SJonas Toth void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
2132d5b252SJonas Toth     const Throwables &Exceptions) {
22c5a4f29eSPiotr Zegar   if (Exceptions.empty())
2332d5b252SJonas Toth     return;
2432d5b252SJonas Toth   Behaviour = State::Throwing;
2532d5b252SJonas Toth   ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
2632d5b252SJonas Toth }
2732d5b252SJonas Toth 
2832d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge(
2932d5b252SJonas Toth     const ExceptionAnalyzer::ExceptionInfo &Other) {
3032d5b252SJonas Toth   // Only the following two cases require an update to the local
3132d5b252SJonas Toth   // 'Behaviour'. If the local entity is already throwing there will be no
3232d5b252SJonas Toth   // change and if the other entity is throwing the merged entity will throw
3332d5b252SJonas Toth   // as well.
3432d5b252SJonas Toth   // If one of both entities is 'Unknown' and the other one does not throw
3532d5b252SJonas Toth   // the merged entity is 'Unknown' as well.
3632d5b252SJonas Toth   if (Other.Behaviour == State::Throwing)
3732d5b252SJonas Toth     Behaviour = State::Throwing;
3832d5b252SJonas Toth   else if (Other.Behaviour == State::Unknown && Behaviour == State::NotThrowing)
3932d5b252SJonas Toth     Behaviour = State::Unknown;
4032d5b252SJonas Toth 
4132d5b252SJonas Toth   ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
4232d5b252SJonas Toth   ThrownExceptions.insert(Other.ThrownExceptions.begin(),
4332d5b252SJonas Toth                           Other.ThrownExceptions.end());
4432d5b252SJonas Toth   return *this;
4532d5b252SJonas Toth }
4632d5b252SJonas Toth 
470303eafcSisuckatcs // FIXME: This could be ported to clang later.
480303eafcSisuckatcs namespace {
490303eafcSisuckatcs 
500303eafcSisuckatcs bool isUnambiguousPublicBaseClass(const Type *DerivedType,
510303eafcSisuckatcs                                   const Type *BaseType) {
520303eafcSisuckatcs   const auto *DerivedClass =
530303eafcSisuckatcs       DerivedType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
540303eafcSisuckatcs   const auto *BaseClass =
550303eafcSisuckatcs       BaseType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
560303eafcSisuckatcs   if (!DerivedClass || !BaseClass)
570303eafcSisuckatcs     return false;
580303eafcSisuckatcs 
590303eafcSisuckatcs   CXXBasePaths Paths;
600303eafcSisuckatcs   Paths.setOrigin(DerivedClass);
610303eafcSisuckatcs 
620303eafcSisuckatcs   bool IsPublicBaseClass = false;
630303eafcSisuckatcs   DerivedClass->lookupInBases(
640303eafcSisuckatcs       [&BaseClass, &IsPublicBaseClass](const CXXBaseSpecifier *BS,
650303eafcSisuckatcs                                        CXXBasePath &) {
660303eafcSisuckatcs         if (BS->getType()
670303eafcSisuckatcs                     ->getCanonicalTypeUnqualified()
680303eafcSisuckatcs                     ->getAsCXXRecordDecl() == BaseClass &&
690303eafcSisuckatcs             BS->getAccessSpecifier() == AS_public) {
700303eafcSisuckatcs           IsPublicBaseClass = true;
710303eafcSisuckatcs           return true;
720303eafcSisuckatcs         }
730303eafcSisuckatcs 
740303eafcSisuckatcs         return false;
750303eafcSisuckatcs       },
760303eafcSisuckatcs       Paths);
770303eafcSisuckatcs 
780303eafcSisuckatcs   return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
790303eafcSisuckatcs          IsPublicBaseClass;
800303eafcSisuckatcs }
810303eafcSisuckatcs 
820303eafcSisuckatcs inline bool isPointerOrPointerToMember(const Type *T) {
830303eafcSisuckatcs   return T->isPointerType() || T->isMemberPointerType();
840303eafcSisuckatcs }
850303eafcSisuckatcs 
860303eafcSisuckatcs std::optional<QualType> getPointeeOrArrayElementQualType(QualType T) {
870303eafcSisuckatcs   if (T->isAnyPointerType() || T->isMemberPointerType())
880303eafcSisuckatcs     return T->getPointeeType();
890303eafcSisuckatcs 
900303eafcSisuckatcs   if (T->isArrayType())
910303eafcSisuckatcs     return T->getAsArrayTypeUnsafe()->getElementType();
920303eafcSisuckatcs 
930303eafcSisuckatcs   return std::nullopt;
940303eafcSisuckatcs }
950303eafcSisuckatcs 
960303eafcSisuckatcs bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
979b127421SJonas Toth   const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
989b127421SJonas Toth   const auto *BaseClass = BaseType->getAsCXXRecordDecl();
999b127421SJonas Toth   if (!DerivedClass || !BaseClass)
1009b127421SJonas Toth     return false;
1019b127421SJonas Toth 
1029b127421SJonas Toth   return !DerivedClass->forallBases(
1039b127421SJonas Toth       [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
1049b127421SJonas Toth }
1059b127421SJonas Toth 
1060303eafcSisuckatcs // Check if T1 is more or Equally qualified than T2.
1070303eafcSisuckatcs bool moreOrEquallyQualified(QualType T1, QualType T2) {
1080303eafcSisuckatcs   return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
1090303eafcSisuckatcs          T1.getQualifiers() == T2.getQualifiers();
1100303eafcSisuckatcs }
1110303eafcSisuckatcs 
1120303eafcSisuckatcs bool isStandardPointerConvertible(QualType From, QualType To) {
1130303eafcSisuckatcs   assert((From->isPointerType() || From->isMemberPointerType()) &&
1140303eafcSisuckatcs          (To->isPointerType() || To->isMemberPointerType()) &&
1150303eafcSisuckatcs          "Pointer conversion should be performed on pointer types only.");
1160303eafcSisuckatcs 
1170303eafcSisuckatcs   if (!moreOrEquallyQualified(To->getPointeeType(), From->getPointeeType()))
1180303eafcSisuckatcs     return false;
1190303eafcSisuckatcs 
1200303eafcSisuckatcs   // (1)
1210303eafcSisuckatcs   // A null pointer constant can be converted to a pointer type ...
1220303eafcSisuckatcs   // The conversion of a null pointer constant to a pointer to cv-qualified type
1230303eafcSisuckatcs   // is a single conversion, and not the sequence of a pointer conversion
1240303eafcSisuckatcs   // followed by a qualification conversion. A null pointer constant of integral
1250303eafcSisuckatcs   // type can be converted to a prvalue of type std::nullptr_t
1260303eafcSisuckatcs   if (To->isPointerType() && From->isNullPtrType())
1270303eafcSisuckatcs     return true;
1280303eafcSisuckatcs 
1290303eafcSisuckatcs   // (2)
1300303eafcSisuckatcs   // A prvalue of type “pointer to cv T”, where T is an object type, can be
1310303eafcSisuckatcs   // converted to a prvalue of type “pointer to cv void”.
1320303eafcSisuckatcs   if (To->isVoidPointerType() && From->isObjectPointerType())
1330303eafcSisuckatcs     return true;
1340303eafcSisuckatcs 
1350303eafcSisuckatcs   // (3)
1360303eafcSisuckatcs   // A prvalue of type “pointer to cv D”, where D is a complete class type, can
1370303eafcSisuckatcs   // be converted to a prvalue of type “pointer to cv B”, where B is a base
1380303eafcSisuckatcs   // class of D. If B is an inaccessible or ambiguous base class of D, a program
1390303eafcSisuckatcs   // that necessitates this conversion is ill-formed.
1400303eafcSisuckatcs   if (const auto *RD = From->getPointeeCXXRecordDecl()) {
1410303eafcSisuckatcs     if (RD->isCompleteDefinition() &&
1420303eafcSisuckatcs         isBaseOf(From->getPointeeType().getTypePtr(),
1430303eafcSisuckatcs                  To->getPointeeType().getTypePtr())) {
14452dd4dbbSHana Dusíková       // If B is an inaccessible or ambiguous base class of D, a program
14552dd4dbbSHana Dusíková       // that necessitates this conversion is ill-formed
14652dd4dbbSHana Dusíková       return isUnambiguousPublicBaseClass(From->getPointeeType().getTypePtr(),
14752dd4dbbSHana Dusíková                                           To->getPointeeType().getTypePtr());
1480303eafcSisuckatcs     }
1490303eafcSisuckatcs   }
1500303eafcSisuckatcs 
1510303eafcSisuckatcs   return false;
1520303eafcSisuckatcs }
1530303eafcSisuckatcs 
1540303eafcSisuckatcs bool isFunctionPointerConvertible(QualType From, QualType To) {
1550303eafcSisuckatcs   if (!From->isFunctionPointerType() && !From->isFunctionType() &&
1560303eafcSisuckatcs       !From->isMemberFunctionPointerType())
1570303eafcSisuckatcs     return false;
1580303eafcSisuckatcs 
1590303eafcSisuckatcs   if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
1600303eafcSisuckatcs     return false;
1610303eafcSisuckatcs 
1620303eafcSisuckatcs   if (To->isFunctionPointerType()) {
1630303eafcSisuckatcs     if (From->isFunctionPointerType())
1640303eafcSisuckatcs       return To->getPointeeType() == From->getPointeeType();
1650303eafcSisuckatcs 
1660303eafcSisuckatcs     if (From->isFunctionType())
1670303eafcSisuckatcs       return To->getPointeeType() == From;
1680303eafcSisuckatcs 
1690303eafcSisuckatcs     return false;
1700303eafcSisuckatcs   }
1710303eafcSisuckatcs 
1720303eafcSisuckatcs   if (To->isMemberFunctionPointerType()) {
1730303eafcSisuckatcs     if (!From->isMemberFunctionPointerType())
1740303eafcSisuckatcs       return false;
1750303eafcSisuckatcs 
1760303eafcSisuckatcs     const auto *FromMember = cast<MemberPointerType>(From);
1770303eafcSisuckatcs     const auto *ToMember = cast<MemberPointerType>(To);
1780303eafcSisuckatcs 
1790303eafcSisuckatcs     // Note: converting Derived::* to Base::* is a different kind of conversion,
1800303eafcSisuckatcs     // called Pointer-to-member conversion.
1810303eafcSisuckatcs     return FromMember->getClass() == ToMember->getClass() &&
1820303eafcSisuckatcs            FromMember->getPointeeType() == ToMember->getPointeeType();
1830303eafcSisuckatcs   }
1840303eafcSisuckatcs 
1850303eafcSisuckatcs   return false;
1860303eafcSisuckatcs }
1870303eafcSisuckatcs 
1880303eafcSisuckatcs // Checks if From is qualification convertible to To based on the current
1890303eafcSisuckatcs // LangOpts. If From is any array, we perform the array to pointer conversion
1900303eafcSisuckatcs // first. The function only performs checks based on C++ rules, which can differ
1910303eafcSisuckatcs // from the C rules.
1920303eafcSisuckatcs //
1930303eafcSisuckatcs // The function should only be called in C++ mode.
1940303eafcSisuckatcs bool isQualificationConvertiblePointer(QualType From, QualType To,
1950303eafcSisuckatcs                                        LangOptions LangOpts) {
1960303eafcSisuckatcs 
1970303eafcSisuckatcs   // [N4659 7.5 (1)]
1980303eafcSisuckatcs   // A cv-decomposition of a type T is a sequence of cv_i and P_i such that T is
1990303eafcSisuckatcs   //    cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U” for n > 0,
2000303eafcSisuckatcs   // where each cv_i is a set of cv-qualifiers, and each P_i is “pointer to”,
2010303eafcSisuckatcs   // “pointer to member of class C_i of type”, “array of N_i”, or
2020303eafcSisuckatcs   // “array of unknown bound of”.
2030303eafcSisuckatcs   //
2040303eafcSisuckatcs   // If P_i designates an array, the cv-qualifiers cv_i+1 on the element type
2050303eafcSisuckatcs   // are also taken as the cv-qualifiers cvi of the array.
2060303eafcSisuckatcs   //
2070303eafcSisuckatcs   // The n-tuple of cv-qualifiers after the first one in the longest
2080303eafcSisuckatcs   // cv-decomposition of T, that is, cv_1, cv_2, ... , cv_n, is called the
2090303eafcSisuckatcs   // cv-qualification signature of T.
2100303eafcSisuckatcs 
2110303eafcSisuckatcs   auto isValidP_i = [](QualType P) {
2120303eafcSisuckatcs     return P->isPointerType() || P->isMemberPointerType() ||
2130303eafcSisuckatcs            P->isConstantArrayType() || P->isIncompleteArrayType();
2140303eafcSisuckatcs   };
2150303eafcSisuckatcs 
2160303eafcSisuckatcs   auto isSameP_i = [](QualType P1, QualType P2) {
2170303eafcSisuckatcs     if (P1->isPointerType())
2180303eafcSisuckatcs       return P2->isPointerType();
2190303eafcSisuckatcs 
2200303eafcSisuckatcs     if (P1->isMemberPointerType())
2210303eafcSisuckatcs       return P2->isMemberPointerType() &&
2220303eafcSisuckatcs              P1->getAs<MemberPointerType>()->getClass() ==
2230303eafcSisuckatcs                  P2->getAs<MemberPointerType>()->getClass();
2240303eafcSisuckatcs 
2250303eafcSisuckatcs     if (P1->isConstantArrayType())
2260303eafcSisuckatcs       return P2->isConstantArrayType() &&
2270303eafcSisuckatcs              cast<ConstantArrayType>(P1)->getSize() ==
2280303eafcSisuckatcs                  cast<ConstantArrayType>(P2)->getSize();
2290303eafcSisuckatcs 
2300303eafcSisuckatcs     if (P1->isIncompleteArrayType())
2310303eafcSisuckatcs       return P2->isIncompleteArrayType();
2320303eafcSisuckatcs 
2330303eafcSisuckatcs     return false;
2340303eafcSisuckatcs   };
2350303eafcSisuckatcs 
2360303eafcSisuckatcs   // (2)
2370303eafcSisuckatcs   // Two types From and To are similar if they have cv-decompositions with the
2380303eafcSisuckatcs   // same n such that corresponding P_i components are the same [(added by
2390303eafcSisuckatcs   // N4849 7.3.5) or one is “array of N_i” and the other is “array of unknown
2400303eafcSisuckatcs   // bound of”], and the types denoted by U are the same.
2410303eafcSisuckatcs   //
2420303eafcSisuckatcs   // (3)
2430303eafcSisuckatcs   // A prvalue expression of type From can be converted to type To if the
2440303eafcSisuckatcs   // following conditions are satisfied:
2450303eafcSisuckatcs   //  - From and To are similar
2460303eafcSisuckatcs   //  - For every i > 0, if const is in cv_i of From then const is in cv_i of
2470303eafcSisuckatcs   //  To, and similarly for volatile.
2480303eafcSisuckatcs   //  - [(derived from addition by N4849 7.3.5) If P_i of From is “array of
2490303eafcSisuckatcs   //  unknown bound of”, P_i of To is “array of unknown bound of”.]
2500303eafcSisuckatcs   //  - If the cv_i of From and cv_i of To are different, then const is in every
2510303eafcSisuckatcs   //  cv_k of To for 0 < k < i.
2520303eafcSisuckatcs 
2530303eafcSisuckatcs   int I = 0;
2540303eafcSisuckatcs   bool ConstUntilI = true;
2550303eafcSisuckatcs   auto SatisfiesCVRules = [&I, &ConstUntilI](const QualType &From,
2560303eafcSisuckatcs                                              const QualType &To) {
2570303eafcSisuckatcs     if (I > 1) {
2580303eafcSisuckatcs       if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
2590303eafcSisuckatcs         return false;
2600303eafcSisuckatcs     }
2610303eafcSisuckatcs 
2620303eafcSisuckatcs     if (I > 0) {
2630303eafcSisuckatcs       if (From.isConstQualified() && !To.isConstQualified())
2640303eafcSisuckatcs         return false;
2650303eafcSisuckatcs 
2660303eafcSisuckatcs       if (From.isVolatileQualified() && !To.isVolatileQualified())
2670303eafcSisuckatcs         return false;
2680303eafcSisuckatcs 
2690303eafcSisuckatcs       ConstUntilI = To.isConstQualified();
2700303eafcSisuckatcs     }
2710303eafcSisuckatcs 
2720303eafcSisuckatcs     return true;
2730303eafcSisuckatcs   };
2740303eafcSisuckatcs 
2750303eafcSisuckatcs   while (isValidP_i(From) && isValidP_i(To)) {
2760303eafcSisuckatcs     // Remove every sugar.
2770303eafcSisuckatcs     From = From.getCanonicalType();
2780303eafcSisuckatcs     To = To.getCanonicalType();
2790303eafcSisuckatcs 
2800303eafcSisuckatcs     if (!SatisfiesCVRules(From, To))
2810303eafcSisuckatcs       return false;
2820303eafcSisuckatcs 
2830303eafcSisuckatcs     if (!isSameP_i(From, To)) {
2840303eafcSisuckatcs       if (LangOpts.CPlusPlus20) {
2850303eafcSisuckatcs         if (From->isConstantArrayType() && !To->isIncompleteArrayType())
2860303eafcSisuckatcs           return false;
2870303eafcSisuckatcs 
2880303eafcSisuckatcs         if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
2890303eafcSisuckatcs           return false;
2900303eafcSisuckatcs 
2910303eafcSisuckatcs       } else {
2920303eafcSisuckatcs         return false;
2930303eafcSisuckatcs       }
2940303eafcSisuckatcs     }
2950303eafcSisuckatcs 
2960303eafcSisuckatcs     ++I;
2970303eafcSisuckatcs     std::optional<QualType> FromPointeeOrElem =
2980303eafcSisuckatcs         getPointeeOrArrayElementQualType(From);
2990303eafcSisuckatcs     std::optional<QualType> ToPointeeOrElem =
3000303eafcSisuckatcs         getPointeeOrArrayElementQualType(To);
3010303eafcSisuckatcs 
3020303eafcSisuckatcs     assert(FromPointeeOrElem &&
3030303eafcSisuckatcs            "From pointer or array has no pointee or element!");
3040303eafcSisuckatcs     assert(ToPointeeOrElem && "To pointer or array has no pointee or element!");
3050303eafcSisuckatcs 
3060303eafcSisuckatcs     From = *FromPointeeOrElem;
3070303eafcSisuckatcs     To = *ToPointeeOrElem;
3080303eafcSisuckatcs   }
3090303eafcSisuckatcs 
3100303eafcSisuckatcs   // In this case the length (n) of From and To are not the same.
3110303eafcSisuckatcs   if (isValidP_i(From) || isValidP_i(To))
3120303eafcSisuckatcs     return false;
3130303eafcSisuckatcs 
3140303eafcSisuckatcs   // We hit U.
3150303eafcSisuckatcs   if (!SatisfiesCVRules(From, To))
3160303eafcSisuckatcs     return false;
3170303eafcSisuckatcs 
3180303eafcSisuckatcs   return From.getTypePtr() == To.getTypePtr();
3190303eafcSisuckatcs }
3200303eafcSisuckatcs } // namespace
3210303eafcSisuckatcs 
32227245077SPiotr Zegar static bool canThrow(const FunctionDecl *Func) {
323*fd2e0483SCongcong Cai   // consteval specifies that every call to the function must produce a
324*fd2e0483SCongcong Cai   // compile-time constant, which cannot evaluate a throw expression without
325*fd2e0483SCongcong Cai   // producing a compilation error.
326*fd2e0483SCongcong Cai   if (Func->isConsteval())
327*fd2e0483SCongcong Cai     return false;
328*fd2e0483SCongcong Cai 
32927245077SPiotr Zegar   const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
33027245077SPiotr Zegar   if (!FunProto)
33127245077SPiotr Zegar     return true;
33227245077SPiotr Zegar 
33327245077SPiotr Zegar   switch (FunProto->canThrow()) {
33427245077SPiotr Zegar   case CT_Cannot:
33527245077SPiotr Zegar     return false;
33627245077SPiotr Zegar   case CT_Dependent: {
33727245077SPiotr Zegar     const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
33827245077SPiotr Zegar     if (!NoexceptExpr)
33927245077SPiotr Zegar       return true; // no noexept - can throw
34027245077SPiotr Zegar 
34127245077SPiotr Zegar     if (NoexceptExpr->isValueDependent())
34227245077SPiotr Zegar       return true; // depend on template - some instance can throw
34327245077SPiotr Zegar 
34427245077SPiotr Zegar     bool Result = false;
34527245077SPiotr Zegar     if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
34627245077SPiotr Zegar                                                   /*InConstantContext=*/true))
34727245077SPiotr Zegar       return true;  // complex X condition in noexcept(X), cannot validate,
34827245077SPiotr Zegar                     // assume that may throw
34927245077SPiotr Zegar     return !Result; // noexcept(false) - can throw
35027245077SPiotr Zegar   }
35127245077SPiotr Zegar   default:
35227245077SPiotr Zegar     return true;
35327245077SPiotr Zegar   };
35427245077SPiotr Zegar }
35527245077SPiotr Zegar 
3560303eafcSisuckatcs bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
3570303eafcSisuckatcs     const Type *HandlerTy, const ASTContext &Context) {
35832d5b252SJonas Toth   llvm::SmallVector<const Type *, 8> TypesToDelete;
3590303eafcSisuckatcs   for (const Type *ExceptionTy : ThrownExceptions) {
3600303eafcSisuckatcs     CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
3610303eafcSisuckatcs     CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
3620303eafcSisuckatcs 
3630303eafcSisuckatcs     // The handler is of type cv T or cv T& and E and T are the same type
3640303eafcSisuckatcs     // (ignoring the top-level cv-qualifiers) ...
3650303eafcSisuckatcs     if (ExceptionCanTy == HandlerCanTy) {
3660303eafcSisuckatcs       TypesToDelete.push_back(ExceptionTy);
3670303eafcSisuckatcs     }
3680303eafcSisuckatcs 
3690303eafcSisuckatcs     // The handler is of type cv T or cv T& and T is an unambiguous public base
3700303eafcSisuckatcs     // class of E ...
3710303eafcSisuckatcs     else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
3720303eafcSisuckatcs                                           HandlerCanTy->getTypePtr())) {
3730303eafcSisuckatcs       TypesToDelete.push_back(ExceptionTy);
3740303eafcSisuckatcs     }
3750303eafcSisuckatcs 
3760303eafcSisuckatcs     if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
3770303eafcSisuckatcs         (HandlerCanTy->getTypeClass() == Type::LValueReference &&
3780303eafcSisuckatcs          !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
3790303eafcSisuckatcs       continue;
3800303eafcSisuckatcs     // The handler is of type cv T or const T& where T is a pointer or
3810303eafcSisuckatcs     // pointer-to-member type and E is a pointer or pointer-to-member type that
3820303eafcSisuckatcs     // can be converted to T by one or more of ...
3830303eafcSisuckatcs     if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
3840303eafcSisuckatcs         isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
3850303eafcSisuckatcs       // A standard pointer conversion not involving conversions to pointers to
3860303eafcSisuckatcs       // private or protected or ambiguous classes ...
38752dd4dbbSHana Dusíková       if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
3880303eafcSisuckatcs         TypesToDelete.push_back(ExceptionTy);
3890303eafcSisuckatcs       }
3900303eafcSisuckatcs       // A function pointer conversion ...
3910303eafcSisuckatcs       else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
3920303eafcSisuckatcs         TypesToDelete.push_back(ExceptionTy);
3930303eafcSisuckatcs       }
3940303eafcSisuckatcs       // A a qualification conversion ...
3950303eafcSisuckatcs       else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
3960303eafcSisuckatcs                                                  Context.getLangOpts())) {
3970303eafcSisuckatcs         TypesToDelete.push_back(ExceptionTy);
3980303eafcSisuckatcs       }
3990303eafcSisuckatcs     }
4000303eafcSisuckatcs 
4010303eafcSisuckatcs     // The handler is of type cv T or const T& where T is a pointer or
4020303eafcSisuckatcs     // pointer-to-member type and E is std::nullptr_t.
4030303eafcSisuckatcs     else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
4040303eafcSisuckatcs              ExceptionCanTy->isNullPtrType()) {
4050303eafcSisuckatcs       TypesToDelete.push_back(ExceptionTy);
4060303eafcSisuckatcs     }
40732d5b252SJonas Toth   }
4089b127421SJonas Toth 
40932d5b252SJonas Toth   for (const Type *T : TypesToDelete)
41032d5b252SJonas Toth     ThrownExceptions.erase(T);
41132d5b252SJonas Toth 
41232d5b252SJonas Toth   reevaluateBehaviour();
413c5a4f29eSPiotr Zegar   return !TypesToDelete.empty();
41432d5b252SJonas Toth }
41532d5b252SJonas Toth 
41632d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo &
41732d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
41832d5b252SJonas Toth     const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) {
41932d5b252SJonas Toth   llvm::SmallVector<const Type *, 8> TypesToDelete;
42032d5b252SJonas Toth   // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible.
42132d5b252SJonas Toth   // Therefore this slightly hacky implementation is required.
42232d5b252SJonas Toth   for (const Type *T : ThrownExceptions) {
42332d5b252SJonas Toth     if (const auto *TD = T->getAsTagDecl()) {
42432d5b252SJonas Toth       if (TD->getDeclName().isIdentifier()) {
42532d5b252SJonas Toth         if ((IgnoreBadAlloc &&
42632d5b252SJonas Toth              (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
427b3e2b1a7SCongcong Cai             (IgnoredTypes.contains(TD->getName())))
42832d5b252SJonas Toth           TypesToDelete.push_back(T);
42932d5b252SJonas Toth       }
43032d5b252SJonas Toth     }
43132d5b252SJonas Toth   }
43232d5b252SJonas Toth   for (const Type *T : TypesToDelete)
43332d5b252SJonas Toth     ThrownExceptions.erase(T);
43432d5b252SJonas Toth 
43532d5b252SJonas Toth   reevaluateBehaviour();
43632d5b252SJonas Toth   return *this;
43732d5b252SJonas Toth }
43832d5b252SJonas Toth 
43932d5b252SJonas Toth void ExceptionAnalyzer::ExceptionInfo::clear() {
44032d5b252SJonas Toth   Behaviour = State::NotThrowing;
44132d5b252SJonas Toth   ContainsUnknown = false;
44232d5b252SJonas Toth   ThrownExceptions.clear();
44332d5b252SJonas Toth }
44432d5b252SJonas Toth 
44532d5b252SJonas Toth void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
446c5a4f29eSPiotr Zegar   if (ThrownExceptions.empty())
44732d5b252SJonas Toth     if (ContainsUnknown)
44832d5b252SJonas Toth       Behaviour = State::Unknown;
44932d5b252SJonas Toth     else
45032d5b252SJonas Toth       Behaviour = State::NotThrowing;
45132d5b252SJonas Toth   else
45232d5b252SJonas Toth     Behaviour = State::Throwing;
45332d5b252SJonas Toth }
45432d5b252SJonas Toth 
45532d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
456daac014fSDeniz Evrenci     const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
4579b127421SJonas Toth     llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
458b3e2b1a7SCongcong Cai   if (!Func || CallStack.contains(Func) ||
459b3e2b1a7SCongcong Cai       (!CallStack.empty() && !canThrow(Func)))
46032d5b252SJonas Toth     return ExceptionInfo::createNonThrowing();
4619b127421SJonas Toth 
4629b127421SJonas Toth   if (const Stmt *Body = Func->getBody()) {
4639b127421SJonas Toth     CallStack.insert(Func);
464daac014fSDeniz Evrenci     ExceptionInfo Result = throwsException(Body, Caught, CallStack);
465d3b188a2SFabian Wolff 
466d3b188a2SFabian Wolff     // For a constructor, we also have to check the initializers.
467d3b188a2SFabian Wolff     if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
468d3b188a2SFabian Wolff       for (const CXXCtorInitializer *Init : Ctor->inits()) {
469daac014fSDeniz Evrenci         ExceptionInfo Excs =
470daac014fSDeniz Evrenci             throwsException(Init->getInit(), Caught, CallStack);
471d3b188a2SFabian Wolff         Result.merge(Excs);
472d3b188a2SFabian Wolff       }
473d3b188a2SFabian Wolff     }
474d3b188a2SFabian Wolff 
4759b127421SJonas Toth     CallStack.erase(Func);
4769b127421SJonas Toth     return Result;
4779b127421SJonas Toth   }
4789b127421SJonas Toth 
47932d5b252SJonas Toth   auto Result = ExceptionInfo::createUnknown();
4809b127421SJonas Toth   if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
4818dc7b982SMark de Wever     for (const QualType &Ex : FPT->exceptions())
48232d5b252SJonas Toth       Result.registerException(Ex.getTypePtr());
4839b127421SJonas Toth   }
4849b127421SJonas Toth   return Result;
4859b127421SJonas Toth }
4869b127421SJonas Toth 
487dd5571d5SKazuaki Ishizaki /// Analyzes a single statement on it's throwing behaviour. This is in principle
48832d5b252SJonas Toth /// possible except some 'Unknown' functions are called.
48932d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
49032d5b252SJonas Toth     const Stmt *St, const ExceptionInfo::Throwables &Caught,
4919b127421SJonas Toth     llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
49232d5b252SJonas Toth   auto Results = ExceptionInfo::createNonThrowing();
4939b127421SJonas Toth   if (!St)
4949b127421SJonas Toth     return Results;
4959b127421SJonas Toth 
4969b127421SJonas Toth   if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
4979b127421SJonas Toth     if (const auto *ThrownExpr = Throw->getSubExpr()) {
4989b127421SJonas Toth       const auto *ThrownType =
4999b127421SJonas Toth           ThrownExpr->getType()->getUnqualifiedDesugaredType();
50032d5b252SJonas Toth       if (ThrownType->isReferenceType())
5019b127421SJonas Toth         ThrownType = ThrownType->castAs<ReferenceType>()
5029b127421SJonas Toth                          ->getPointeeType()
5039b127421SJonas Toth                          ->getUnqualifiedDesugaredType();
50432d5b252SJonas Toth       Results.registerException(
50532d5b252SJonas Toth           ThrownExpr->getType()->getUnqualifiedDesugaredType());
50632d5b252SJonas Toth     } else
50732d5b252SJonas Toth       // A rethrow of a caught exception happens which makes it possible
50832d5b252SJonas Toth       // to throw all exception that are caught in the 'catch' clause of
50932d5b252SJonas Toth       // the parent try-catch block.
51032d5b252SJonas Toth       Results.registerExceptions(Caught);
5119b127421SJonas Toth   } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
51232d5b252SJonas Toth     ExceptionInfo Uncaught =
51332d5b252SJonas Toth         throwsException(Try->getTryBlock(), Caught, CallStack);
514ab2d3ce4SAlexander Kornienko     for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
515ab2d3ce4SAlexander Kornienko       const CXXCatchStmt *Catch = Try->getHandler(I);
51632d5b252SJonas Toth 
517b3e2b1a7SCongcong Cai       // Everything is caught through 'catch(...)'.
5189b127421SJonas Toth       if (!Catch->getExceptionDecl()) {
51932d5b252SJonas Toth         ExceptionInfo Rethrown = throwsException(
52032d5b252SJonas Toth             Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
52132d5b252SJonas Toth         Results.merge(Rethrown);
5229b127421SJonas Toth         Uncaught.clear();
5239b127421SJonas Toth       } else {
5249b127421SJonas Toth         const auto *CaughtType =
5259b127421SJonas Toth             Catch->getCaughtType()->getUnqualifiedDesugaredType();
5269b127421SJonas Toth         if (CaughtType->isReferenceType()) {
5279b127421SJonas Toth           CaughtType = CaughtType->castAs<ReferenceType>()
5289b127421SJonas Toth                            ->getPointeeType()
5299b127421SJonas Toth                            ->getUnqualifiedDesugaredType();
5309b127421SJonas Toth         }
53132d5b252SJonas Toth 
53232d5b252SJonas Toth         // If the caught exception will catch multiple previously potential
53332d5b252SJonas Toth         // thrown types (because it's sensitive to inheritance) the throwing
53432d5b252SJonas Toth         // situation changes. First of all filter the exception types and
53532d5b252SJonas Toth         // analyze if the baseclass-exception is rethrown.
5360303eafcSisuckatcs         if (Uncaught.filterByCatch(
5370303eafcSisuckatcs                 CaughtType, Catch->getExceptionDecl()->getASTContext())) {
53832d5b252SJonas Toth           ExceptionInfo::Throwables CaughtExceptions;
53932d5b252SJonas Toth           CaughtExceptions.insert(CaughtType);
54032d5b252SJonas Toth           ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
54132d5b252SJonas Toth                                                    CaughtExceptions, CallStack);
54232d5b252SJonas Toth           Results.merge(Rethrown);
5439b127421SJonas Toth         }
5449b127421SJonas Toth       }
5459b127421SJonas Toth     }
54632d5b252SJonas Toth     Results.merge(Uncaught);
5479b127421SJonas Toth   } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
5489b127421SJonas Toth     if (const FunctionDecl *Func = Call->getDirectCallee()) {
549daac014fSDeniz Evrenci       ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
55032d5b252SJonas Toth       Results.merge(Excs);
5519b127421SJonas Toth     }
552d3b188a2SFabian Wolff   } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
553d3b188a2SFabian Wolff     ExceptionInfo Excs =
554daac014fSDeniz Evrenci         throwsException(Construct->getConstructor(), Caught, CallStack);
555d3b188a2SFabian Wolff     Results.merge(Excs);
556d3b188a2SFabian Wolff   } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
557d3b188a2SFabian Wolff     ExceptionInfo Excs =
558d3b188a2SFabian Wolff         throwsException(DefaultInit->getExpr(), Caught, CallStack);
559d3b188a2SFabian Wolff     Results.merge(Excs);
5606219b7c6SDeniz Evrenci   } else if (const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
5616219b7c6SDeniz Evrenci     for (const Stmt *Child : Coro->childrenExclBody()) {
562daac014fSDeniz Evrenci       if (Child != Coro->getExceptionHandler()) {
5636219b7c6SDeniz Evrenci         ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
5646219b7c6SDeniz Evrenci         Results.merge(Excs);
5656219b7c6SDeniz Evrenci       }
566daac014fSDeniz Evrenci     }
5676219b7c6SDeniz Evrenci     ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
568daac014fSDeniz Evrenci     Results.merge(throwsException(Coro->getExceptionHandler(),
569daac014fSDeniz Evrenci                                   Excs.getExceptionTypes(), CallStack));
5706219b7c6SDeniz Evrenci     for (const Type *Throwable : Excs.getExceptionTypes()) {
5716219b7c6SDeniz Evrenci       if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
5726219b7c6SDeniz Evrenci         ExceptionInfo DestructorExcs =
573daac014fSDeniz Evrenci             throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
5746219b7c6SDeniz Evrenci         Results.merge(DestructorExcs);
5756219b7c6SDeniz Evrenci       }
5766219b7c6SDeniz Evrenci     }
5779b127421SJonas Toth   } else {
5789b127421SJonas Toth     for (const Stmt *Child : St->children()) {
57932d5b252SJonas Toth       ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
58032d5b252SJonas Toth       Results.merge(Excs);
5819b127421SJonas Toth     }
5829b127421SJonas Toth   }
5839b127421SJonas Toth   return Results;
5849b127421SJonas Toth }
5859b127421SJonas Toth 
58632d5b252SJonas Toth ExceptionAnalyzer::ExceptionInfo
587d5ce5718SRoman Lebedev ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
58832d5b252SJonas Toth   ExceptionInfo ExceptionList;
5899b127421SJonas Toth 
59032d5b252SJonas Toth   // Check if the function has already been analyzed and reuse that result.
591e588ef8aSAMS21   const auto CacheEntry = FunctionCache.find(Func);
592e588ef8aSAMS21   if (CacheEntry == FunctionCache.end()) {
5939b127421SJonas Toth     llvm::SmallSet<const FunctionDecl *, 32> CallStack;
594daac014fSDeniz Evrenci     ExceptionList =
595daac014fSDeniz Evrenci         throwsException(Func, ExceptionInfo::Throwables(), CallStack);
59632d5b252SJonas Toth 
59732d5b252SJonas Toth     // Cache the result of the analysis. This is done prior to filtering
59832d5b252SJonas Toth     // because it is best to keep as much information as possible.
59932d5b252SJonas Toth     // The results here might be relevant to different analysis passes
60032d5b252SJonas Toth     // with different needs as well.
601e588ef8aSAMS21     FunctionCache.try_emplace(Func, ExceptionList);
60232d5b252SJonas Toth   } else
603e588ef8aSAMS21     ExceptionList = CacheEntry->getSecond();
60432d5b252SJonas Toth 
605d5ce5718SRoman Lebedev   return ExceptionList;
606d5ce5718SRoman Lebedev }
607d5ce5718SRoman Lebedev 
608462446fdSRoman Lebedev ExceptionAnalyzer::ExceptionInfo
609462446fdSRoman Lebedev ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
610462446fdSRoman Lebedev   llvm::SmallSet<const FunctionDecl *, 32> CallStack;
611462446fdSRoman Lebedev   return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
612462446fdSRoman Lebedev }
613462446fdSRoman Lebedev 
614d5ce5718SRoman Lebedev template <typename T>
615d5ce5718SRoman Lebedev ExceptionAnalyzer::ExceptionInfo
616d5ce5718SRoman Lebedev ExceptionAnalyzer::analyzeDispatch(const T *Node) {
617d5ce5718SRoman Lebedev   ExceptionInfo ExceptionList = analyzeImpl(Node);
618d5ce5718SRoman Lebedev 
61932d5b252SJonas Toth   if (ExceptionList.getBehaviour() == State::NotThrowing ||
62032d5b252SJonas Toth       ExceptionList.getBehaviour() == State::Unknown)
62132d5b252SJonas Toth     return ExceptionList;
6229b127421SJonas Toth 
6239b127421SJonas Toth   // Remove all ignored exceptions from the list of exceptions that can be
6249b127421SJonas Toth   // thrown.
62532d5b252SJonas Toth   ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
6269b127421SJonas Toth 
62732d5b252SJonas Toth   return ExceptionList;
6289b127421SJonas Toth }
629d5ce5718SRoman Lebedev 
630d5ce5718SRoman Lebedev ExceptionAnalyzer::ExceptionInfo
631d5ce5718SRoman Lebedev ExceptionAnalyzer::analyze(const FunctionDecl *Func) {
632d5ce5718SRoman Lebedev   return analyzeDispatch(Func);
633d5ce5718SRoman Lebedev }
634d5ce5718SRoman Lebedev 
635e588ef8aSAMS21 ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::analyze(const Stmt *Stmt) {
636462446fdSRoman Lebedev   return analyzeDispatch(Stmt);
637462446fdSRoman Lebedev }
638462446fdSRoman Lebedev 
6397d2ea6c4SCarlos Galvez } // namespace clang::tidy::utils
640