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();
326 if (Func->isConsteval())
329 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
333 switch (FunProto->canThrow()) {
337 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
341 if (NoexceptExpr->isValueDependent())
345 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
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();
365 if (ExceptionCanTy == HandlerCanTy) {
366 TypesToDelete.push_back(ExceptionTy);
371 else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
372 HandlerCanTy->getTypePtr())) {
373 TypesToDelete.push_back(ExceptionTy);
376 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
377 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
378 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
383 if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
384 isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
387 if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
388 TypesToDelete.push_back(ExceptionTy);
391 else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
392 TypesToDelete.push_back(ExceptionTy);
395 else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
396 Context.getLangOpts())) {
397 TypesToDelete.push_back(ExceptionTy);
403 else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
404 ExceptionCanTy->isNullPtrType()) {
405 TypesToDelete.push_back(ExceptionTy);
409 for (
const Type *T : TypesToDelete)
410 ThrownExceptions.erase(T);
412 reevaluateBehaviour();
413 return !TypesToDelete.empty();
418 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
419 llvm::SmallVector<const Type *, 8> TypesToDelete;
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);
432 for (
const Type *T : TypesToDelete)
433 ThrownExceptions.erase(T);
435 reevaluateBehaviour();
441 ContainsUnknown =
false;
442 ThrownExceptions.clear();
445void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
446 if (ThrownExceptions.empty())
457 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
458 if (!Func || CallStack.contains(Func) ||
459 (!CallStack.empty() && !
canThrow(Func)))
462 if (
const Stmt *Body = Func->getBody()) {
463 CallStack.insert(Func);
464 ExceptionInfo Result = throwsException(Body, Caught, CallStack);
467 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
468 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
470 throwsException(Init->getInit(), Caught, CallStack);
475 CallStack.erase(Func);
480 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
481 for (
const QualType &Ex : FPT->exceptions())
482 Result.registerException(Ex.getTypePtr());
489ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
491 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
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>()
503 ->getUnqualifiedDesugaredType();
505 ThrownExpr->getType()->getUnqualifiedDesugaredType());
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);
518 if (!Catch->getExceptionDecl()) {
519 ExceptionInfo Rethrown = throwsException(
520 Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
524 const auto *CaughtType =
525 Catch->getCaughtType()->getUnqualifiedDesugaredType();
526 if (CaughtType->isReferenceType()) {
527 CaughtType = CaughtType->castAs<ReferenceType>()
529 ->getUnqualifiedDesugaredType();
536 if (Uncaught.filterByCatch(
537 CaughtType, Catch->getExceptionDecl()->getASTContext())) {
539 CaughtExceptions.insert(CaughtType);
540 ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
541 CaughtExceptions, CallStack);
547 }
else if (
const auto *Call = dyn_cast<CallExpr>(St)) {
548 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
549 ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
552 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
554 throwsException(Construct->getConstructor(), Caught, CallStack);
556 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
558 throwsException(DefaultInit->getExpr(), Caught, CallStack);
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);
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);
578 for (
const Stmt *Child : St->children()) {
579 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
586ExceptionAnalyzer::ExceptionInfo
587ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
588 ExceptionInfo ExceptionList;
591 const auto CacheEntry = FunctionCache.find(Func);
592 if (CacheEntry == FunctionCache.end()) {
593 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
601 FunctionCache.try_emplace(Func, ExceptionList);
603 ExceptionList = CacheEntry->getSecond();
605 return ExceptionList;
608ExceptionAnalyzer::ExceptionInfo
609ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
610 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
615ExceptionAnalyzer::ExceptionInfo
616ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
617 ExceptionInfo ExceptionList = analyzeImpl(Node);
621 return ExceptionList;
625 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
627 return ExceptionList;
630ExceptionAnalyzer::ExceptionInfo
632 return analyzeDispatch(Func);
636 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)