brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · 84adbf3 Raw
363 lines · cpp
1//=======- ASTUtils.cpp ------------------------------------------*- C++ -*-==//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 "ASTUtils.h"10#include "PtrTypesSemantics.h"11#include "clang/AST/Attr.h"12#include "clang/AST/Decl.h"13#include "clang/AST/DeclCXX.h"14#include "clang/AST/ExprCXX.h"15#include "clang/AST/ExprObjC.h"16#include "clang/AST/StmtVisitor.h"17#include <optional>18 19namespace clang {20 21bool isSafePtr(clang::CXXRecordDecl *Decl) {22  return isRefCounted(Decl) || isCheckedPtr(Decl);23}24 25bool tryToFindPtrOrigin(26    const Expr *E, bool StopAtFirstRefCountedObj,27    std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,28    std::function<bool(const clang::QualType)> isSafePtrType,29    std::function<bool(const clang::Decl *)> isSafeGlobalDecl,30    std::function<bool(const clang::Expr *, bool)> callback) {31  while (E) {32    if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {33      if (auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {34        auto QT = VD->getType();35        auto IsImmortal = safeGetName(VD) == "NSApp";36        if (VD->hasGlobalStorage() && (IsImmortal || QT.isConstQualified()))37          return callback(E, true);38        if (VD->hasGlobalStorage() && isSafeGlobalDecl(VD))39          return callback(E, true);40      }41    }42    if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {43      E = tempExpr->getSubExpr();44      continue;45    }46    if (auto *tempExpr = dyn_cast<CXXBindTemporaryExpr>(E)) {47      E = tempExpr->getSubExpr();48      continue;49    }50    if (auto *tempExpr = dyn_cast<CXXConstructExpr>(E)) {51      if (auto *C = tempExpr->getConstructor()) {52        if (auto *Class = C->getParent(); Class && isSafePtr(Class))53          return callback(E, true);54        break;55      }56    }57    if (auto *TempExpr = dyn_cast<CXXUnresolvedConstructExpr>(E)) {58      if (isSafePtrType(TempExpr->getTypeAsWritten()))59        return callback(TempExpr, true);60    }61    if (auto *POE = dyn_cast<PseudoObjectExpr>(E)) {62      if (auto *RF = POE->getResultExpr()) {63        E = RF;64        continue;65      }66    }67    if (auto *tempExpr = dyn_cast<ParenExpr>(E)) {68      E = tempExpr->getSubExpr();69      continue;70    }71    if (auto *OpaqueValue = dyn_cast<OpaqueValueExpr>(E)) {72      E = OpaqueValue->getSourceExpr();73      continue;74    }75    if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {76      return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,77                                isSafePtr, isSafePtrType, isSafeGlobalDecl,78                                callback) &&79             tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,80                                isSafePtr, isSafePtrType, isSafeGlobalDecl,81                                callback);82    }83    if (auto *cast = dyn_cast<CastExpr>(E)) {84      if (StopAtFirstRefCountedObj) {85        if (auto *ConversionFunc =86                dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {87          if (isCtorOfSafePtr(ConversionFunc))88            return callback(E, true);89        }90        if (isa<CXXFunctionalCastExpr>(E) && isSafePtrType(cast->getType()))91          return callback(E, true);92      }93      // FIXME: This can give false "origin" that would lead to false negatives94      // in checkers. See https://reviews.llvm.org/D37023 for reference.95      E = cast->getSubExpr();96      continue;97    }98    if (auto *call = dyn_cast<CallExpr>(E)) {99      if (auto *Callee = call->getCalleeDecl()) {100        if (Callee->hasAttr<CFReturnsRetainedAttr>() ||101            Callee->hasAttr<NSReturnsRetainedAttr>() ||102            Callee->hasAttr<NSReturnsAutoreleasedAttr>()) {103          return callback(E, true);104        }105      }106 107      if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {108        if (auto *decl = memberCall->getMethodDecl()) {109          std::optional<bool> IsGetterOfRefCt = isGetterOfSafePtr(decl);110          if (IsGetterOfRefCt && *IsGetterOfRefCt) {111            E = memberCall->getImplicitObjectArgument();112            if (StopAtFirstRefCountedObj) {113              return callback(E, true);114            }115            continue;116          }117        }118      }119 120      if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(E)) {121        if (auto *Callee = operatorCall->getDirectCallee()) {122          auto ClsName = safeGetName(Callee->getParent());123          if (isRefType(ClsName) || isCheckedPtr(ClsName) ||124              isRetainPtrOrOSPtr(ClsName) || ClsName == "unique_ptr" ||125              ClsName == "UniqueRef" || ClsName == "WeakPtr" ||126              ClsName == "WeakRef") {127            if (operatorCall->getNumArgs() == 1) {128              E = operatorCall->getArg(0);129              continue;130            }131          }132        }133      }134 135      if (call->isCallToStdMove() && call->getNumArgs() == 1) {136        E = call->getArg(0)->IgnoreParenCasts();137        continue;138      }139 140      if (auto *callee = call->getDirectCallee()) {141        if (isCtorOfSafePtr(callee)) {142          if (StopAtFirstRefCountedObj)143            return callback(E, true);144 145          E = call->getArg(0);146          continue;147        }148 149        if (isSafePtrType(callee->getReturnType()))150          return callback(E, true);151 152        if (isSingleton(callee))153          return callback(E, true);154 155        if (callee->isInStdNamespace() && safeGetName(callee) == "forward") {156          E = call->getArg(0);157          continue;158        }159 160        if (isPtrConversion(callee)) {161          E = call->getArg(0);162          continue;163        }164 165        auto Name = safeGetName(callee);166        if (Name == "__builtin___CFStringMakeConstantString" ||167            Name == "NSStringFromSelector" || Name == "NSSelectorFromString" ||168            Name == "NSStringFromClass" || Name == "NSClassFromString" ||169            Name == "NSStringFromProtocol" || Name == "NSProtocolFromString")170          return callback(E, true);171      } else if (auto *CalleeE = call->getCallee()) {172        if (auto *E = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {173          if (isSingleton(E->getFoundDecl()))174            return callback(E, true);175        }176 177        if (auto *MemberExpr = dyn_cast<CXXDependentScopeMemberExpr>(CalleeE)) {178          auto *Base = MemberExpr->getBase();179          auto MemberName = MemberExpr->getMember().getAsString();180          bool IsGetter = MemberName == "get" || MemberName == "ptr";181          if (Base && isSafePtrType(Base->getType()) && IsGetter)182            return callback(E, true);183        }184      }185 186      // Sometimes, canonical type erroneously turns Ref<T> into T.187      // Workaround this problem by checking again if the original type was188      // a SubstTemplateTypeParmType of a safe smart pointer type (e.g. Ref).189      if (auto *CalleeDecl = call->getCalleeDecl()) {190        if (auto *FD = dyn_cast<FunctionDecl>(CalleeDecl)) {191          auto RetType = FD->getReturnType();192          if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(RetType)) {193            if (auto *SubstType = Subst->desugar().getTypePtr()) {194              if (auto *RD = dyn_cast<RecordType>(SubstType)) {195                if (auto *CXX = dyn_cast<CXXRecordDecl>(RD->getDecl()))196                  if (isSafePtr(CXX))197                    return callback(E, true);198              }199            }200          }201        }202      }203    }204    if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {205      if (auto *Method = ObjCMsgExpr->getMethodDecl()) {206        if (isSafePtrType(Method->getReturnType()))207          return callback(E, true);208      }209      auto Selector = ObjCMsgExpr->getSelector();210      auto NameForFirstSlot = Selector.getNameForSlot(0);211      if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass") &&212          !Selector.getNumArgs())213        return callback(E, true);214    }215    if (auto *ObjCProtocol = dyn_cast<ObjCProtocolExpr>(E))216      return callback(ObjCProtocol, true);217    if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(E))218      return callback(ObjCDict, true);219    if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(E))220      return callback(ObjCArray, true);221    if (auto *ObjCStr = dyn_cast<ObjCStringLiteral>(E))222      return callback(ObjCStr, true);223    if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {224      // FIXME: Currently accepts ANY unary operator. Is it OK?225      E = unaryOp->getSubExpr();226      continue;227    }228    if (auto *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) {229      if (StopAtFirstRefCountedObj)230        return callback(BoxedExpr, true);231      E = BoxedExpr->getSubExpr();232      continue;233    }234    break;235  }236  // Some other expression.237  return callback(E, false);238}239 240bool isASafeCallArg(const Expr *E) {241  assert(E);242  if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {243    auto *FoundDecl = Ref->getFoundDecl();244    if (auto *D = dyn_cast_or_null<VarDecl>(FoundDecl)) {245      if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())246        return true;247      if (auto *ImplicitP = dyn_cast<ImplicitParamDecl>(D)) {248        auto Kind = ImplicitP->getParameterKind();249        if (Kind == ImplicitParamKind::ObjCSelf ||250            Kind == ImplicitParamKind::ObjCCmd ||251            Kind == ImplicitParamKind::CXXThis ||252            Kind == ImplicitParamKind::CXXVTT)253          return true;254      }255    } else if (auto *BD = dyn_cast_or_null<BindingDecl>(FoundDecl)) {256      VarDecl *VD = BD->getHoldingVar();257      if (VD && (isa<ParmVarDecl>(VD) || VD->isLocalVarDecl()))258        return true;259    }260  }261  if (isa<CXXTemporaryObjectExpr>(E))262    return true; // A temporary lives until the end of this statement.263  if (isConstOwnerPtrMemberExpr(E))264    return true;265 266  // TODO: checker for method calls on non-refcounted objects267  return isa<CXXThisExpr>(E);268}269 270bool isNullPtr(const clang::Expr *E) {271  if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E))272    return true;273  if (auto *Int = dyn_cast_or_null<IntegerLiteral>(E)) {274    if (Int->getValue().isZero())275      return true;276  }277  return false;278}279 280bool isConstOwnerPtrMemberExpr(const clang::Expr *E) {281  if (auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {282    if (auto *Callee = MCE->getDirectCallee()) {283      auto Name = safeGetName(Callee);284      if (Name == "get" || Name == "ptr")285        E = MCE->getImplicitObjectArgument();286      if (isa<CXXConversionDecl>(Callee))287        E = MCE->getImplicitObjectArgument();288    }289  } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(E)) {290    if (OCE->getOperator() == OO_Star && OCE->getNumArgs() == 1)291      E = OCE->getArg(0);292  }293  const ValueDecl *D = nullptr;294  if (auto *ME = dyn_cast<MemberExpr>(E))295    D = ME->getMemberDecl();296  else if (auto *IVR = dyn_cast<ObjCIvarRefExpr>(E))297    D = IVR->getDecl();298  if (!D)299    return false;300  auto T = D->getType();301  return isOwnerPtrType(T) && T.isConstQualified();302}303 304bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E) {305  auto *ME = dyn_cast<MemberExpr>(E);306  if (!ME)307    return false;308  auto *Base = ME->getBase();309  if (!Base)310    return false;311  if (!isa<CXXThisExpr>(Base->IgnoreParenCasts()))312    return false;313  auto *D = ME->getMemberDecl();314  if (!D)315    return false;316  auto T = D->getType();317  auto *CXXRD = T->getAsCXXRecordDecl();318  if (!CXXRD)319    return false;320  auto result = isCheckedPtrCapable(CXXRD);321  return result && *result;322}323 324class EnsureFunctionVisitor325    : public ConstStmtVisitor<EnsureFunctionVisitor, bool> {326public:327  bool VisitStmt(const Stmt *S) {328    for (const Stmt *Child : S->children()) {329      if (Child && !Visit(Child))330        return false;331    }332    return true;333  }334 335  bool VisitReturnStmt(const ReturnStmt *RS) {336    if (auto *RV = RS->getRetValue()) {337      RV = RV->IgnoreParenCasts();338      if (isNullPtr(RV))339        return true;340      return isConstOwnerPtrMemberExpr(RV);341    }342    return false;343  }344};345 346bool EnsureFunctionAnalysis::isACallToEnsureFn(const clang::Expr *E) const {347  auto *MCE = dyn_cast<CXXMemberCallExpr>(E);348  if (!MCE)349    return false;350  auto *Callee = MCE->getDirectCallee();351  if (!Callee)352    return false;353  auto *Body = Callee->getBody();354  if (!Body || Callee->isVirtualAsWritten())355    return false;356  auto [CacheIt, IsNew] = Cache.insert(std::make_pair(Callee, false));357  if (IsNew)358    CacheIt->second = EnsureFunctionVisitor().Visit(Body);359  return CacheIt->second;360}361 362} // namespace clang363