286 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ExceptionSpecAnalyzer.h"10 11#include "clang/AST/Expr.h"12#include "clang/AST/Type.h"13 14namespace clang::tidy::utils {15 16ExceptionSpecAnalyzer::State17ExceptionSpecAnalyzer::analyze(const FunctionDecl *FuncDecl) {18 // Check if function exist in cache or add temporary value to cache to protect19 // against endless recursion.20 const auto [CacheEntry, NotFound] =21 FunctionCache.try_emplace(FuncDecl, State::NotThrowing);22 if (NotFound) {23 const ExceptionSpecAnalyzer::State State = analyzeImpl(FuncDecl);24 // Update result with calculated value25 FunctionCache[FuncDecl] = State;26 return State;27 }28 29 return CacheEntry->getSecond();30}31 32ExceptionSpecAnalyzer::State33ExceptionSpecAnalyzer::analyzeUnresolvedOrDefaulted(34 const CXXMethodDecl *MethodDecl, const FunctionProtoType *FuncProto) {35 if (!FuncProto || !MethodDecl)36 return State::Unknown;37 38 const DefaultableMemberKind Kind = getDefaultableMemberKind(MethodDecl);39 40 if (Kind == DefaultableMemberKind::None)41 return State::Unknown;42 43 return analyzeRecord(MethodDecl->getParent(), Kind, SkipMethods::Yes);44}45 46ExceptionSpecAnalyzer::State47ExceptionSpecAnalyzer::analyzeFieldDecl(const FieldDecl *FDecl,48 DefaultableMemberKind Kind) {49 if (!FDecl)50 return State::Unknown;51 52 if (const CXXRecordDecl *RecDecl =53 FDecl->getType()->getUnqualifiedDesugaredType()->getAsCXXRecordDecl())54 return analyzeRecord(RecDecl, Kind);55 56 // Trivial types do not throw57 if (FDecl->getType().isTrivialType(FDecl->getASTContext()))58 return State::NotThrowing;59 60 return State::Unknown;61}62 63ExceptionSpecAnalyzer::State64ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier &Base,65 DefaultableMemberKind Kind) {66 const auto *RecType = Base.getType()->getAs<RecordType>();67 if (!RecType)68 return State::Unknown;69 70 return analyzeRecord(RecType->getAsCXXRecordDecl(), Kind);71}72 73ExceptionSpecAnalyzer::State74ExceptionSpecAnalyzer::analyzeRecord(const CXXRecordDecl *RecordDecl,75 DefaultableMemberKind Kind,76 SkipMethods SkipMethods) {77 if (!RecordDecl)78 return State::Unknown;79 80 // Trivial implies noexcept81 if (hasTrivialMemberKind(RecordDecl, Kind))82 return State::NotThrowing;83 84 if (SkipMethods == SkipMethods::No)85 for (const auto *MethodDecl : RecordDecl->methods())86 if (getDefaultableMemberKind(MethodDecl) == Kind)87 return analyze(MethodDecl);88 89 for (const auto &BaseSpec : RecordDecl->bases()) {90 const State Result = analyzeBase(BaseSpec, Kind);91 if (Result == State::Throwing || Result == State::Unknown)92 return Result;93 }94 95 for (const auto &BaseSpec : RecordDecl->vbases()) {96 const State Result = analyzeBase(BaseSpec, Kind);97 if (Result == State::Throwing || Result == State::Unknown)98 return Result;99 }100 101 for (const auto *FDecl : RecordDecl->fields())102 if (!FDecl->isInvalidDecl() && !FDecl->isUnnamedBitField()) {103 const State Result = analyzeFieldDecl(FDecl, Kind);104 if (Result == State::Throwing || Result == State::Unknown)105 return Result;106 }107 108 return State::NotThrowing;109}110 111ExceptionSpecAnalyzer::State112ExceptionSpecAnalyzer::analyzeImpl(const FunctionDecl *FuncDecl) {113 const auto *FuncProto = FuncDecl->getType()->getAs<FunctionProtoType>();114 if (!FuncProto)115 return State::Unknown;116 117 const ExceptionSpecificationType EST = FuncProto->getExceptionSpecType();118 119 if (EST == EST_Unevaluated || (EST == EST_None && FuncDecl->isDefaulted()))120 return analyzeUnresolvedOrDefaulted(cast<CXXMethodDecl>(FuncDecl),121 FuncProto);122 123 return analyzeFunctionEST(FuncDecl, FuncProto);124}125 126ExceptionSpecAnalyzer::State127ExceptionSpecAnalyzer::analyzeFunctionEST(const FunctionDecl *FuncDecl,128 const FunctionProtoType *FuncProto) {129 if (!FuncDecl || !FuncProto)130 return State::Unknown;131 132 if (isUnresolvedExceptionSpec(FuncProto->getExceptionSpecType()))133 return State::Unknown;134 135 // A non defaulted destructor without the noexcept specifier is still noexcept136 if (isa<CXXDestructorDecl>(FuncDecl) &&137 FuncDecl->getExceptionSpecType() == EST_None)138 return State::NotThrowing;139 140 switch (FuncProto->canThrow()) {141 case CT_Cannot:142 return State::NotThrowing;143 case CT_Dependent: {144 const Expr *NoexceptExpr = FuncProto->getNoexceptExpr();145 if (!NoexceptExpr)146 return State::NotThrowing;147 148 // We can't resolve value dependence so just return unknown149 if (NoexceptExpr->isValueDependent())150 return State::Unknown;151 152 // Try to evaluate the expression to a boolean value153 bool Result = false;154 if (NoexceptExpr->EvaluateAsBooleanCondition(155 Result, FuncDecl->getASTContext(), true))156 return Result ? State::NotThrowing : State::Throwing;157 158 // The noexcept expression is not value dependent but we can't evaluate it159 // as a boolean condition so we have no idea if its throwing or not160 return State::Unknown;161 }162 default:163 return State::Throwing;164 };165}166 167bool ExceptionSpecAnalyzer::hasTrivialMemberKind(const CXXRecordDecl *RecDecl,168 DefaultableMemberKind Kind) {169 if (!RecDecl)170 return false;171 172 switch (Kind) {173 case DefaultableMemberKind::DefaultConstructor:174 return RecDecl->hasTrivialDefaultConstructor();175 case DefaultableMemberKind::CopyConstructor:176 return RecDecl->hasTrivialCopyConstructor();177 case DefaultableMemberKind::MoveConstructor:178 return RecDecl->hasTrivialMoveConstructor();179 case DefaultableMemberKind::CopyAssignment:180 return RecDecl->hasTrivialCopyAssignment();181 case DefaultableMemberKind::MoveAssignment:182 return RecDecl->hasTrivialMoveAssignment();183 case DefaultableMemberKind::Destructor:184 return RecDecl->hasTrivialDestructor();185 186 default:187 return false;188 }189}190 191bool ExceptionSpecAnalyzer::isConstructor(DefaultableMemberKind Kind) {192 switch (Kind) {193 case DefaultableMemberKind::DefaultConstructor:194 case DefaultableMemberKind::CopyConstructor:195 case DefaultableMemberKind::MoveConstructor:196 return true;197 198 default:199 return false;200 }201}202 203bool ExceptionSpecAnalyzer::isSpecialMember(DefaultableMemberKind Kind) {204 switch (Kind) {205 case DefaultableMemberKind::DefaultConstructor:206 case DefaultableMemberKind::CopyConstructor:207 case DefaultableMemberKind::MoveConstructor:208 case DefaultableMemberKind::CopyAssignment:209 case DefaultableMemberKind::MoveAssignment:210 case DefaultableMemberKind::Destructor:211 return true;212 default:213 return false;214 }215}216 217bool ExceptionSpecAnalyzer::isComparison(DefaultableMemberKind Kind) {218 switch (Kind) {219 case DefaultableMemberKind::CompareEqual:220 case DefaultableMemberKind::CompareNotEqual:221 case DefaultableMemberKind::CompareRelational:222 case DefaultableMemberKind::CompareThreeWay:223 return true;224 default:225 return false;226 }227}228 229ExceptionSpecAnalyzer::DefaultableMemberKind230ExceptionSpecAnalyzer::getDefaultableMemberKind(const FunctionDecl *FuncDecl) {231 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl)) {232 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl)) {233 if (Ctor->isDefaultConstructor())234 return DefaultableMemberKind::DefaultConstructor;235 236 if (Ctor->isCopyConstructor())237 return DefaultableMemberKind::CopyConstructor;238 239 if (Ctor->isMoveConstructor())240 return DefaultableMemberKind::MoveConstructor;241 }242 243 if (MethodDecl->isCopyAssignmentOperator())244 return DefaultableMemberKind::CopyAssignment;245 246 if (MethodDecl->isMoveAssignmentOperator())247 return DefaultableMemberKind::MoveAssignment;248 249 if (isa<CXXDestructorDecl>(FuncDecl))250 return DefaultableMemberKind::Destructor;251 }252 253 const LangOptions &LangOpts = FuncDecl->getLangOpts();254 255 switch (FuncDecl->getDeclName().getCXXOverloadedOperator()) {256 case OO_EqualEqual:257 return DefaultableMemberKind::CompareEqual;258 259 case OO_ExclaimEqual:260 return DefaultableMemberKind::CompareNotEqual;261 262 case OO_Spaceship:263 // No point allowing this if <=> doesn't exist in the current language mode.264 if (!LangOpts.CPlusPlus20)265 break;266 return DefaultableMemberKind::CompareThreeWay;267 268 case OO_Less:269 case OO_LessEqual:270 case OO_Greater:271 case OO_GreaterEqual:272 // No point allowing this if <=> doesn't exist in the current language mode.273 if (!LangOpts.CPlusPlus20)274 break;275 return DefaultableMemberKind::CompareRelational;276 277 default:278 break;279 }280 281 // Not a defaultable member kind282 return DefaultableMemberKind::None;283}284 285} // namespace clang::tidy::utils286