14 const Type *ExceptionType) {
15 assert(ExceptionType !=
nullptr &&
"Only valid types are accepted");
17 ThrownExceptions.insert(ExceptionType);
22 if (Exceptions.empty())
25 ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
41 ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
42 ThrownExceptions.insert(Other.ThrownExceptions.begin(),
43 Other.ThrownExceptions.end());
50bool isUnambiguousPublicBaseClass(
const Type *DerivedType,
51 const Type *BaseType) {
52 const auto *DerivedClass =
53 DerivedType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
54 const auto *BaseClass =
55 BaseType->getCanonicalTypeUnqualified()->getAsCXXRecordDecl();
56 if (!DerivedClass || !BaseClass)
60 Paths.setOrigin(DerivedClass);
62 bool IsPublicBaseClass =
false;
63 DerivedClass->lookupInBases(
64 [&BaseClass, &IsPublicBaseClass](
const CXXBaseSpecifier *BS,
67 ->getCanonicalTypeUnqualified()
68 ->getAsCXXRecordDecl() == BaseClass &&
69 BS->getAccessSpecifier() == AS_public) {
70 IsPublicBaseClass = true;
78 return !Paths.isAmbiguous(BaseType->getCanonicalTypeUnqualified()) &&
82inline bool isPointerOrPointerToMember(
const Type *T) {
83 return T->isPointerType() || T->isMemberPointerType();
86std::optional<QualType> getPointeeOrArrayElementQualType(QualType T) {
87 if (T->isAnyPointerType() || T->isMemberPointerType())
88 return T->getPointeeType();
91 return T->getAsArrayTypeUnsafe()->getElementType();
96bool isBaseOf(
const Type *DerivedType,
const Type *BaseType) {
97 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
98 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
99 if (!DerivedClass || !BaseClass)
102 return !DerivedClass->forallBases(
103 [BaseClass](
const CXXRecordDecl *Cur) {
return Cur != BaseClass; });
107bool moreOrEquallyQualified(QualType T1, QualType T2) {
108 return T1.getQualifiers().isStrictSupersetOf(T2.getQualifiers()) ||
109 T1.getQualifiers() == T2.getQualifiers();
112bool isStandardPointerConvertible(QualType From, QualType To) {
113 assert((From->isPointerType() || From->isMemberPointerType()) &&
114 (To->isPointerType() || To->isMemberPointerType()) &&
115 "Pointer conversion should be performed on pointer types only.");
117 if (!moreOrEquallyQualified(To->getPointeeType(), From->getPointeeType()))
126 if (To->isPointerType() && From->isNullPtrType())
132 if (To->isVoidPointerType() && From->isObjectPointerType())
140 if (
const auto *RD = From->getPointeeCXXRecordDecl()) {
141 if (RD->isCompleteDefinition() &&
142 isBaseOf(From->getPointeeType().getTypePtr(),
143 To->getPointeeType().getTypePtr())) {
151bool isFunctionPointerConvertible(QualType From, QualType To) {
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->getClass() == ToMember->getClass() &&
179 FromMember->getPointeeType() == ToMember->getPointeeType();
191bool isQualificationConvertiblePointer(QualType From, QualType To,
192 LangOptions LangOpts) {
208 auto isValidP_i = [](QualType P) {
209 return P->isPointerType() || P->isMemberPointerType() ||
210 P->isConstantArrayType() || P->isIncompleteArrayType();
213 auto isSameP_i = [](QualType P1, QualType P2) {
214 if (P1->isPointerType())
215 return P2->isPointerType();
217 if (P1->isMemberPointerType())
218 return P2->isMemberPointerType() &&
219 P1->getAs<MemberPointerType>()->getClass() ==
220 P2->getAs<MemberPointerType>()->getClass();
222 if (P1->isConstantArrayType())
223 return P2->isConstantArrayType() &&
224 cast<ConstantArrayType>(P1)->getSize() ==
225 cast<ConstantArrayType>(P2)->getSize();
227 if (P1->isIncompleteArrayType())
228 return P2->isIncompleteArrayType();
251 bool ConstUntilI =
true;
252 auto SatisfiesCVRules = [&I, &ConstUntilI](
const QualType &From,
253 const QualType &To) {
255 if (From.getQualifiers() != To.getQualifiers() && !ConstUntilI)
260 if (From.isConstQualified() && !To.isConstQualified())
263 if (From.isVolatileQualified() && !To.isVolatileQualified())
266 ConstUntilI = To.isConstQualified();
272 while (isValidP_i(From) && isValidP_i(To)) {
274 From = From.getCanonicalType();
275 To = To.getCanonicalType();
277 if (!SatisfiesCVRules(From, To))
280 if (!isSameP_i(From, To)) {
281 if (LangOpts.CPlusPlus20) {
282 if (From->isConstantArrayType() && !To->isIncompleteArrayType())
285 if (From->isIncompleteArrayType() && !To->isIncompleteArrayType())
294 std::optional<QualType> FromPointeeOrElem =
295 getPointeeOrArrayElementQualType(From);
296 std::optional<QualType> ToPointeeOrElem =
297 getPointeeOrArrayElementQualType(To);
299 assert(FromPointeeOrElem &&
300 "From pointer or array has no pointee or element!");
301 assert(ToPointeeOrElem &&
"To pointer or array has no pointee or element!");
303 From = *FromPointeeOrElem;
304 To = *ToPointeeOrElem;
308 if (isValidP_i(From) || isValidP_i(To))
312 if (!SatisfiesCVRules(From, To))
315 return From.getTypePtr() == To.getTypePtr();
320 const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
324 switch (FunProto->canThrow()) {
328 const Expr *NoexceptExpr = FunProto->getNoexceptExpr();
332 if (NoexceptExpr->isValueDependent())
336 if (!NoexceptExpr->EvaluateAsBooleanCondition(Result, Func->getASTContext(),
348 const Type *HandlerTy,
const ASTContext &Context) {
349 llvm::SmallVector<const Type *, 8> TypesToDelete;
350 for (
const Type *ExceptionTy : ThrownExceptions) {
351 CanQualType ExceptionCanTy = ExceptionTy->getCanonicalTypeUnqualified();
352 CanQualType HandlerCanTy = HandlerTy->getCanonicalTypeUnqualified();
356 if (ExceptionCanTy == HandlerCanTy) {
357 TypesToDelete.push_back(ExceptionTy);
362 else if (isUnambiguousPublicBaseClass(ExceptionCanTy->getTypePtr(),
363 HandlerCanTy->getTypePtr())) {
364 TypesToDelete.push_back(ExceptionTy);
367 if (HandlerCanTy->getTypeClass() == Type::RValueReference ||
368 (HandlerCanTy->getTypeClass() == Type::LValueReference &&
369 !HandlerCanTy->getTypePtr()->getPointeeType().isConstQualified()))
374 if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
375 isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
378 if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy) &&
379 isUnambiguousPublicBaseClass(
380 ExceptionCanTy->getTypePtr()->getPointeeType().getTypePtr(),
381 HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr())) {
382 TypesToDelete.push_back(ExceptionTy);
385 else if (isFunctionPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
386 TypesToDelete.push_back(ExceptionTy);
389 else if (isQualificationConvertiblePointer(ExceptionCanTy, HandlerCanTy,
390 Context.getLangOpts())) {
391 TypesToDelete.push_back(ExceptionTy);
397 else if (isPointerOrPointerToMember(HandlerCanTy->getTypePtr()) &&
398 ExceptionCanTy->isNullPtrType()) {
399 TypesToDelete.push_back(ExceptionTy);
403 for (
const Type *T : TypesToDelete)
404 ThrownExceptions.erase(T);
406 reevaluateBehaviour();
407 return !TypesToDelete.empty();
412 const llvm::StringSet<> &IgnoredTypes,
bool IgnoreBadAlloc) {
413 llvm::SmallVector<const Type *, 8> TypesToDelete;
416 for (
const Type *T : ThrownExceptions) {
417 if (
const auto *TD = T->getAsTagDecl()) {
418 if (TD->getDeclName().isIdentifier()) {
419 if ((IgnoreBadAlloc &&
420 (TD->getName() ==
"bad_alloc" && TD->isInStdNamespace())) ||
421 (IgnoredTypes.count(TD->getName()) > 0))
422 TypesToDelete.push_back(T);
426 for (
const Type *T : TypesToDelete)
427 ThrownExceptions.erase(T);
429 reevaluateBehaviour();
435 ContainsUnknown =
false;
436 ThrownExceptions.clear();
439void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
440 if (ThrownExceptions.empty())
451 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
452 if (!Func || CallStack.count(Func) || (!CallStack.empty() && !
canThrow(Func)))
455 if (
const Stmt *Body = Func->getBody()) {
456 CallStack.insert(Func);
457 ExceptionInfo Result = throwsException(Body, Caught, CallStack);
460 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
461 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
463 throwsException(Init->getInit(), Caught, CallStack);
468 CallStack.erase(Func);
473 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
474 for (
const QualType &Ex : FPT->exceptions())
475 Result.registerException(Ex.getTypePtr());
482ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
484 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
489 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
490 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
491 const auto *ThrownType =
492 ThrownExpr->getType()->getUnqualifiedDesugaredType();
493 if (ThrownType->isReferenceType())
494 ThrownType = ThrownType->castAs<ReferenceType>()
496 ->getUnqualifiedDesugaredType();
498 ThrownExpr->getType()->getUnqualifiedDesugaredType());
503 Results.registerExceptions(Caught);
504 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
505 ExceptionInfo Uncaught =
506 throwsException(Try->getTryBlock(), Caught, CallStack);
507 for (
unsigned I = 0; I < Try->getNumHandlers(); ++I) {
508 const CXXCatchStmt *Catch = Try->getHandler(I);
511 if (!Catch->getExceptionDecl()) {
512 ExceptionInfo Rethrown = throwsException(
513 Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
517 const auto *CaughtType =
518 Catch->getCaughtType()->getUnqualifiedDesugaredType();
519 if (CaughtType->isReferenceType()) {
520 CaughtType = CaughtType->castAs<ReferenceType>()
522 ->getUnqualifiedDesugaredType();
529 if (Uncaught.filterByCatch(
530 CaughtType, Catch->getExceptionDecl()->getASTContext())) {
532 CaughtExceptions.insert(CaughtType);
533 ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
534 CaughtExceptions, CallStack);
540 }
else if (
const auto *Call = dyn_cast<CallExpr>(St)) {
541 if (
const FunctionDecl *Func = Call->getDirectCallee()) {
542 ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
545 }
else if (
const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
547 throwsException(Construct->getConstructor(), Caught, CallStack);
549 }
else if (
const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
551 throwsException(DefaultInit->getExpr(), Caught, CallStack);
553 }
else if (
const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
554 for (
const Stmt *Child : Coro->childrenExclBody()) {
555 if (Child != Coro->getExceptionHandler()) {
556 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
560 ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
561 Results.merge(throwsException(Coro->getExceptionHandler(),
562 Excs.getExceptionTypes(), CallStack));
563 for (
const Type *Throwable : Excs.getExceptionTypes()) {
564 if (
const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
565 ExceptionInfo DestructorExcs =
566 throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
571 for (
const Stmt *Child : St->children()) {
572 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
579ExceptionAnalyzer::ExceptionInfo
580ExceptionAnalyzer::analyzeImpl(
const FunctionDecl *Func) {
581 ExceptionInfo ExceptionList;
584 const auto CacheEntry = FunctionCache.find(Func);
585 if (CacheEntry == FunctionCache.end()) {
586 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
594 FunctionCache.try_emplace(Func, ExceptionList);
596 ExceptionList = CacheEntry->getSecond();
598 return ExceptionList;
601ExceptionAnalyzer::ExceptionInfo
602ExceptionAnalyzer::analyzeImpl(
const Stmt *Stmt) {
603 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
608ExceptionAnalyzer::ExceptionInfo
609ExceptionAnalyzer::analyzeDispatch(
const T *Node) {
610 ExceptionInfo ExceptionList = analyzeImpl(Node);
614 return ExceptionList;
618 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
620 return ExceptionList;
623ExceptionAnalyzer::ExceptionInfo
625 return analyzeDispatch(Func);
629 return analyzeDispatch(Stmt);
std::vector< CodeCompletionResult > Results
Bundle the gathered information about an entity like a function regarding it's exception behaviour.
bool 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 createNonThrowing()
void clear()
Clear the state to 'NonThrowing' to make the corresponding entity neutral.
void registerException(const Type *ExceptionType)
Register a single exception type as recognized potential exception to be thrown.
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...
static ExceptionInfo createUnknown()
void registerExceptions(const Throwables &Exceptions)
Registers a SmallVector of exception types as recognized potential exceptions to be thrown.
llvm::SmallSet< const Type *, 2 > Throwables
ExceptionInfo & merge(const ExceptionInfo &Other)
Updates the local state according to the other state.
@ 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.
ExceptionInfo analyze(const FunctionDecl *Func)
static bool canThrow(const FunctionDecl *Func)