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