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);
49 const Type *BaseType) {
50 const auto *DerivedClass =
51 DerivedType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
52 const auto *BaseClass =
53 BaseType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
54 if (!DerivedClass || !BaseClass)
58 Paths.setOrigin(DerivedClass);
60 bool IsPublicBaseClass =
false;
61 DerivedClass->lookupInBases(
62 [&BaseClass, &IsPublicBaseClass](
const CXXBaseSpecifier *BS,
65 ->getCanonicalTypeUnqualified()
66 ->getAsCXXRecordDecl() == BaseClass &&
67 BS->getAccessSpecifier() == AS_public) {
68 IsPublicBaseClass =
true;
76 return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
81 return T->isPointerType() || T->isMemberPointerType();
85 if (T->isAnyPointerType() || T->isMemberPointerType())
86 return T->getPointeeType();
89 return T->getAsArrayTypeUnsafe()->getElementType();
94static bool isBaseOf(
const Type *DerivedType,
const Type *BaseType) {
95 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
96 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
97 if (!DerivedClass || !BaseClass)
100 return !DerivedClass->forallBases(
101 [BaseClass](
const CXXRecordDecl *Cur) {
return Cur != BaseClass; });
106 return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
107 T1.getQualifiers() == T2.getQualifiers();
111 assert((From->isPointerType() || From->isMemberPointerType()) &&
112 (To->isPointerType() || To->isMemberPointerType()) &&
113 "Pointer conversion should be performed on pointer types only.");
124 if (To->isPointerType() && From->isNullPtrType())
130 if (To->isVoidPointerType() && From->isObjectPointerType())
138 if (
const auto *RD = From->getPointeeCXXRecordDecl()) {
139 if (RD->isCompleteDefinition() &&
140 isBaseOf(From->getPointeeType().getTypePtr(),
141 To->getPointeeType().getTypePtr())) {
145 To->getPointeeType().getTypePtr());
153 if (!From->isFunctionPointerType() && !From->isFunctionType() &&
154 !From->isMemberFunctionPointerType())
157 if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
160 if (To->isFunctionPointerType()) {
161 if (From->isFunctionPointerType())
162 return To->getPointeeType() == From->getPointeeType();
164 if (From->isFunctionType())
165 return To->getPointeeType() == From;
170 if (To->isMemberFunctionPointerType()) {
171 if (!From->isMemberFunctionPointerType())
174 const auto *FromMember = cast<MemberPointerType>(From);
175 const auto *ToMember = cast<MemberPointerType>(To);
179 return FromMember->getQualifier() == ToMember->getQualifier() &&
180 FromMember->getMostRecentCXXRecordDecl() ==
181 ToMember->getMostRecentCXXRecordDecl() &&
182 FromMember->getPointeeType() == ToMember->getPointeeType();
195 const LangOptions &LangOpts) {
212 auto IsValidP_i = [](QualType
P) {
213 return P->isPointerType() ||
P->isMemberPointerType() ||
214 P->isConstantArrayType() ||
P->isIncompleteArrayType();
218 auto IsSameP_i = [](QualType P1, QualType P2) {
219 if (P1->isPointerType())
220 return P2->isPointerType();
222 if (P1->isMemberPointerType())
223 return P2->isMemberPointerType() &&
224 P1->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl() ==
225 P2->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl();
227 if (P1->isConstantArrayType())
228 return P2->isConstantArrayType() &&
229 cast<ConstantArrayType>(P1)->getSize() ==
230 cast<ConstantArrayType>(P2)->getSize();
232 if (P1->isIncompleteArrayType())
233 return P2->isIncompleteArrayType();
256 bool ConstUntilI =
true;
257 auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
258 const QualType &To) {
260 if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
265 if (From.isConstQualified() && !To.isConstQualified())
268 if (From.isVolatileQualified() && !To.isVolatileQualified())
271 ConstUntilI = To.isConstQualified();
277 while (IsValidP_i(From) && IsValidP_i(To)) {
279 From = From.getCanonicalType();
280 To = To.getCanonicalType();
282 if (!SatisfiesCVRules(From, To))
285 if (!IsSameP_i(From, To)) {
286 if (LangOpts.CPlusPlus20) {
287 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
290 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
299 std::optional<QualType> FromPointeeOrElem =
301 std::optional<QualType> ToPointeeOrElem =
304 assert(FromPointeeOrElem &&
305 "From pointer or array has no pointee or element!");
306 assert(ToPointeeOrElem &&
"To pointer or array has no pointee or element!");
308 From = *FromPointeeOrElem;
309 To = *ToPointeeOrElem;
313 if (IsValidP_i(From) || IsValidP_i(To))
317 if (!SatisfiesCVRules(From, To))
320 return From.getTypePtr() == To.getTypePtr();
327 if (Func->isConsteval())
330 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
334 switch (FunProto->canThrow()) {
338 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
342 if (NoexceptExpr->isValueDependent())
346 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
359 const ASTContext &Context) {
360 llvm::SmallVector<const Type *, 8> TypesToDelete;
361 for (
const auto &ThrownException : ThrownExceptions) {
362 const Type *ExceptionTy = ThrownException.getFirst();
363 const CanQualType ExceptionCanTy =
364 ExceptionTy->getCanonicalTypeUnqualified();
365 const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
369 if (ExceptionCanTy == HandlerCanTy) {
370 TypesToDelete.push_back(ExceptionTy);
376 HandlerCanTy->getTypePtr())) {
377 TypesToDelete.push_back(ExceptionTy);
380 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
381 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
382 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
392 TypesToDelete.push_back(ExceptionTy);
396 TypesToDelete.push_back(ExceptionTy);
400 Context.getLangOpts())) {
401 TypesToDelete.push_back(ExceptionTy);
408 ExceptionCanTy->isNullPtrType()) {
409 TypesToDelete.push_back(ExceptionTy);
415 for (
const Type *TypeToDelete : TypesToDelete) {
416 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
417 if (DeleteIt != ThrownExceptions.end()) {
418 DeletedExceptions.insert(*DeleteIt);
419 ThrownExceptions.erase(DeleteIt);
423 reevaluateBehaviour();
424 return DeletedExceptions;
429 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
430 llvm::SmallVector<const Type *, 8> TypesToDelete;
433 for (
const auto &ThrownException : ThrownExceptions) {
434 const Type *T = ThrownException.getFirst();
435 if (
const auto *TD = T->getAsTagDecl()) {
436 if (TD->getDeclName().isIdentifier()) {
437 if ((IgnoreBadAlloc &&
438 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
439 (IgnoredTypes.contains(TD->getName())))
440 TypesToDelete.push_back(T);
444 for (
const Type *T : TypesToDelete)
445 ThrownExceptions.erase(T);
447 reevaluateBehaviour();
453 ContainsUnknown =
false;
454 ThrownExceptions.clear();
457void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
458 if (ThrownExceptions.empty())
473 if (
const Stmt *Body = Func->getBody()) {
475 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
478 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
479 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
480 const ExceptionInfo Excs =
481 throwsException(Init->getInit(), Caught,
CallStack);
491 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
492 for (
const QualType &Ex : FPT->exceptions()) {
494 Result.registerException(
496 {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
505ExceptionAnalyzer::ExceptionInfo
506ExceptionAnalyzer::throwsException(
const Stmt *St,
513 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
514 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
515 const auto *ThrownType =
516 ThrownExpr->getType()->getUnqualifiedDesugaredType();
517 if (ThrownType->isReferenceType())
518 ThrownType = ThrownType->castAs<ReferenceType>()
520 ->getUnqualifiedDesugaredType();
521 Results.registerException(
522 ThrownExpr->getType()->getUnqualifiedDesugaredType(),
523 {Throw->getBeginLoc(), CallStack});
528 Results.registerExceptions(Caught);
529 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
531 throwsException(Try->getTryBlock(), Caught,
CallStack);
532 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
533 const CXXCatchStmt *Catch = Try->getHandler(I);
536 if (!Catch->getExceptionDecl()) {
538 Catch->getHandlerBlock(), Uncaught.getExceptions(),
CallStack);
539 Results.merge(Rethrown);
542 const auto *CaughtType =
543 Catch->getCaughtType()->getUnqualifiedDesugaredType();
544 if (CaughtType->isReferenceType()) {
545 CaughtType = CaughtType->castAs<ReferenceType>()
547 ->getUnqualifiedDesugaredType();
555 Uncaught.filterByCatch(CaughtType,
556 Catch->getExceptionDecl()->getASTContext());
557 if (!FilteredExceptions.empty()) {
559 Catch->getHandlerBlock(), FilteredExceptions,
CallStack);
560 Results.merge(Rethrown);
564 Results.merge(Uncaught);
565 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
567 throwsException(DefaultInit->getExpr(), Caught,
CallStack);
569 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
570 for (
const Stmt *Child : Coro->childrenExclBody()) {
571 if (Child != Coro->getExceptionHandler()) {
577 throwsException(Coro->getBody(), Caught,
CallStack);
578 Results.merge(throwsException(Coro->getExceptionHandler(),
580 for (
const auto &Exception : Excs.getExceptions()) {
581 const Type *ExcType = Exception.getFirst();
582 if (
const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
584 ThrowableRec->getDestructor(), Caught,
CallStack, SourceLocation{});
585 Results.merge(DestructorExcs);
588 }
else if (
const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
589 for (
const Stmt *Init : Lambda->capture_inits()) {
595 for (
const Stmt *Child : St->children()) {
602 if (
const auto *Call = dyn_cast<CallExpr>(St)) {
603 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
605 throwsException(Func, Caught,
CallStack,
Call->getBeginLoc());
608 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
609 ExceptionInfo Excs = throwsException(Construct->getConstructor(), Caught,
617ExceptionAnalyzer::ExceptionInfo
618ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
622 const auto CacheEntry = FunctionCache.find(Func);
623 if (CacheEntry == FunctionCache.end()) {
632 FunctionCache.try_emplace(Func, ExceptionList);
634 ExceptionList = CacheEntry->getSecond();
636 return ExceptionList;
639ExceptionAnalyzer::ExceptionInfo
640ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
646ExceptionAnalyzer::ExceptionInfo
647ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
652 return ExceptionList;
656 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
658 return ExceptionList;
661ExceptionAnalyzer::ExceptionInfo
663 return analyzeDispatch(Func);
667 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 std::optional< QualType > getPointeeOrArrayElementQualType(QualType T)
static bool isQualificationConvertiblePointer(QualType From, QualType To, const LangOptions &LangOpts)
static bool canThrow(const FunctionDecl *Func)
static bool moreOrEquallyQualified(QualType T1, QualType T2)
static bool isPointerOrPointerToMember(const Type *T)
static bool isUnambiguousPublicBaseClass(const Type *DerivedType, const Type *BaseType)
static bool isStandardPointerConvertible(QualType From, QualType To)
static bool isBaseOf(const Type *DerivedType, const Type *BaseType)
static bool isFunctionPointerConvertible(QualType From, QualType To)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
Holds information about where an exception is thrown.