15 assert(ExceptionType !=
nullptr &&
"Only valid types are accepted");
17 ThrownExceptions.insert({ExceptionType,
ThrowInfo});
22 if (Exceptions.empty())
25 ThrownExceptions.insert_range(Exceptions);
41 ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
42 ThrownExceptions.insert_range(Other.ThrownExceptions);
49bool isUnambiguousPublicBaseClass(
const Type *DerivedType,
50 const Type *BaseType) {
51 const auto *DerivedClass =
52 DerivedType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
53 const auto *BaseClass =
54 BaseType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
55 if (!DerivedClass || !BaseClass)
59 Paths.setOrigin(DerivedClass);
61 bool IsPublicBaseClass =
false;
62 DerivedClass->lookupInBases(
63 [&BaseClass, &IsPublicBaseClass](
const CXXBaseSpecifier *BS,
66 ->getCanonicalTypeUnqualified()
67 ->getAsCXXRecordDecl() == BaseClass &&
68 BS->getAccessSpecifier() == AS_public) {
69 IsPublicBaseClass =
true;
77 return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
81inline bool isPointerOrPointerToMember(
const Type *T) {
82 return T->isPointerType() || T->isMemberPointerType();
85std::optional<QualType> getPointeeOrArrayElementQualType(QualType T) {
86 if (T->isAnyPointerType() || T->isMemberPointerType())
87 return T->getPointeeType();
90 return T->getAsArrayTypeUnsafe()->getElementType();
95bool isBaseOf(
const Type *DerivedType,
const Type *BaseType) {
96 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
97 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
98 if (!DerivedClass || !BaseClass)
101 return !DerivedClass->forallBases(
102 [BaseClass](
const CXXRecordDecl *Cur) {
return Cur != BaseClass; });
106bool moreOrEquallyQualified(QualType T1, QualType T2) {
107 return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
108 T1.getQualifiers() == T2.getQualifiers();
111bool isStandardPointerConvertible(QualType From, QualType To) {
112 assert((From->isPointerType() || From->isMemberPointerType()) &&
113 (To->isPointerType() || To->isMemberPointerType()) &&
114 "Pointer conversion should be performed on pointer types only.");
116 if (!moreOrEquallyQualified(To->getPointeeType(), From->getPointeeType()))
125 if (To->isPointerType() && From->isNullPtrType())
131 if (To->isVoidPointerType() && From->isObjectPointerType())
139 if (
const auto *RD = From->getPointeeCXXRecordDecl()) {
140 if (RD->isCompleteDefinition() &&
141 isBaseOf(From->getPointeeType().getTypePtr(),
142 To->getPointeeType().getTypePtr())) {
145 return isUnambiguousPublicBaseClass(From->getPointeeType().getTypePtr(),
146 To->getPointeeType().getTypePtr());
153bool isFunctionPointerConvertible(QualType From, QualType To) {
154 if (!From->isFunctionPointerType() && !From->isFunctionType() &&
155 !From->isMemberFunctionPointerType())
158 if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
161 if (To->isFunctionPointerType()) {
162 if (From->isFunctionPointerType())
163 return To->getPointeeType() == From->getPointeeType();
165 if (From->isFunctionType())
166 return To->getPointeeType() == From;
171 if (To->isMemberFunctionPointerType()) {
172 if (!From->isMemberFunctionPointerType())
175 const auto *FromMember = cast<MemberPointerType>(From);
176 const auto *ToMember = cast<MemberPointerType>(To);
180 return FromMember->getQualifier() == ToMember->getQualifier() &&
181 FromMember->getMostRecentCXXRecordDecl() ==
182 ToMember->getMostRecentCXXRecordDecl() &&
183 FromMember->getPointeeType() == ToMember->getPointeeType();
195bool isQualificationConvertiblePointer(QualType From, QualType To,
196 LangOptions LangOpts) {
213 auto IsValidP_i = [](QualType
P) {
214 return P->isPointerType() ||
P->isMemberPointerType() ||
215 P->isConstantArrayType() ||
P->isIncompleteArrayType();
219 auto IsSameP_i = [](QualType P1, QualType P2) {
220 if (P1->isPointerType())
221 return P2->isPointerType();
223 if (P1->isMemberPointerType())
224 return P2->isMemberPointerType() &&
225 P1->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl() ==
226 P2->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl();
228 if (P1->isConstantArrayType())
229 return P2->isConstantArrayType() &&
230 cast<ConstantArrayType>(P1)->getSize() ==
231 cast<ConstantArrayType>(P2)->getSize();
233 if (P1->isIncompleteArrayType())
234 return P2->isIncompleteArrayType();
257 bool ConstUntilI =
true;
258 auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
259 const QualType &To) {
261 if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
266 if (From.isConstQualified() && !To.isConstQualified())
269 if (From.isVolatileQualified() && !To.isVolatileQualified())
272 ConstUntilI = To.isConstQualified();
278 while (IsValidP_i(From) && IsValidP_i(To)) {
280 From = From.getCanonicalType();
281 To = To.getCanonicalType();
283 if (!SatisfiesCVRules(From, To))
286 if (!IsSameP_i(From, To)) {
287 if (LangOpts.CPlusPlus20) {
288 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
291 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
300 std::optional<QualType> FromPointeeOrElem =
301 getPointeeOrArrayElementQualType(From);
302 std::optional<QualType> ToPointeeOrElem =
303 getPointeeOrArrayElementQualType(To);
305 assert(FromPointeeOrElem &&
306 "From pointer or array has no pointee or element!");
307 assert(ToPointeeOrElem &&
"To pointer or array has no pointee or element!");
309 From = *FromPointeeOrElem;
310 To = *ToPointeeOrElem;
314 if (IsValidP_i(From) || IsValidP_i(To))
318 if (!SatisfiesCVRules(From, To))
321 return From.getTypePtr() == To.getTypePtr();
329 if (Func->isConsteval())
332 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
336 switch (FunProto->canThrow()) {
340 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
344 if (NoexceptExpr->isValueDependent())
348 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
361 const ASTContext &Context) {
362 llvm::SmallVector<const Type *, 8> TypesToDelete;
363 for (
const auto &ThrownException : ThrownExceptions) {
364 const Type *ExceptionTy = ThrownException.getFirst();
365 CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
366 CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
370 if (ExceptionCanTy == HandlerCanTy) {
371 TypesToDelete.push_back(ExceptionTy);
376 else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
377 HandlerCanTy->getTypePtr())) {
378 TypesToDelete.push_back(ExceptionTy);
381 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
382 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
383 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
388 if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
389 isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
392 if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
393 TypesToDelete.push_back(ExceptionTy);
396 else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
397 TypesToDelete.push_back(ExceptionTy);
400 else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
401 Context.getLangOpts())) {
402 TypesToDelete.push_back(ExceptionTy);
408 else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
409 ExceptionCanTy->isNullPtrType()) {
410 TypesToDelete.push_back(ExceptionTy);
416 for (
const Type *TypeToDelete : TypesToDelete) {
417 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
418 if (DeleteIt != ThrownExceptions.end()) {
419 DeletedExceptions.insert(*DeleteIt);
420 ThrownExceptions.erase(DeleteIt);
424 reevaluateBehaviour();
425 return DeletedExceptions;
430 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
431 llvm::SmallVector<const Type *, 8> TypesToDelete;
434 for (
const auto &ThrownException : ThrownExceptions) {
435 const Type *T = ThrownException.getFirst();
436 if (
const auto *TD = T->getAsTagDecl()) {
437 if (TD->getDeclName().isIdentifier()) {
438 if ((IgnoreBadAlloc &&
439 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
440 (IgnoredTypes.contains(TD->getName())))
441 TypesToDelete.push_back(T);
445 for (
const Type *T : TypesToDelete)
446 ThrownExceptions.erase(T);
448 reevaluateBehaviour();
454 ContainsUnknown =
false;
455 ThrownExceptions.clear();
458void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
459 if (ThrownExceptions.empty())
474 if (
const Stmt *Body = Func->getBody()) {
476 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
479 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
480 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
482 throwsException(Init->getInit(), Caught,
CallStack);
492 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
493 for (
const QualType &Ex : FPT->exceptions()) {
495 Result.registerException(
497 {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
506ExceptionAnalyzer::ExceptionInfo
507ExceptionAnalyzer::throwsException(
const Stmt *St,
514 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
515 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
516 const auto *ThrownType =
517 ThrownExpr->getType()->getUnqualifiedDesugaredType();
518 if (ThrownType->isReferenceType())
519 ThrownType = ThrownType->castAs<ReferenceType>()
521 ->getUnqualifiedDesugaredType();
522 Results.registerException(
523 ThrownExpr->getType()->getUnqualifiedDesugaredType(),
524 {Throw->getBeginLoc(), CallStack});
529 Results.registerExceptions(Caught);
530 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
532 throwsException(Try->getTryBlock(), Caught,
CallStack);
533 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
534 const CXXCatchStmt *Catch = Try->getHandler(I);
537 if (!Catch->getExceptionDecl()) {
539 Catch->getHandlerBlock(), Uncaught.getExceptions(),
CallStack);
540 Results.merge(Rethrown);
543 const auto *CaughtType =
544 Catch->getCaughtType()->getUnqualifiedDesugaredType();
545 if (CaughtType->isReferenceType()) {
546 CaughtType = CaughtType->castAs<ReferenceType>()
548 ->getUnqualifiedDesugaredType();
556 Uncaught.filterByCatch(CaughtType,
557 Catch->getExceptionDecl()->getASTContext());
558 if (!FilteredExceptions.empty()) {
560 Catch->getHandlerBlock(), FilteredExceptions,
CallStack);
561 Results.merge(Rethrown);
565 Results.merge(Uncaught);
566 }
else if (
const auto *Call = dyn_cast<CallExpr>(St)) {
567 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
569 throwsException(Func, Caught,
CallStack,
Call->getBeginLoc());
572 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
573 ExceptionInfo Excs = throwsException(Construct->getConstructor(), Caught,
576 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
578 throwsException(DefaultInit->getExpr(), Caught,
CallStack);
580 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
581 for (
const Stmt *Child : Coro->childrenExclBody()) {
582 if (Child != Coro->getExceptionHandler()) {
588 Results.merge(throwsException(Coro->getExceptionHandler(),
590 for (
const auto &Exception : Excs.getExceptions()) {
591 const Type *ExcType = Exception.getFirst();
592 if (
const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
594 ThrowableRec->getDestructor(), Caught,
CallStack, SourceLocation{});
595 Results.merge(DestructorExcs);
598 }
else if (
const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
599 for (
const Stmt *Init : Lambda->capture_inits()) {
604 for (
const Stmt *Child : St->children()) {
612ExceptionAnalyzer::ExceptionInfo
613ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
617 const auto CacheEntry = FunctionCache.find(Func);
618 if (CacheEntry == FunctionCache.end()) {
627 FunctionCache.try_emplace(Func, ExceptionList);
629 ExceptionList = CacheEntry->getSecond();
631 return ExceptionList;
634ExceptionAnalyzer::ExceptionInfo
635ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
641ExceptionAnalyzer::ExceptionInfo
642ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
647 return ExceptionList;
651 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
653 return ExceptionList;
656ExceptionAnalyzer::ExceptionInfo
658 return analyzeDispatch(Func);
662 return analyzeDispatch(Stmt);
Bundle the gathered information about an entity like a function regarding it's exception behaviour.
static ExceptionInfo createNonThrowing()
void clear()
Clear the state to 'NonThrowing' to make the corresponding entity neutral.
llvm::SmallDenseMap< const Type *, ThrowInfo, 2 > Throwables
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...
Throwables 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 createUnknown()
void registerExceptions(const Throwables &Exceptions)
Registers a SmallVector of exception types as recognized potential exceptions to be thrown.
ExceptionInfo & merge(const ExceptionInfo &Other)
Updates the local state according to the other state.
void registerException(const Type *ExceptionType, const ThrowInfo &ThrowInfo)
Register a single exception type as recognized potential exception to be thrown.
@ 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.
llvm::MapVector< const FunctionDecl *, SourceLocation > CallStack
We use a MapVector to preserve the order of the functions in the call stack as well as have fast look...
ExceptionInfo analyze(const FunctionDecl *Func)
@ Type
An inlay hint that for a type annotation.
static bool canThrow(const FunctionDecl *Func)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
Holds information about where an exception is thrown.