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 RD && RD->isCompleteDefinition() &&
140 isBaseOf(From->getPointeeType().getTypePtr(),
141 To->getPointeeType().getTypePtr())) {
145 To->getPointeeType().getTypePtr());
152 if (!From->isFunctionPointerType() && !From->isFunctionType() &&
153 !From->isMemberFunctionPointerType())
156 if (!To->isFunctionPointerType() && !To->isMemberFunctionPointerType())
159 if (To->isFunctionPointerType()) {
160 if (From->isFunctionPointerType())
161 return To->getPointeeType() == From->getPointeeType();
163 if (From->isFunctionType())
164 return To->getPointeeType() == From;
169 if (To->isMemberFunctionPointerType()) {
170 if (!From->isMemberFunctionPointerType())
173 const auto *FromMember = cast<MemberPointerType>(From);
174 const auto *ToMember = cast<MemberPointerType>(To);
178 return FromMember->getQualifier() == ToMember->getQualifier() &&
179 FromMember->getMostRecentCXXRecordDecl() ==
180 ToMember->getMostRecentCXXRecordDecl() &&
181 FromMember->getPointeeType() == ToMember->getPointeeType();
194 const LangOptions &LangOpts) {
210 auto IsValidP_i = [](QualType
P) {
211 return P->isPointerType() ||
P->isMemberPointerType() ||
212 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>()->getMostRecentCXXRecordDecl() ==
223 P2->getAs<MemberPointerType>()->getMostRecentCXXRecordDecl();
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 const auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
256 const QualType &To) {
257 if (I > 1 && From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
261 if (From.isConstQualified() && !To.isConstQualified())
264 if (From.isVolatileQualified() && !To.isVolatileQualified())
267 ConstUntilI = To.isConstQualified();
273 while (IsValidP_i(From) && IsValidP_i(To)) {
275 From = From.getCanonicalType();
276 To = To.getCanonicalType();
278 if (!SatisfiesCVRules(From, To))
281 if (!IsSameP_i(From, To)) {
282 if (LangOpts.CPlusPlus20) {
283 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
286 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
295 std::optional<QualType> FromPointeeOrElem =
297 std::optional<QualType> ToPointeeOrElem =
300 assert(FromPointeeOrElem &&
301 "From pointer or array has no pointee or element!");
302 assert(ToPointeeOrElem &&
"To pointer or array has no pointee or element!");
304 From = *FromPointeeOrElem;
305 To = *ToPointeeOrElem;
309 if (IsValidP_i(From) || IsValidP_i(To))
313 if (!SatisfiesCVRules(From, To))
316 return From.getTypePtr() == To.getTypePtr();
323 if (Func->isConsteval())
326 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
333 if (isUnresolvedExceptionSpec(FunProto->getExceptionSpecType()))
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) {
363 for (
const auto &ThrownException : ThrownExceptions) {
364 const Type *ExceptionTy = ThrownException.getFirst();
367 const CanQualType ExceptionCanTy =
368 ExceptionTy->getCanonicalTypeUnqualified();
369 const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
373 if (ExceptionCanTy == HandlerCanTy) {
374 TypesToDelete.push_back(ExceptionTy);
380 HandlerCanTy->getTypePtr())) {
381 TypesToDelete.push_back(ExceptionTy);
384 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
385 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
386 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
396 TypesToDelete.push_back(ExceptionTy);
400 TypesToDelete.push_back(ExceptionTy);
404 Context.getLangOpts())) {
405 TypesToDelete.push_back(ExceptionTy);
412 ExceptionCanTy->isNullPtrType()) {
413 TypesToDelete.push_back(ExceptionTy);
419 for (
const Type *TypeToDelete : TypesToDelete) {
420 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
421 if (DeleteIt != ThrownExceptions.end()) {
422 DeletedExceptions.insert(*DeleteIt);
423 ThrownExceptions.erase(DeleteIt);
427 reevaluateBehaviour();
428 return DeletedExceptions;
433 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
437 for (
const auto &ThrownException : ThrownExceptions) {
438 const Type *T = ThrownException.getFirst();
441 if (
const auto *TD = T->getAsTagDecl();
442 TD && TD->getDeclName().isIdentifier() &&
444 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
445 IgnoredTypes.contains(TD->getName())))
446 TypesToDelete.push_back(T);
448 for (
const Type *T : TypesToDelete)
449 ThrownExceptions.erase(T);
451 reevaluateBehaviour();
457 ContainsUnknown =
false;
458 ThrowsUnknown =
false;
459 ThrownExceptions.clear();
462void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
463 if (ThrownExceptions.empty() && !ThrowsUnknown)
478 if (
const Stmt *Body = Func->getBody()) {
480 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
483 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
484 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
485 const ExceptionInfo Excs =
486 throwsException(Init->getInit(), Caught,
CallStack);
493 if (AssumeUnannotatedFunctionsAsThrowing &&
495 Result.registerException(
nullptr, {Func->getLocation(),
CallStack});
509 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
510 if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
513 for (
const QualType &Ex : FPT->exceptions()) {
515 Result.registerException(
517 {Func->getExceptionSpecSourceRange().getBegin(), CallStack});
522 if (AssumeMissingDefinitionsFunctionsAsThrowing &&
525 Result.registerException(
nullptr, {Func->getLocation(),
CallStack});
534ExceptionAnalyzer::ExceptionInfo
535ExceptionAnalyzer::throwsException(
const Stmt *St,
542 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
543 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
544 const auto *ThrownType =
545 ThrownExpr->getType()->getUnqualifiedDesugaredType();
546 if (ThrownType->isReferenceType())
547 ThrownType = ThrownType->castAs<ReferenceType>()
549 ->getUnqualifiedDesugaredType();
550 Results.registerException(
551 ThrownExpr->getType()->getUnqualifiedDesugaredType(),
552 {Throw->getBeginLoc(), CallStack});
557 Results.registerExceptions(Caught);
559 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
561 throwsException(Try->getTryBlock(), Caught,
CallStack);
562 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
563 const CXXCatchStmt *Catch = Try->getHandler(I);
566 if (!Catch->getExceptionDecl()) {
568 Catch->getHandlerBlock(), Uncaught.getExceptions(),
CallStack);
569 Results.merge(Rethrown);
572 const auto *CaughtType =
573 Catch->getCaughtType()->getUnqualifiedDesugaredType();
574 if (CaughtType->isReferenceType()) {
575 CaughtType = CaughtType->castAs<ReferenceType>()
577 ->getUnqualifiedDesugaredType();
585 Uncaught.filterByCatch(CaughtType,
586 Catch->getExceptionDecl()->getASTContext());
587 if (!FilteredExceptions.empty()) {
589 Catch->getHandlerBlock(), FilteredExceptions,
CallStack);
590 Results.merge(Rethrown);
594 Results.merge(Uncaught);
595 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
597 throwsException(DefaultInit->getExpr(), Caught,
CallStack);
599 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
600 for (
const Stmt *Child : Coro->childrenExclBody()) {
601 if (Child != Coro->getExceptionHandler()) {
607 throwsException(Coro->getBody(), Caught,
CallStack);
608 Results.merge(throwsException(Coro->getExceptionHandler(),
610 for (
const auto &Exception : Excs.getExceptions()) {
611 const Type *ExcType = Exception.getFirst();
614 if (
const CXXRecordDecl *ThrowableRec = ExcType->getAsCXXRecordDecl()) {
616 ThrowableRec->getDestructor(), Caught,
CallStack, SourceLocation{});
617 Results.merge(DestructorExcs);
620 }
else if (
const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
621 for (
const Stmt *Init : Lambda->capture_inits()) {
627 for (
const Stmt *Child : St->children()) {
634 if (
const auto *Call = dyn_cast<CallExpr>(St)) {
635 if (
const FunctionDecl *Func =
Call->getDirectCallee()) {
637 throwsException(Func, Caught,
CallStack,
Call->getBeginLoc());
640 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
642 throwsException(Construct->getConstructor(), Caught,
CallStack,
643 Construct->getBeginLoc());
650ExceptionAnalyzer::ExceptionInfo
651ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
655 const auto CacheEntry = FunctionCache.find(Func);
656 if (CacheEntry == FunctionCache.end()) {
665 FunctionCache.try_emplace(Func, ExceptionList);
667 ExceptionList = CacheEntry->getSecond();
670 return ExceptionList;
673ExceptionAnalyzer::ExceptionInfo
674ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
680ExceptionAnalyzer::ExceptionInfo
681ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
686 return ExceptionList;
690 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
692 return ExceptionList;
695ExceptionAnalyzer::ExceptionInfo
697 return analyzeDispatch(Func);
701 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.