14 const Type *ExceptionType) {
15 assert(ExceptionType !=
nullptr &&
"Only valid types are accepted");
17 ThrownExceptions.insert(ExceptionType);
22 if (Exceptions.empty())
25 ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
41 ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
42 ThrownExceptions.insert(Other.ThrownExceptions.begin(),
43 Other.ThrownExceptions.end());
50bool 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)
60 Paths.setOrigin(DerivedClass);
62 bool IsPublicBaseClass =
false;
63 DerivedClass->lookupInBases(
64 [&BaseClass, &IsPublicBaseClass](
const CXXBaseSpecifier *BS,
67 ->getCanonicalTypeUnqualified()
68 ->getAsCXXRecordDecl() == BaseClass &&
69 BS->getAccessSpecifier() == AS_public) {
70 IsPublicBaseClass = true;
78 return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
82inline bool isPointerOrPointerToMember(
const Type *T) {
83 return T->isPointerType() || T->isMemberPointerType();
86std::optional<QualType> getPointeeOrArrayElementQualType(QualType T) {
87 if (T->isAnyPointerType() || T->isMemberPointerType())
88 return T->getPointeeType();
91 return T->getAsArrayTypeUnsafe()->getElementType();
96bool isBaseOf(
const Type *DerivedType,
const Type *BaseType) {
97 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
98 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
99 if (!DerivedClass || !BaseClass)
102 return !DerivedClass->forallBases(
103 [BaseClass](
const CXXRecordDecl *Cur) {
return Cur != BaseClass; });
107bool moreOrEquallyQualified(QualType T1, QualType T2) {
108 return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
109 T1.getQualifiers() == T2.getQualifiers();
112bool 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.");
117 if (!moreOrEquallyQualified(To->getPointeeType(), From->getPointeeType()))
126 if (To->isPointerType() && From->isNullPtrType())
132 if (To->isVoidPointerType() && From->isObjectPointerType())
140 if (
const auto *RD = From->getPointeeCXXRecordDecl()) {
141 if (RD->isCompleteDefinition() &&
142 isBaseOf(From->getPointeeType().getTypePtr(),
143 To->getPointeeType().getTypePtr())) {
146 return isUnambiguousPublicBaseClass(From->getPointeeType().getTypePtr(),
147 To->getPointeeType().getTypePtr());
154bool isFunctionPointerConvertible(QualType From, QualType To) {
155 if (!From->isFunctionPointerType() && !From->isFunctionType() &&
156 !From->isMemberFunctionPointerType())
159 if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
162 if (To->isFunctionPointerType()) {
163 if (From->isFunctionPointerType())
164 return To->getPointeeType() == From->getPointeeType();
166 if (From->isFunctionType())
167 return To->getPointeeType() == From;
172 if (To->isMemberFunctionPointerType()) {
173 if (!From->isMemberFunctionPointerType())
176 const auto *FromMember = cast<MemberPointerType>(From);
177 const auto *ToMember = cast<MemberPointerType>(To);
181 return FromMember->getClass() == ToMember->getClass() &&
182 FromMember->getPointeeType() == ToMember->getPointeeType();
194bool isQualificationConvertiblePointer(QualType From, QualType To,
195 LangOptions LangOpts) {
211 auto isValidP_i = [](QualType P) {
212 return P->isPointerType() || P->isMemberPointerType() ||
213 P->isConstantArrayType() || P->isIncompleteArrayType();
216 auto isSameP_i = [](QualType P1, QualType P2) {
217 if (P1->isPointerType())
218 return P2->isPointerType();
220 if (P1->isMemberPointerType())
221 return P2->isMemberPointerType() &&
222 P1->getAs<MemberPointerType>()->getClass() ==
223 P2->getAs<MemberPointerType>()->getClass();
225 if (P1->isConstantArrayType())
226 return P2->isConstantArrayType() &&
227 cast<ConstantArrayType>(P1)->getSize() ==
228 cast<ConstantArrayType>(P2)->getSize();
230 if (P1->isIncompleteArrayType())
231 return P2->isIncompleteArrayType();
254 bool ConstUntilI =
true;
255 auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
256 const QualType &To) {
258 if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
263 if (From.isConstQualified() && !To.isConstQualified())
266 if (From.isVolatileQualified() && !To.isVolatileQualified())
269 ConstUntilI = To.isConstQualified();
275 while (isValidP_i(From) && isValidP_i(To)) {
277 From = From.getCanonicalType();
278 To = To.getCanonicalType();
280 if (!SatisfiesCVRules(From, To))
283 if (!isSameP_i(From, To)) {
284 if (LangOpts.CPlusPlus20) {
285 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
288 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
297 std::optional<QualType> FromPointeeOrElem =
298 getPointeeOrArrayElementQualType(From);
299 std::optional<QualType> ToPointeeOrElem =
300 getPointeeOrArrayElementQualType(To);
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!");
306 From = *FromPointeeOrElem;
307 To = *ToPointeeOrElem;
311 if (isValidP_i(From) || isValidP_i(To))
315 if (!SatisfiesCVRules(From, To))
318 return From.getTypePtr() == To.getTypePtr();
323 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
327 switch (FunProto->canThrow()) {
331 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
335 if (NoexceptExpr->isValueDependent())
339 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
351 const Type *HandlerTy,
const ASTContext &Context) {
352 llvm::SmallVector<const Type *, 8> TypesToDelete;
353 for (
const Type *ExceptionTy : ThrownExceptions) {
354 CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
355 CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
359 if (ExceptionCanTy == HandlerCanTy) {
360 TypesToDelete.push_back(ExceptionTy);
365 else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
366 HandlerCanTy->getTypePtr())) {
367 TypesToDelete.push_back(ExceptionTy);
370 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
371 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
372 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
377 if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
378 isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
381 if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
382 TypesToDelete.push_back(ExceptionTy);
385 else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
386 TypesToDelete.push_back(ExceptionTy);
389 else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
390 Context.getLangOpts())) {
391 TypesToDelete.push_back(ExceptionTy);
397 else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
398 ExceptionCanTy->isNullPtrType()) {
399 TypesToDelete.push_back(ExceptionTy);
403 for (
const Type *T : TypesToDelete)
404 ThrownExceptions.erase(T);
406 reevaluateBehaviour();
407 return !TypesToDelete.empty();
412 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
413 llvm::SmallVector<const Type *, 8> TypesToDelete;
416 for (
const Type *T : ThrownExceptions) {
417 if (
const auto *TD = T->getAsTagDecl()) {
418 if (TD->getDeclName().isIdentifier()) {
419 if ((IgnoreBadAlloc &&
420 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
421 (IgnoredTypes.count(TD->getName()) > 0))
422 TypesToDelete.push_back(T);
426 for (
const Type *T : TypesToDelete)
427 ThrownExceptions.erase(T);
429 reevaluateBehaviour();
435 ContainsUnknown =
false;
436 ThrownExceptions.clear();
439void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
440 if (ThrownExceptions.empty())
451 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
452 if (!Func || CallStack.count(Func) || (!CallStack.empty() && !
canThrow(Func)))
455 if (
const Stmt *Body = Func->getBody()) {
456 CallStack.insert(Func);
457 ExceptionInfo Result = throwsException(Body, Caught, CallStack);
460 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
461 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
463 throwsException(Init->getInit(), Caught, CallStack);
468 CallStack.erase(Func);
473 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
474 for (
const QualType &Ex : FPT->exceptions())
475 Result.registerException(Ex.getTypePtr());
482ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
484 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
489 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
490 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
491 const auto *ThrownType =
492 ThrownExpr->getType()->getUnqualifiedDesugaredType();
493 if (ThrownType->isReferenceType())
494 ThrownType = ThrownType->castAs<ReferenceType>()
496 ->getUnqualifiedDesugaredType();
498 ThrownExpr->getType()->getUnqualifiedDesugaredType());
503 Results.registerExceptions(Caught);
504 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
505 ExceptionInfo Uncaught =
506 throwsException(Try->getTryBlock(), Caught, CallStack);
507 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
508 const CXXCatchStmt *Catch = Try->getHandler(I);
511 if (!Catch->getExceptionDecl()) {
512 ExceptionInfo Rethrown = throwsException(
513 Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
517 const auto *CaughtType =
518 Catch->getCaughtType()->getUnqualifiedDesugaredType();
519 if (CaughtType->isReferenceType()) {
520 CaughtType = CaughtType->castAs<ReferenceType>()
522 ->getUnqualifiedDesugaredType();
529 if (Uncaught.filterByCatch(
530 CaughtType, Catch->getExceptionDecl()->getASTContext())) {
532 CaughtExceptions.insert(CaughtType);
533 ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
534 CaughtExceptions, CallStack);
540 }
else if (
const auto *Call = dyn_cast<CallExpr>(St)) {
541 if (
const FunctionDecl *Func = Call->getDirectCallee()) {
542 ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
545 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
547 throwsException(Construct->getConstructor(), Caught, CallStack);
549 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
551 throwsException(DefaultInit->getExpr(), Caught, CallStack);
553 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
554 for (
const Stmt *Child : Coro->childrenExclBody()) {
555 if (Child != Coro->getExceptionHandler()) {
556 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
560 ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
561 Results.merge(throwsException(Coro->getExceptionHandler(),
562 Excs.getExceptionTypes(), CallStack));
563 for (
const Type *Throwable : Excs.getExceptionTypes()) {
564 if (
const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
565 ExceptionInfo DestructorExcs =
566 throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
571 for (
const Stmt *Child : St->children()) {
572 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
579ExceptionAnalyzer::ExceptionInfo
580ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
581 ExceptionInfo ExceptionList;
584 const auto CacheEntry = FunctionCache.find(Func);
585 if (CacheEntry == FunctionCache.end()) {
586 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
594 FunctionCache.try_emplace(Func, ExceptionList);
596 ExceptionList = CacheEntry->getSecond();
598 return ExceptionList;
601ExceptionAnalyzer::ExceptionInfo
602ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
603 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
608ExceptionAnalyzer::ExceptionInfo
609ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
610 ExceptionInfo ExceptionList = analyzeImpl(Node);
614 return ExceptionList;
618 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
620 return ExceptionList;
623ExceptionAnalyzer::ExceptionInfo
625 return analyzeDispatch(Func);
629 return analyzeDispatch(Stmt);
std::vector< CodeCompletionResult > Results
Bundle the gathered information about an entity like a function regarding it's exception behaviour.
bool filterByCatch(const Type *HandlerTy, const ASTContext &Context)
This method is useful in case 'catch' clauses are analyzed as it is possible to catch multiple except...
static ExceptionInfo createNonThrowing()
void clear()
Clear the state to 'NonThrowing' to make the corresponding entity neutral.
void registerException(const Type *ExceptionType)
Register a single exception type as recognized potential exception to be thrown.
ExceptionInfo & filterIgnoredExceptions(const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc)
Filter the set of thrown exception type against a set of ignored types that shall not be considered i...
static ExceptionInfo createUnknown()
void registerExceptions(const Throwables &Exceptions)
Registers a SmallVector of exception types as recognized potential exceptions to be thrown.
llvm::SmallSet< const Type *, 2 > Throwables
ExceptionInfo & merge(const ExceptionInfo &Other)
Updates the local state according to the other state.
@ Throwing
The function can definitely throw given an AST.
@ Unknown
This can happen for extern functions without available definition.
@ NotThrowing
This function can not throw, given an AST.
ExceptionInfo analyze(const FunctionDecl *Func)
static bool canThrow(const FunctionDecl *Func)