16 ThrownExceptions.insert({ExceptionType,
ThrowInfo});
21 if (Exceptions.empty())
24 ThrownExceptions.insert_range(Exceptions);
40 ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
41 ThrowsUnknown = ThrowsUnknown || Other.ThrowsUnknown;
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>();
336 if (isUnresolvedExceptionSpec(FunProto->getExceptionSpecType()))
339 switch (FunProto->canThrow()) {
343 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
347 if (NoexceptExpr->isValueDependent())
351 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
364 const ASTContext &Context) {
366 for (
const auto &ThrownException : ThrownExceptions) {
367 const Type *ExceptionTy = ThrownException.getFirst();
368 const CanQualType ExceptionCanTy =
369 ExceptionTy->getCanonicalTypeUnqualified();
370 const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
374 if (ExceptionCanTy == HandlerCanTy) {
375 TypesToDelete.push_back(ExceptionTy);
381 HandlerCanTy->getTypePtr())) {
382 TypesToDelete.push_back(ExceptionTy);
385 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
386 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
387 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
397 TypesToDelete.push_back(ExceptionTy);
401 TypesToDelete.push_back(ExceptionTy);
405 Context.getLangOpts())) {
406 TypesToDelete.push_back(ExceptionTy);
413 ExceptionCanTy->isNullPtrType()) {
414 TypesToDelete.push_back(ExceptionTy);
420 for (
const Type *TypeToDelete : TypesToDelete) {
421 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
422 if (DeleteIt != ThrownExceptions.end()) {
423 DeletedExceptions.insert(*DeleteIt);
424 ThrownExceptions.erase(DeleteIt);
428 reevaluateBehaviour();
429 return DeletedExceptions;
434 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
438 for (
const auto &ThrownException : ThrownExceptions) {
439 const Type *T = ThrownException.getFirst();
442 if (
const auto *TD = T->getAsTagDecl()) {
443 if (TD->getDeclName().isIdentifier()) {
444 if ((IgnoreBadAlloc &&
445 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
446 (IgnoredTypes.contains(TD->getName())))
447 TypesToDelete.push_back(T);
451 for (
const Type *T : TypesToDelete)
452 ThrownExceptions.erase(T);
454 reevaluateBehaviour();
460 ContainsUnknown =
false;
461 ThrowsUnknown =
false;
462 ThrownExceptions.clear();
465void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
466 if (ThrownExceptions.empty() && !ThrowsUnknown)
481 if (
const Stmt *Body = Func->getBody()) {
483 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
486 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
487 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
488 const ExceptionInfo Excs =
489 throwsException(Init->getInit(), Caught,
CallStack);
496 if (AssumeUnannotatedFunctionsAsThrowing &&
498 Result.registerException(
nullptr, {Func->getLocation(),
CallStack});
507 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
508 if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
511 for (
const QualType &Ex : FPT->exceptions()) {
513 Result.registerException(
515 {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
520 if (AssumeMissingDefinitionsFunctionsAsThrowing &&
523 Result.registerException(
nullptr, {Func->getLocation(),
CallStack});
532ExceptionAnalyzer::ExceptionInfo
533ExceptionAnalyzer::throwsException(
const Stmt *St,
540 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
541 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
542 const auto *ThrownType =
543 ThrownExpr->getType()->getUnqualifiedDesugaredType();
544 if (ThrownType->isReferenceType())
545 ThrownType = ThrownType->castAs<ReferenceType>()
547 ->getUnqualifiedDesugaredType();
548 Results.registerException(
549 ThrownExpr->getType()->getUnqualifiedDesugaredType(),
550 {Throw->getBeginLoc(), CallStack});
555 Results.registerExceptions(Caught);
557 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
559 throwsException(Try->getTryBlock(), Caught,
CallStack);
560 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
561 const CXXCatchStmt *Catch = Try->getHandler(I);
564 if (!Catch->getExceptionDecl()) {
566 Catch->getHandlerBlock(), Uncaught.getExceptions(),
CallStack);
567 Results.merge(Rethrown);
570 const auto *CaughtType =
571 Catch->getCaughtType()->getUnqualifiedDesugaredType();
572 if (CaughtType->isReferenceType()) {
573 CaughtType = CaughtType->castAs<ReferenceType>()
575 ->getUnqualifiedDesugaredType();
583 Uncaught.filterByCatch(CaughtType,
584 Catch->getExceptionDecl()->getASTContext());
585 if (!FilteredExceptions.empty()) {
587 Catch->getHandlerBlock(), FilteredExceptions,
CallStack);
588 Results.merge(Rethrown);
592 Results.merge(Uncaught);
593 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
595 throwsException(DefaultInit->getExpr(), Caught,
CallStack);
597 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
598 for (
const Stmt *Child : Coro->childrenExclBody()) {
599 if (Child != Coro->getExceptionHandler()) {
605 throwsException(Coro->getBody(), Caught,
CallStack);
606 Results.merge(throwsException(Coro->getExceptionHandler(),
608 for (
const auto &Exception : Excs.getExceptions()) {
609 const Type *ExcType = Exception.getFirst();
610 if (
const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
612 ThrowableRec->getDestructor(), Caught,
CallStack, SourceLocation{});
613 Results.merge(DestructorExcs);
616 }
else if (
const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
617 for (
const Stmt *Init : Lambda->capture_inits()) {
623 for (
const Stmt *Child : St->children()) {
630 if (
const auto *Call = dyn_cast<CallExpr>(St)) {
631 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
633 throwsException(Func, Caught,
CallStack,
Call->getBeginLoc());
636 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
638 throwsException(Construct->getConstructor(), Caught,
CallStack,
639 Construct->getBeginLoc());
646ExceptionAnalyzer::ExceptionInfo
647ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
651 const auto CacheEntry = FunctionCache.find(Func);
652 if (CacheEntry == FunctionCache.end()) {
661 FunctionCache.try_emplace(Func, ExceptionList);
663 ExceptionList = CacheEntry->getSecond();
666 return ExceptionList;
669ExceptionAnalyzer::ExceptionInfo
670ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
676ExceptionAnalyzer::ExceptionInfo
677ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
682 return ExceptionList;
686 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
688 return ExceptionList;
691ExceptionAnalyzer::ExceptionInfo
693 return analyzeDispatch(Func);
697 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.