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) {
211 auto IsValidP_i = [](QualType
P) {
212 return P->isPointerType() ||
P->isMemberPointerType() ||
213 P->isConstantArrayType() ||
P->isIncompleteArrayType();
217 auto IsSameP_i = [](QualType P1, QualType P2) {
218 if (P1->isPointerType())
219 return P2->isPointerType();
221 if (P1->isMemberPointerType())
222 return P2->isMemberPointerType() &&
223 P1->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl() ==
224 P2->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl();
226 if (P1->isConstantArrayType())
227 return P2->isConstantArrayType() &&
228 cast<ConstantArrayType>(P1)->getSize() ==
229 cast<ConstantArrayType>(P2)->getSize();
231 if (P1->isIncompleteArrayType())
232 return P2->isIncompleteArrayType();
255 bool ConstUntilI =
true;
256 auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
257 const QualType &To) {
259 if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
264 if (From.isConstQualified() && !To.isConstQualified())
267 if (From.isVolatileQualified() && !To.isVolatileQualified())
270 ConstUntilI = To.isConstQualified();
276 while (IsValidP_i(From) && IsValidP_i(To)) {
278 From = From.getCanonicalType();
279 To = To.getCanonicalType();
281 if (!SatisfiesCVRules(From, To))
284 if (!IsSameP_i(From, To)) {
285 if (LangOpts.CPlusPlus20) {
286 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
289 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
298 std::optional<QualType> FromPointeeOrElem =
300 std::optional<QualType> ToPointeeOrElem =
303 assert(FromPointeeOrElem &&
304 "From pointer or array has no pointee or element!");
305 assert(ToPointeeOrElem &&
"To pointer or array has no pointee or element!");
307 From = *FromPointeeOrElem;
308 To = *ToPointeeOrElem;
312 if (IsValidP_i(From) || IsValidP_i(To))
316 if (!SatisfiesCVRules(From, To))
319 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(),
358 const ASTContext &Context) {
359 llvm::SmallVector<const Type *, 8> TypesToDelete;
360 for (
const auto &ThrownException : ThrownExceptions) {
361 const Type *ExceptionTy = ThrownException.getFirst();
362 const CanQualType ExceptionCanTy =
363 ExceptionTy->getCanonicalTypeUnqualified();
364 const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
368 if (ExceptionCanTy == HandlerCanTy) {
369 TypesToDelete.push_back(ExceptionTy);
375 HandlerCanTy->getTypePtr())) {
376 TypesToDelete.push_back(ExceptionTy);
379 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
380 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
381 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
391 TypesToDelete.push_back(ExceptionTy);
395 TypesToDelete.push_back(ExceptionTy);
399 Context.getLangOpts())) {
400 TypesToDelete.push_back(ExceptionTy);
407 ExceptionCanTy->isNullPtrType()) {
408 TypesToDelete.push_back(ExceptionTy);
414 for (
const Type *TypeToDelete : TypesToDelete) {
415 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
416 if (DeleteIt != ThrownExceptions.end()) {
417 DeletedExceptions.insert(*DeleteIt);
418 ThrownExceptions.erase(DeleteIt);
422 reevaluateBehaviour();
423 return DeletedExceptions;
428 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
429 llvm::SmallVector<const Type *, 8> TypesToDelete;
432 for (
const auto &ThrownException : ThrownExceptions) {
433 const Type *T = ThrownException.getFirst();
434 if (
const auto *TD = T->getAsTagDecl()) {
435 if (TD->getDeclName().isIdentifier()) {
436 if ((IgnoreBadAlloc &&
437 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
438 (IgnoredTypes.contains(TD->getName())))
439 TypesToDelete.push_back(T);
443 for (
const Type *T : TypesToDelete)
444 ThrownExceptions.erase(T);
446 reevaluateBehaviour();
452 ContainsUnknown =
false;
453 ThrownExceptions.clear();
456void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
457 if (ThrownExceptions.empty())
472 if (
const Stmt *Body = Func->getBody()) {
474 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
477 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
478 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
479 const ExceptionInfo Excs =
480 throwsException(Init->getInit(), Caught,
CallStack);
490 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
491 for (
const QualType &Ex : FPT->exceptions()) {
493 Result.registerException(
495 {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
504ExceptionAnalyzer::ExceptionInfo
505ExceptionAnalyzer::throwsException(
const Stmt *St,
512 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
513 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
514 const auto *ThrownType =
515 ThrownExpr->getType()->getUnqualifiedDesugaredType();
516 if (ThrownType->isReferenceType())
517 ThrownType = ThrownType->castAs<ReferenceType>()
519 ->getUnqualifiedDesugaredType();
520 Results.registerException(
521 ThrownExpr->getType()->getUnqualifiedDesugaredType(),
522 {Throw->getBeginLoc(), CallStack});
527 Results.registerExceptions(Caught);
528 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
530 throwsException(Try->getTryBlock(), Caught,
CallStack);
531 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
532 const CXXCatchStmt *Catch = Try->getHandler(I);
535 if (!Catch->getExceptionDecl()) {
537 Catch->getHandlerBlock(), Uncaught.getExceptions(),
CallStack);
538 Results.merge(Rethrown);
541 const auto *CaughtType =
542 Catch->getCaughtType()->getUnqualifiedDesugaredType();
543 if (CaughtType->isReferenceType()) {
544 CaughtType = CaughtType->castAs<ReferenceType>()
546 ->getUnqualifiedDesugaredType();
554 Uncaught.filterByCatch(CaughtType,
555 Catch->getExceptionDecl()->getASTContext());
556 if (!FilteredExceptions.empty()) {
558 Catch->getHandlerBlock(), FilteredExceptions,
CallStack);
559 Results.merge(Rethrown);
563 Results.merge(Uncaught);
564 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
566 throwsException(DefaultInit->getExpr(), Caught,
CallStack);
568 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
569 for (
const Stmt *Child : Coro->childrenExclBody()) {
570 if (Child != Coro->getExceptionHandler()) {
576 throwsException(Coro->getBody(), Caught,
CallStack);
577 Results.merge(throwsException(Coro->getExceptionHandler(),
579 for (
const auto &Exception : Excs.getExceptions()) {
580 const Type *ExcType = Exception.getFirst();
581 if (
const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
583 ThrowableRec->getDestructor(), Caught,
CallStack, SourceLocation{});
584 Results.merge(DestructorExcs);
587 }
else if (
const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
588 for (
const Stmt *Init : Lambda->capture_inits()) {
594 for (
const Stmt *Child : St->children()) {
601 if (
const auto *Call = dyn_cast<CallExpr>(St)) {
602 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
604 throwsException(Func, Caught,
CallStack,
Call->getBeginLoc());
607 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
609 throwsException(Construct->getConstructor(), Caught,
CallStack,
610 Construct->getBeginLoc());
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.