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();
365 const CanQualType ExceptionCanTy =
366 ExceptionTy->getCanonicalTypeUnqualified();
367 const CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
371 if (ExceptionCanTy == HandlerCanTy) {
372 TypesToDelete.push_back(ExceptionTy);
378 HandlerCanTy->getTypePtr())) {
379 TypesToDelete.push_back(ExceptionTy);
382 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
383 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
384 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
394 TypesToDelete.push_back(ExceptionTy);
398 TypesToDelete.push_back(ExceptionTy);
402 Context.getLangOpts())) {
403 TypesToDelete.push_back(ExceptionTy);
410 ExceptionCanTy->isNullPtrType()) {
411 TypesToDelete.push_back(ExceptionTy);
417 for (
const Type *TypeToDelete : TypesToDelete) {
418 const auto DeleteIt = ThrownExceptions.find(TypeToDelete);
419 if (DeleteIt != ThrownExceptions.end()) {
420 DeletedExceptions.insert(*DeleteIt);
421 ThrownExceptions.erase(DeleteIt);
425 reevaluateBehaviour();
426 return DeletedExceptions;
431 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
435 for (
const auto &ThrownException : ThrownExceptions) {
436 const Type *T = ThrownException.getFirst();
439 if (
const auto *TD = T->getAsTagDecl();
440 TD && TD->getDeclName().isIdentifier() &&
442 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
443 IgnoredTypes.contains(TD->getName())))
444 TypesToDelete.push_back(T);
446 for (
const Type *T : TypesToDelete)
447 ThrownExceptions.erase(T);
449 reevaluateBehaviour();
455 ContainsUnknown =
false;
456 ThrowsUnknown =
false;
457 ThrownExceptions.clear();
460void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
461 if (ThrownExceptions.empty() && !ThrowsUnknown)
476 if (
const Stmt *Body = Func->getBody()) {
478 ExceptionInfo Result = throwsException(Body, Caught,
CallStack);
481 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
482 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
483 const ExceptionInfo Excs =
484 throwsException(Init->getInit(), Caught,
CallStack);
491 if (AssumeUnannotatedFunctionsAsThrowing &&
493 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.