xref: /llvm-project/clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp (revision 3d56ea05b6c746a7144f643bef2ebd599f605b8b)
15abe338fSAMS21 //===--- ExceptionSpecAnalyzer.cpp - clang-tidy ---------------------------===//
25abe338fSAMS21 //
35abe338fSAMS21 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45abe338fSAMS21 // See https://llvm.org/LICENSE.txt for license information.
55abe338fSAMS21 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65abe338fSAMS21 //
75abe338fSAMS21 //===----------------------------------------------------------------------===//
85abe338fSAMS21 
95abe338fSAMS21 #include "ExceptionSpecAnalyzer.h"
105abe338fSAMS21 
115abe338fSAMS21 #include "clang/AST/Expr.h"
125abe338fSAMS21 
135abe338fSAMS21 namespace clang::tidy::utils {
145abe338fSAMS21 
155abe338fSAMS21 ExceptionSpecAnalyzer::State
analyze(const FunctionDecl * FuncDecl)165abe338fSAMS21 ExceptionSpecAnalyzer::analyze(const FunctionDecl *FuncDecl) {
175abe338fSAMS21   // Check if the function has already been analyzed and reuse that result.
185abe338fSAMS21   const auto CacheEntry = FunctionCache.find(FuncDecl);
195abe338fSAMS21   if (CacheEntry == FunctionCache.end()) {
20cbdc3e1bSPiotr Zegar     ExceptionSpecAnalyzer::State State = analyzeImpl(FuncDecl);
215abe338fSAMS21 
225abe338fSAMS21     // Cache the result of the analysis.
235abe338fSAMS21     FunctionCache.try_emplace(FuncDecl, State);
245abe338fSAMS21     return State;
255abe338fSAMS21   }
265abe338fSAMS21 
27cbdc3e1bSPiotr Zegar   return CacheEntry->getSecond();
28cbdc3e1bSPiotr Zegar }
29cbdc3e1bSPiotr Zegar 
305abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeUnresolvedOrDefaulted(const CXXMethodDecl * MethodDecl,const FunctionProtoType * FuncProto)315abe338fSAMS21 ExceptionSpecAnalyzer::analyzeUnresolvedOrDefaulted(
325abe338fSAMS21     const CXXMethodDecl *MethodDecl, const FunctionProtoType *FuncProto) {
335abe338fSAMS21   if (!FuncProto || !MethodDecl)
345abe338fSAMS21     return State::Unknown;
355abe338fSAMS21 
365abe338fSAMS21   const DefaultableMemberKind Kind = getDefaultableMemberKind(MethodDecl);
375abe338fSAMS21 
385abe338fSAMS21   if (Kind == DefaultableMemberKind::None)
395abe338fSAMS21     return State::Unknown;
405abe338fSAMS21 
415abe338fSAMS21   return analyzeRecord(MethodDecl->getParent(), Kind, SkipMethods::Yes);
425abe338fSAMS21 }
435abe338fSAMS21 
445abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeFieldDecl(const FieldDecl * FDecl,DefaultableMemberKind Kind)455abe338fSAMS21 ExceptionSpecAnalyzer::analyzeFieldDecl(const FieldDecl *FDecl,
465abe338fSAMS21                                         DefaultableMemberKind Kind) {
475abe338fSAMS21   if (!FDecl)
485abe338fSAMS21     return State::Unknown;
495abe338fSAMS21 
505abe338fSAMS21   if (const CXXRecordDecl *RecDecl =
515abe338fSAMS21           FDecl->getType()->getUnqualifiedDesugaredType()->getAsCXXRecordDecl())
525abe338fSAMS21     return analyzeRecord(RecDecl, Kind);
535abe338fSAMS21 
545abe338fSAMS21   // Trivial types do not throw
555abe338fSAMS21   if (FDecl->getType().isTrivialType(FDecl->getASTContext()))
565abe338fSAMS21     return State::NotThrowing;
575abe338fSAMS21 
585abe338fSAMS21   return State::Unknown;
595abe338fSAMS21 }
605abe338fSAMS21 
615abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeBase(const CXXBaseSpecifier & Base,DefaultableMemberKind Kind)625abe338fSAMS21 ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier &Base,
635abe338fSAMS21                                    DefaultableMemberKind Kind) {
645abe338fSAMS21   const auto *RecType = Base.getType()->getAs<RecordType>();
655abe338fSAMS21   if (!RecType)
665abe338fSAMS21     return State::Unknown;
675abe338fSAMS21 
685abe338fSAMS21   const auto *BaseClass = cast<CXXRecordDecl>(RecType->getDecl());
695abe338fSAMS21 
705abe338fSAMS21   return analyzeRecord(BaseClass, Kind);
715abe338fSAMS21 }
725abe338fSAMS21 
735abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeRecord(const CXXRecordDecl * RecordDecl,DefaultableMemberKind Kind,SkipMethods SkipMethods)745abe338fSAMS21 ExceptionSpecAnalyzer::analyzeRecord(const CXXRecordDecl *RecordDecl,
755abe338fSAMS21                                      DefaultableMemberKind Kind,
765abe338fSAMS21                                      SkipMethods SkipMethods) {
775abe338fSAMS21   if (!RecordDecl)
785abe338fSAMS21     return State::Unknown;
795abe338fSAMS21 
805abe338fSAMS21   // Trivial implies noexcept
815abe338fSAMS21   if (hasTrivialMemberKind(RecordDecl, Kind))
825abe338fSAMS21     return State::NotThrowing;
835abe338fSAMS21 
845abe338fSAMS21   if (SkipMethods == SkipMethods::No)
855abe338fSAMS21     for (const auto *MethodDecl : RecordDecl->methods())
865abe338fSAMS21       if (getDefaultableMemberKind(MethodDecl) == Kind)
875abe338fSAMS21         return analyze(MethodDecl);
885abe338fSAMS21 
895abe338fSAMS21   for (const auto &BaseSpec : RecordDecl->bases()) {
905abe338fSAMS21     State Result = analyzeBase(BaseSpec, Kind);
915abe338fSAMS21     if (Result == State::Throwing || Result == State::Unknown)
925abe338fSAMS21       return Result;
935abe338fSAMS21   }
945abe338fSAMS21 
955abe338fSAMS21   for (const auto &BaseSpec : RecordDecl->vbases()) {
965abe338fSAMS21     State Result = analyzeBase(BaseSpec, Kind);
975abe338fSAMS21     if (Result == State::Throwing || Result == State::Unknown)
985abe338fSAMS21       return Result;
995abe338fSAMS21   }
1005abe338fSAMS21 
1015abe338fSAMS21   for (const auto *FDecl : RecordDecl->fields())
102*3d56ea05STimm Baeder     if (!FDecl->isInvalidDecl() && !FDecl->isUnnamedBitField()) {
1035abe338fSAMS21       State Result = analyzeFieldDecl(FDecl, Kind);
1045abe338fSAMS21       if (Result == State::Throwing || Result == State::Unknown)
1055abe338fSAMS21         return Result;
1065abe338fSAMS21     }
1075abe338fSAMS21 
1085abe338fSAMS21   return State::NotThrowing;
1095abe338fSAMS21 }
1105abe338fSAMS21 
1115abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeImpl(const FunctionDecl * FuncDecl)1125abe338fSAMS21 ExceptionSpecAnalyzer::analyzeImpl(const FunctionDecl *FuncDecl) {
1135abe338fSAMS21   const auto *FuncProto = FuncDecl->getType()->getAs<FunctionProtoType>();
1145abe338fSAMS21   if (!FuncProto)
1155abe338fSAMS21     return State::Unknown;
1165abe338fSAMS21 
1175abe338fSAMS21   const ExceptionSpecificationType EST = FuncProto->getExceptionSpecType();
1185abe338fSAMS21 
1195abe338fSAMS21   if (EST == EST_Unevaluated || (EST == EST_None && FuncDecl->isDefaulted()))
1205abe338fSAMS21     return analyzeUnresolvedOrDefaulted(cast<CXXMethodDecl>(FuncDecl),
1215abe338fSAMS21                                         FuncProto);
1225abe338fSAMS21 
1235abe338fSAMS21   return analyzeFunctionEST(FuncDecl, FuncProto);
1245abe338fSAMS21 }
1255abe338fSAMS21 
1265abe338fSAMS21 ExceptionSpecAnalyzer::State
analyzeFunctionEST(const FunctionDecl * FuncDecl,const FunctionProtoType * FuncProto)1275abe338fSAMS21 ExceptionSpecAnalyzer::analyzeFunctionEST(const FunctionDecl *FuncDecl,
1285abe338fSAMS21                                           const FunctionProtoType *FuncProto) {
1295abe338fSAMS21   if (!FuncDecl || !FuncProto)
1305abe338fSAMS21     return State::Unknown;
1315abe338fSAMS21 
1325abe338fSAMS21   if (isUnresolvedExceptionSpec(FuncProto->getExceptionSpecType()))
1335abe338fSAMS21     return State::Unknown;
1345abe338fSAMS21 
135474a2b93SAMS21   // A non defaulted destructor without the noexcept specifier is still noexcept
136474a2b93SAMS21   if (isa<CXXDestructorDecl>(FuncDecl) &&
137474a2b93SAMS21       FuncDecl->getExceptionSpecType() == EST_None)
138474a2b93SAMS21     return State::NotThrowing;
139474a2b93SAMS21 
1405abe338fSAMS21   switch (FuncProto->canThrow()) {
1415abe338fSAMS21   case CT_Cannot:
1425abe338fSAMS21     return State::NotThrowing;
1435abe338fSAMS21   case CT_Dependent: {
1445abe338fSAMS21     const Expr *NoexceptExpr = FuncProto->getNoexceptExpr();
145f9bd62fbSAMS21     if (!NoexceptExpr)
146f9bd62fbSAMS21       return State::NotThrowing;
147f9bd62fbSAMS21 
148f9bd62fbSAMS21     // We can't resolve value dependence so just return unknown
149f9bd62fbSAMS21     if (NoexceptExpr->isValueDependent())
150f9bd62fbSAMS21       return State::Unknown;
151f9bd62fbSAMS21 
152f9bd62fbSAMS21     // Try to evaluate the expression to a boolean value
153cbdc3e1bSPiotr Zegar     bool Result = false;
154f9bd62fbSAMS21     if (NoexceptExpr->EvaluateAsBooleanCondition(
155f9bd62fbSAMS21             Result, FuncDecl->getASTContext(), true))
156f9bd62fbSAMS21       return Result ? State::NotThrowing : State::Throwing;
157f9bd62fbSAMS21 
158f9bd62fbSAMS21     // The noexcept expression is not value dependent but we can't evaluate it
159f9bd62fbSAMS21     // as a boolean condition so we have no idea if its throwing or not
160f9bd62fbSAMS21     return State::Unknown;
1615abe338fSAMS21   }
1625abe338fSAMS21   default:
1635abe338fSAMS21     return State::Throwing;
1645abe338fSAMS21   };
1655abe338fSAMS21 }
1665abe338fSAMS21 
hasTrivialMemberKind(const CXXRecordDecl * RecDecl,DefaultableMemberKind Kind)1675abe338fSAMS21 bool ExceptionSpecAnalyzer::hasTrivialMemberKind(const CXXRecordDecl *RecDecl,
1685abe338fSAMS21                                                  DefaultableMemberKind Kind) {
1695abe338fSAMS21   if (!RecDecl)
1705abe338fSAMS21     return false;
1715abe338fSAMS21 
1725abe338fSAMS21   switch (Kind) {
1735abe338fSAMS21   case DefaultableMemberKind::DefaultConstructor:
1745abe338fSAMS21     return RecDecl->hasTrivialDefaultConstructor();
1755abe338fSAMS21   case DefaultableMemberKind::CopyConstructor:
1765abe338fSAMS21     return RecDecl->hasTrivialCopyConstructor();
1775abe338fSAMS21   case DefaultableMemberKind::MoveConstructor:
1785abe338fSAMS21     return RecDecl->hasTrivialMoveConstructor();
1795abe338fSAMS21   case DefaultableMemberKind::CopyAssignment:
1805abe338fSAMS21     return RecDecl->hasTrivialCopyAssignment();
1815abe338fSAMS21   case DefaultableMemberKind::MoveAssignment:
1825abe338fSAMS21     return RecDecl->hasTrivialMoveAssignment();
1835abe338fSAMS21   case DefaultableMemberKind::Destructor:
1845abe338fSAMS21     return RecDecl->hasTrivialDestructor();
1855abe338fSAMS21 
1865abe338fSAMS21   default:
1875abe338fSAMS21     return false;
1885abe338fSAMS21   }
1895abe338fSAMS21 }
1905abe338fSAMS21 
isConstructor(DefaultableMemberKind Kind)1915abe338fSAMS21 bool ExceptionSpecAnalyzer::isConstructor(DefaultableMemberKind Kind) {
1925abe338fSAMS21   switch (Kind) {
1935abe338fSAMS21   case DefaultableMemberKind::DefaultConstructor:
1945abe338fSAMS21   case DefaultableMemberKind::CopyConstructor:
1955abe338fSAMS21   case DefaultableMemberKind::MoveConstructor:
1965abe338fSAMS21     return true;
1975abe338fSAMS21 
1985abe338fSAMS21   default:
1995abe338fSAMS21     return false;
2005abe338fSAMS21   }
2015abe338fSAMS21 }
2025abe338fSAMS21 
isSpecialMember(DefaultableMemberKind Kind)2035abe338fSAMS21 bool ExceptionSpecAnalyzer::isSpecialMember(DefaultableMemberKind Kind) {
2045abe338fSAMS21   switch (Kind) {
2055abe338fSAMS21   case DefaultableMemberKind::DefaultConstructor:
2065abe338fSAMS21   case DefaultableMemberKind::CopyConstructor:
2075abe338fSAMS21   case DefaultableMemberKind::MoveConstructor:
2085abe338fSAMS21   case DefaultableMemberKind::CopyAssignment:
2095abe338fSAMS21   case DefaultableMemberKind::MoveAssignment:
2105abe338fSAMS21   case DefaultableMemberKind::Destructor:
2115abe338fSAMS21     return true;
2125abe338fSAMS21   default:
2135abe338fSAMS21     return false;
2145abe338fSAMS21   }
2155abe338fSAMS21 }
2165abe338fSAMS21 
isComparison(DefaultableMemberKind Kind)2175abe338fSAMS21 bool ExceptionSpecAnalyzer::isComparison(DefaultableMemberKind Kind) {
2185abe338fSAMS21   switch (Kind) {
2195abe338fSAMS21   case DefaultableMemberKind::CompareEqual:
2205abe338fSAMS21   case DefaultableMemberKind::CompareNotEqual:
2215abe338fSAMS21   case DefaultableMemberKind::CompareRelational:
2225abe338fSAMS21   case DefaultableMemberKind::CompareThreeWay:
2235abe338fSAMS21     return true;
2245abe338fSAMS21   default:
2255abe338fSAMS21     return false;
2265abe338fSAMS21   }
2275abe338fSAMS21 }
2285abe338fSAMS21 
2295abe338fSAMS21 ExceptionSpecAnalyzer::DefaultableMemberKind
getDefaultableMemberKind(const FunctionDecl * FuncDecl)2305abe338fSAMS21 ExceptionSpecAnalyzer::getDefaultableMemberKind(const FunctionDecl *FuncDecl) {
2315abe338fSAMS21   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl)) {
2325abe338fSAMS21     if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
2335abe338fSAMS21       if (Ctor->isDefaultConstructor())
2345abe338fSAMS21         return DefaultableMemberKind::DefaultConstructor;
2355abe338fSAMS21 
2365abe338fSAMS21       if (Ctor->isCopyConstructor())
2375abe338fSAMS21         return DefaultableMemberKind::CopyConstructor;
2385abe338fSAMS21 
2395abe338fSAMS21       if (Ctor->isMoveConstructor())
2405abe338fSAMS21         return DefaultableMemberKind::MoveConstructor;
2415abe338fSAMS21     }
2425abe338fSAMS21 
2435abe338fSAMS21     if (MethodDecl->isCopyAssignmentOperator())
2445abe338fSAMS21       return DefaultableMemberKind::CopyAssignment;
2455abe338fSAMS21 
2465abe338fSAMS21     if (MethodDecl->isMoveAssignmentOperator())
2475abe338fSAMS21       return DefaultableMemberKind::MoveAssignment;
2485abe338fSAMS21 
2495abe338fSAMS21     if (isa<CXXDestructorDecl>(FuncDecl))
2505abe338fSAMS21       return DefaultableMemberKind::Destructor;
2515abe338fSAMS21   }
2525abe338fSAMS21 
2535abe338fSAMS21   const LangOptions &LangOpts = FuncDecl->getLangOpts();
2545abe338fSAMS21 
2555abe338fSAMS21   switch (FuncDecl->getDeclName().getCXXOverloadedOperator()) {
2565abe338fSAMS21   case OO_EqualEqual:
2575abe338fSAMS21     return DefaultableMemberKind::CompareEqual;
2585abe338fSAMS21 
2595abe338fSAMS21   case OO_ExclaimEqual:
2605abe338fSAMS21     return DefaultableMemberKind::CompareNotEqual;
2615abe338fSAMS21 
2625abe338fSAMS21   case OO_Spaceship:
2635abe338fSAMS21     // No point allowing this if <=> doesn't exist in the current language mode.
2645abe338fSAMS21     if (!LangOpts.CPlusPlus20)
2655abe338fSAMS21       break;
2665abe338fSAMS21     return DefaultableMemberKind::CompareThreeWay;
2675abe338fSAMS21 
2685abe338fSAMS21   case OO_Less:
2695abe338fSAMS21   case OO_LessEqual:
2705abe338fSAMS21   case OO_Greater:
2715abe338fSAMS21   case OO_GreaterEqual:
2725abe338fSAMS21     // No point allowing this if <=> doesn't exist in the current language mode.
2735abe338fSAMS21     if (!LangOpts.CPlusPlus20)
2745abe338fSAMS21       break;
2755abe338fSAMS21     return DefaultableMemberKind::CompareRelational;
2765abe338fSAMS21 
2775abe338fSAMS21   default:
2785abe338fSAMS21     break;
2795abe338fSAMS21   }
2805abe338fSAMS21 
2815abe338fSAMS21   // Not a defaultable member kind
2825abe338fSAMS21   return DefaultableMemberKind::None;
2835abe338fSAMS21 }
2845abe338fSAMS21 
2855abe338fSAMS21 } // namespace clang::tidy::utils
286