brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.9 KiB · 2da0966 Raw
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