brintos

brintos / llvm-project-archived public Read only

0
0
Text · 32.9 KiB · cad2c72 Raw
946 lines · cpp
1// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- 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//  This file defines diagnostics for RetainCountChecker, which implements10//  a reference count checker for Core Foundation and Cocoa on (Mac OS X).11//12//===----------------------------------------------------------------------===//13 14#include "RetainCountDiagnostics.h"15#include "RetainCountChecker.h"16#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/SmallVector.h"18#include <optional>19 20using namespace clang;21using namespace ento;22using namespace retaincountchecker;23 24static bool isNumericLiteralExpression(const Expr *E) {25  // FIXME: This set of cases was copied from SemaExprObjC.26  return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral,27             ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E);28}29 30/// If type represents a pointer to CXXRecordDecl,31/// and is not a typedef, return the decl name.32/// Otherwise, return the serialization of type.33static std::string getPrettyTypeName(QualType QT) {34  QualType PT = QT->getPointeeType();35  if (!PT.isNull() && !QT->getAs<TypedefType>())36    if (const auto *RD = PT->getAsCXXRecordDecl())37      return std::string(RD->getName());38  return QT.getAsString();39}40 41/// Write information about the type state change to @c os,42/// return whether the note should be generated.43static bool shouldGenerateNote(llvm::raw_string_ostream &os,44                               const RefVal *PrevT,45                               const RefVal &CurrV,46                               bool DeallocSent) {47  // Get the previous type state.48  RefVal PrevV = *PrevT;49 50  // Specially handle -dealloc.51  if (DeallocSent) {52    // Determine if the object's reference count was pushed to zero.53    assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");54    // We may not have transitioned to 'release' if we hit an error.55    // This case is handled elsewhere.56    if (CurrV.getKind() == RefVal::Released) {57      assert(CurrV.getCombinedCounts() == 0);58      os << "Object released by directly sending the '-dealloc' message";59      return true;60    }61  }62 63  // Determine if the typestate has changed.64  if (!PrevV.hasSameState(CurrV))65    switch (CurrV.getKind()) {66    case RefVal::Owned:67    case RefVal::NotOwned:68      if (PrevV.getCount() == CurrV.getCount()) {69        // Did an autorelease message get sent?70        if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())71          return false;72 73        assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());74        os << "Object autoreleased";75        return true;76      }77 78      if (PrevV.getCount() > CurrV.getCount())79        os << "Reference count decremented.";80      else81        os << "Reference count incremented.";82 83      if (unsigned Count = CurrV.getCount())84        os << " The object now has a +" << Count << " retain count.";85 86      return true;87 88    case RefVal::Released:89      if (CurrV.getIvarAccessHistory() ==90              RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&91          CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {92        os << "Strong instance variable relinquished. ";93      }94      os << "Object released.";95      return true;96 97    case RefVal::ReturnedOwned:98      // Autoreleases can be applied after marking a node ReturnedOwned.99      if (CurrV.getAutoreleaseCount())100        return false;101 102      os << "Object returned to caller as an owning reference (single "103            "retain count transferred to caller)";104      return true;105 106    case RefVal::ReturnedNotOwned:107      os << "Object returned to caller with a +0 retain count";108      return true;109 110    default:111      return false;112    }113  return true;114}115 116/// Finds argument index of the out paramter in the call @c S117/// corresponding to the symbol @c Sym.118/// If none found, returns std::nullopt.119static std::optional<unsigned>120findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx,121                   SymbolRef &Sym, std::optional<CallEventRef<>> CE) {122  if (!CE)123    return std::nullopt;124 125  for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)126    if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())127      if (const auto *TR = dyn_cast<TypedValueRegion>(MR))128        if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)129          return Idx;130 131  return std::nullopt;132}133 134static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {135  if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {136    if (ME->getMemberDecl()->getNameAsString() != "alloc")137      return std::nullopt;138    const Expr *This = ME->getBase()->IgnoreParenImpCasts();139    if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {140      const ValueDecl *VD = DRE->getDecl();141      if (VD->getNameAsString() != "metaClass")142        return std::nullopt;143 144      if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))145        return RD->getNameAsString();146 147    }148  }149  return std::nullopt;150}151 152static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {153  if (const auto *CE = dyn_cast<CallExpr>(S))154    if (auto Out = findMetaClassAlloc(CE->getCallee()))155      return *Out;156  return getPrettyTypeName(QT);157}158 159static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,160                                           const LocationContext *LCtx,161                                           const RefVal &CurrV, SymbolRef &Sym,162                                           const Stmt *S,163                                           llvm::raw_string_ostream &os) {164  CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();165  if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {166    // Get the name of the callee (if it is available)167    // from the tracked SVal.168    SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);169    const FunctionDecl *FD = X.getAsFunctionDecl();170 171    // If failed, try to get it from AST.172    if (!FD)173      FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());174 175    if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {176      os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';177    } else if (FD) {178      os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';179    } else {180      os << "function call";181    }182  } else if (isa<CXXNewExpr>(S)) {183    os << "Operator 'new'";184  } else {185    assert(isa<ObjCMessageExpr>(S));186    CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(187        cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0});188 189    switch (Call->getMessageKind()) {190    case OCM_Message:191      os << "Method";192      break;193    case OCM_PropertyAccess:194      os << "Property";195      break;196    case OCM_Subscript:197      os << "Subscript";198      break;199    }200  }201 202  std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0});203  auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);204 205  // If index is not found, we assume that the symbol was returned.206  if (!Idx) {207    os << " returns ";208  } else {209    os << " writes ";210  }211 212  if (CurrV.getObjKind() == ObjKind::CF) {213    os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";214  } else if (CurrV.getObjKind() == ObjKind::OS) {215    os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())216       << "' with a ";217  } else if (CurrV.getObjKind() == ObjKind::Generalized) {218    os << "an object of type '" << Sym->getType() << "' with a ";219  } else {220    assert(CurrV.getObjKind() == ObjKind::ObjC);221    QualType T = Sym->getType();222    if (!isa<ObjCObjectPointerType>(T)) {223      os << "an Objective-C object with a ";224    } else {225      const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);226      os << "an instance of " << PT->getPointeeType() << " with a ";227    }228  }229 230  if (CurrV.isOwned()) {231    os << "+1 retain count";232  } else {233    assert(CurrV.isNotOwned());234    os << "+0 retain count";235  }236 237  if (Idx) {238    os << " into an out parameter '";239    const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];240    PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),241                              /*Qualified=*/false);242    os << "'";243 244    QualType RT = (*CE)->getResultType();245    if (!RT.isNull() && !RT->isVoidType()) {246      SVal RV = (*CE)->getReturnValue();247      if (CurrSt->isNull(RV).isConstrainedTrue()) {248        os << " (assuming the call returns zero)";249      } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {250        os << " (assuming the call returns non-zero)";251      }252 253    }254  }255}256 257namespace clang {258namespace ento {259namespace retaincountchecker {260 261class RefCountReportVisitor : public BugReporterVisitor {262protected:263  SymbolRef Sym;264  bool IsReleaseUnowned;265 266public:267  RefCountReportVisitor(SymbolRef S, bool IRU)268      : Sym(S), IsReleaseUnowned(IRU) {}269 270  void Profile(llvm::FoldingSetNodeID &ID) const override {271    static int x = 0;272    ID.AddPointer(&x);273    ID.AddPointer(Sym);274  }275 276  PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,277                                   BugReporterContext &BRC,278                                   PathSensitiveBugReport &BR) override;279 280  PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,281                                    const ExplodedNode *N,282                                    PathSensitiveBugReport &BR) override;283};284 285class RefLeakReportVisitor : public RefCountReportVisitor {286public:287  RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding)288      : RefCountReportVisitor(Sym, /*IsReleaseUnowned=*/false),289        LastBinding(LastBinding) {}290 291  PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,292                                    const ExplodedNode *N,293                                    PathSensitiveBugReport &BR) override;294 295private:296  const MemRegion *LastBinding;297};298 299} // end namespace retaincountchecker300} // end namespace ento301} // end namespace clang302 303 304/// Find the first node with the parent stack frame.305static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {306  const StackFrameContext *SC = Pred->getStackFrame();307  if (SC->inTopFrame())308    return nullptr;309  const StackFrameContext *PC = SC->getParent()->getStackFrame();310  if (!PC)311    return nullptr;312 313  const ExplodedNode *N = Pred;314  while (N && N->getStackFrame() != PC) {315    N = N->getFirstPred();316  }317  return N;318}319 320 321/// Insert a diagnostic piece at function exit322/// if a function parameter is annotated as "os_consumed",323/// but it does not actually consume the reference.324static std::shared_ptr<PathDiagnosticEventPiece>325annotateConsumedSummaryMismatch(const ExplodedNode *N,326                                CallExitBegin &CallExitLoc,327                                const SourceManager &SM,328                                CallEventManager &CEMgr) {329 330  const ExplodedNode *CN = getCalleeNode(N);331  if (!CN)332    return nullptr;333 334  CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());335 336  std::string sbuf;337  llvm::raw_string_ostream os(sbuf);338  ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();339  for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {340    const ParmVarDecl *PVD = Parameters[I];341 342    if (!PVD->hasAttr<OSConsumedAttr>())343      continue;344 345    if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {346      const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);347      const RefVal *CountAtExit = getRefBinding(N->getState(), SR);348 349      if (!CountBeforeCall || !CountAtExit)350        continue;351 352      unsigned CountBefore = CountBeforeCall->getCount();353      unsigned CountAfter = CountAtExit->getCount();354 355      bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;356      if (!AsExpected) {357        os << "Parameter '";358        PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),359                                  /*Qualified=*/false);360        os << "' is marked as consuming, but the function did not consume "361           << "the reference\n";362      }363    }364  }365 366  if (sbuf.empty())367    return nullptr;368 369  PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);370  return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);371}372 373/// Annotate the parameter at the analysis entry point.374static std::shared_ptr<PathDiagnosticEventPiece>375annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,376                       const SourceManager &SM) {377  auto PP = N->getLocationAs<BlockEdge>();378  if (!PP)379    return nullptr;380 381  const CFGBlock *Src = PP->getSrc();382  const RefVal *CurrT = getRefBinding(N->getState(), Sym);383 384  if (&Src->getParent()->getEntry() != Src || !CurrT ||385      getRefBinding(N->getFirstPred()->getState(), Sym))386    return nullptr;387 388  const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());389  const auto *PVD = cast<ParmVarDecl>(VR->getDecl());390  PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);391 392  std::string s;393  llvm::raw_string_ostream os(s);394  os << "Parameter '" << PVD->getDeclName() << "' starts at +";395  if (CurrT->getCount() == 1) {396    os << "1, as it is marked as consuming";397  } else {398    assert(CurrT->getCount() == 0);399    os << "0";400  }401  return std::make_shared<PathDiagnosticEventPiece>(L, s);402}403 404PathDiagnosticPieceRef405RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,406                                 PathSensitiveBugReport &BR) {407  const SourceManager &SM = BRC.getSourceManager();408  CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();409  if (auto CE = N->getLocationAs<CallExitBegin>())410    if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))411      return PD;412 413  if (auto PD = annotateStartParameter(N, Sym, SM))414    return PD;415 416  // FIXME: We will eventually need to handle non-statement-based events417  // (__attribute__((cleanup))).418  if (!N->getLocation().getAs<StmtPoint>())419    return nullptr;420 421  // Check if the type state has changed.422  const ExplodedNode *PrevNode = N->getFirstPred();423  ProgramStateRef PrevSt = PrevNode->getState();424  ProgramStateRef CurrSt = N->getState();425  const LocationContext *LCtx = N->getLocationContext();426 427  const RefVal* CurrT = getRefBinding(CurrSt, Sym);428  if (!CurrT)429    return nullptr;430 431  const RefVal &CurrV = *CurrT;432  const RefVal *PrevT = getRefBinding(PrevSt, Sym);433 434  // Create a string buffer to constain all the useful things we want435  // to tell the user.436  std::string sbuf;437  llvm::raw_string_ostream os(sbuf);438 439  if (PrevT && IsReleaseUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {440    os << "Object is now not exclusively owned";441    auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);442    return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);443  }444 445  // This is the allocation site since the previous node had no bindings446  // for this symbol.447  if (!PrevT) {448    const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();449 450    if (isa<ObjCIvarRefExpr>(S) &&451        isSynthesizedAccessor(LCtx->getStackFrame())) {452      S = LCtx->getStackFrame()->getCallSite();453    }454 455    if (isa<ObjCArrayLiteral>(S)) {456      os << "NSArray literal is an object with a +0 retain count";457    } else if (isa<ObjCDictionaryLiteral>(S)) {458      os << "NSDictionary literal is an object with a +0 retain count";459    } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {460      if (isNumericLiteralExpression(BL->getSubExpr()))461        os << "NSNumber literal is an object with a +0 retain count";462      else {463        const ObjCInterfaceDecl *BoxClass = nullptr;464        if (const ObjCMethodDecl *Method = BL->getBoxingMethod())465          BoxClass = Method->getClassInterface();466 467        // We should always be able to find the boxing class interface,468        // but consider this future-proofing.469        if (BoxClass) {470          os << *BoxClass << " b";471        } else {472          os << "B";473        }474 475        os << "oxed expression produces an object with a +0 retain count";476      }477    } else if (isa<ObjCIvarRefExpr>(S)) {478      os << "Object loaded from instance variable";479    } else {480      generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);481    }482 483    PathDiagnosticLocation Pos(S, SM, N->getLocationContext());484    return std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);485  }486 487  // Gather up the effects that were performed on the object at this488  // program point489  bool DeallocSent = false;490 491  const ProgramPointTag *Tag = N->getLocation().getTag();492 493  if (Tag == &RetainCountChecker::getCastFailTag()) {494    os << "Assuming dynamic cast returns null due to type mismatch";495  }496 497  if (Tag == &RetainCountChecker::getDeallocSentTag()) {498    // We only have summaries attached to nodes after evaluating CallExpr and499    // ObjCMessageExprs.500    const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();501 502    if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {503      // Iterate through the parameter expressions and see if the symbol504      // was ever passed as an argument.505      unsigned i = 0;506 507      for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {508 509        // Retrieve the value of the argument.  Is it the symbol510        // we are interested in?511        if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)512          continue;513 514        // We have an argument.  Get the effect!515        DeallocSent = true;516      }517    } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {518      if (const Expr *receiver = ME->getInstanceReceiver()) {519        if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)520              .getAsLocSymbol() == Sym) {521          // The symbol we are tracking is the receiver.522          DeallocSent = true;523        }524      }525    }526  }527 528  if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))529    return nullptr;530 531  if (sbuf.empty())532    return nullptr; // We have nothing to say!533 534  const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();535  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),536                                N->getLocationContext());537  auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, sbuf);538 539  // Add the range by scanning the children of the statement for any bindings540  // to Sym.541  for (const Stmt *Child : S->children())542    if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))543      if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {544        P->addRange(Exp->getSourceRange());545        break;546      }547 548  return std::move(P);549}550 551static std::optional<std::string> describeRegion(const MemRegion *MR) {552  if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))553    return std::string(VR->getDecl()->getName());554  // Once we support more storage locations for bindings,555  // this would need to be improved.556  return std::nullopt;557}558 559using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>;560 561namespace {562class VarBindingsCollector : public StoreManager::BindingsHandler {563  SymbolRef Sym;564  Bindings &Result;565 566public:567  VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)568      : Sym(Sym), Result(ToFill) {}569 570  bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R,571                     SVal Val) override {572    SymbolRef SymV = Val.getAsLocSymbol();573    if (!SymV || SymV != Sym)574      return true;575 576    if (isa<NonParamVarRegion>(R))577      Result.emplace_back(R, Val);578 579    return true;580  }581};582} // namespace583 584static Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager,585                                           const ExplodedNode *Node,586                                           SymbolRef Sym) {587  Bindings Result;588  VarBindingsCollector Collector{Sym, Result};589  while (Result.empty() && Node) {590    Manager.iterBindings(Node->getState(), Collector);591    Node = Node->getFirstPred();592  }593 594  return Result;595}596 597namespace {598// Find the first node in the current function context that referred to the599// tracked symbol and the memory location that value was stored to. Note, the600// value is only reported if the allocation occurred in the same function as601// the leak. The function can also return a location context, which should be602// treated as interesting.603struct AllocationInfo {604  const ExplodedNode* N;605  const MemRegion *R;606  const LocationContext *InterestingMethodContext;607  AllocationInfo(const ExplodedNode *InN,608                 const MemRegion *InR,609                 const LocationContext *InInterestingMethodContext) :610    N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}611};612} // end anonymous namespace613 614static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,615                                        const ExplodedNode *N, SymbolRef Sym) {616  const ExplodedNode *AllocationNode = N;617  const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;618  const MemRegion *FirstBinding = nullptr;619  const LocationContext *LeakContext = N->getLocationContext();620 621  // The location context of the init method called on the leaked object, if622  // available.623  const LocationContext *InitMethodContext = nullptr;624 625  while (N) {626    ProgramStateRef St = N->getState();627    const LocationContext *NContext = N->getLocationContext();628 629    if (!getRefBinding(St, Sym))630      break;631 632    StoreManager::FindUniqueBinding FB(Sym);633    StateMgr.iterBindings(St, FB);634 635    if (FB) {636      const MemRegion *R = FB.getRegion();637      // Do not show local variables belonging to a function other than638      // where the error is reported.639      if (const auto *MR = R->getMemorySpaceAs<StackSpaceRegion>(St))640        if (MR->getStackFrame() == LeakContext->getStackFrame())641          FirstBinding = R;642    }643 644    // AllocationNode is the last node in which the symbol was tracked.645    AllocationNode = N;646 647    // AllocationNodeInCurrentContext, is the last node in the current or648    // parent context in which the symbol was tracked.649    //650    // Note that the allocation site might be in the parent context. For example,651    // the case where an allocation happens in a block that captures a reference652    // to it and that reference is overwritten/dropped by another call to653    // the block.654    if (NContext == LeakContext || NContext->isParentOf(LeakContext))655      AllocationNodeInCurrentOrParentContext = N;656 657    // Find the last init that was called on the given symbol and store the658    // init method's location context.659    if (!InitMethodContext)660      if (auto CEP = N->getLocation().getAs<CallEnter>()) {661        const Stmt *CE = CEP->getCallExpr();662        if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {663          const Stmt *RecExpr = ME->getInstanceReceiver();664          if (RecExpr) {665            SVal RecV = St->getSVal(RecExpr, NContext);666            if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)667              InitMethodContext = CEP->getCalleeContext();668          }669        }670      }671 672    N = N->getFirstPred();673  }674 675  // If we are reporting a leak of the object that was allocated with alloc,676  // mark its init method as interesting.677  const LocationContext *InterestingMethodContext = nullptr;678  if (InitMethodContext) {679    const ProgramPoint AllocPP = AllocationNode->getLocation();680    if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())681      if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())682        if (ME->getMethodFamily() == OMF_alloc)683          InterestingMethodContext = InitMethodContext;684  }685 686  // If allocation happened in a function different from the leak node context,687  // do not report the binding.688  assert(N && "Could not find allocation node");689 690  if (AllocationNodeInCurrentOrParentContext &&691      AllocationNodeInCurrentOrParentContext->getLocationContext() !=692      LeakContext)693    FirstBinding = nullptr;694 695  return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,696                        InterestingMethodContext);697}698 699PathDiagnosticPieceRef700RefCountReportVisitor::getEndPath(BugReporterContext &BRC,701                                  const ExplodedNode *EndN,702                                  PathSensitiveBugReport &BR) {703  BR.markInteresting(Sym);704  return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);705}706 707PathDiagnosticPieceRef708RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,709                                 const ExplodedNode *EndN,710                                 PathSensitiveBugReport &BR) {711 712  // Tell the BugReporterContext to report cases when the tracked symbol is713  // assigned to different variables, etc.714  BR.markInteresting(Sym);715 716  PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();717 718  std::string sbuf;719  llvm::raw_string_ostream os(sbuf);720 721  os << "Object leaked: ";722 723  std::optional<std::string> RegionDescription = describeRegion(LastBinding);724  if (RegionDescription) {725    os << "object allocated and stored into '" << *RegionDescription << '\'';726  } else {727    os << "allocated object of type '" << getPrettyTypeName(Sym->getType())728       << "'";729  }730 731  // Get the retain count.732  const RefVal *RV = getRefBinding(EndN->getState(), Sym);733  assert(RV);734 735  if (RV->getKind() == RefVal::ErrorLeakReturned) {736    const Decl *D = &EndN->getCodeDecl();737 738    os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "739                                  : " is returned from a function ");740 741    if (D->hasAttr<CFReturnsNotRetainedAttr>()) {742      os << "that is annotated as CF_RETURNS_NOT_RETAINED";743    } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {744      os << "that is annotated as NS_RETURNS_NOT_RETAINED";745    } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {746      os << "that is annotated as OS_RETURNS_NOT_RETAINED";747    } else {748      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {749        if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {750          os << "managed by Automatic Reference Counting";751        } else {752          os << "whose name ('" << MD->getSelector().getAsString()753             << "') does not start with "754                "'copy', 'mutableCopy', 'alloc' or 'new'."755                "  This violates the naming convention rules"756                " given in the Memory Management Guide for Cocoa";757        }758      } else {759        const FunctionDecl *FD = cast<FunctionDecl>(D);760        ObjKind K = RV->getObjKind();761        if (K == ObjKind::ObjC || K == ObjKind::CF) {762          os << "whose name ('" << *FD763             << "') does not contain 'Copy' or 'Create'.  This violates the "764                "naming convention rules given in the Memory Management Guide "765                "for Core Foundation";766        } else if (RV->getObjKind() == ObjKind::OS) {767          std::string FuncName = FD->getNameAsString();768          os << "whose name ('" << FuncName << "') starts with '"769             << StringRef(FuncName).substr(0, 3) << "'";770        }771      }772    }773  } else {774    os << " is not referenced later in this execution path and has a retain "775          "count of +"776       << RV->getCount();777  }778 779  return std::make_shared<PathDiagnosticEventPiece>(L, sbuf);780}781 782RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,783                               ExplodedNode *n, SymbolRef sym, bool isLeak,784                               bool IsReleaseUnowned)785    : PathSensitiveBugReport(D, D.getReportMessage(), n), Sym(sym),786      isLeak(isLeak) {787  if (!isLeak)788    addVisitor<RefCountReportVisitor>(sym, IsReleaseUnowned);789}790 791RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,792                               ExplodedNode *n, SymbolRef sym,793                               StringRef endText)794    : PathSensitiveBugReport(D, D.getReportMessage(), endText, n) {795 796  addVisitor<RefCountReportVisitor>(sym, /*IsReleaseUnowned=*/false);797}798 799void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) {800  const SourceManager &SMgr = Ctx.getSourceManager();801 802  if (!Sym->getOriginRegion())803    return;804 805  auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion());806  if (Region) {807    const Decl *PDecl = Region->getDecl();808    if (isa_and_nonnull<ParmVarDecl>(PDecl)) {809      PathDiagnosticLocation ParamLocation =810          PathDiagnosticLocation::create(PDecl, SMgr);811      Location = ParamLocation;812      UniqueingLocation = ParamLocation;813      UniqueingDecl = Ctx.getLocationContext()->getDecl();814    }815  }816}817 818void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) {819  // Most bug reports are cached at the location where they occurred.820  // With leaks, we want to unique them by the location where they were821  // allocated, and only report a single path.  To do this, we need to find822  // the allocation site of a piece of tracked memory, which we do via a823  // call to GetAllocationSite.  This will walk the ExplodedGraph backwards.824  // Note that this is *not* the trimmed graph; we are guaranteed, however,825  // that all ancestor nodes that represent the allocation site have the826  // same SourceLocation.827  const ExplodedNode *AllocNode = nullptr;828 829  const SourceManager &SMgr = Ctx.getSourceManager();830 831  AllocationInfo AllocI =832      GetAllocationSite(Ctx.getStateManager(), getErrorNode(), Sym);833 834  AllocNode = AllocI.N;835  AllocFirstBinding = AllocI.R;836  markInteresting(AllocI.InterestingMethodContext);837 838  // Get the SourceLocation for the allocation site.839  // FIXME: This will crash the analyzer if an allocation comes from an840  // implicit call (ex: a destructor call).841  // (Currently there are no such allocations in Cocoa, though.)842  AllocStmt = AllocNode->getStmtForDiagnostics();843 844  if (!AllocStmt) {845    AllocFirstBinding = nullptr;846    return;847  }848 849  PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(850      AllocStmt, SMgr, AllocNode->getLocationContext());851  Location = AllocLocation;852 853  // Set uniqieing info, which will be used for unique the bug reports. The854  // leaks should be uniqued on the allocation site.855  UniqueingLocation = AllocLocation;856  UniqueingDecl = AllocNode->getLocationContext()->getDecl();857}858 859void RefLeakReport::createDescription(CheckerContext &Ctx) {860  assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());861  Description.clear();862  llvm::raw_string_ostream os(Description);863  os << "Potential leak of an object";864 865  std::optional<std::string> RegionDescription =866      describeRegion(AllocBindingToReport);867  if (RegionDescription) {868    os << " stored into '" << *RegionDescription << '\'';869  } else {870 871    // If we can't figure out the name, just supply the type information.872    os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";873  }874}875 876void RefLeakReport::findBindingToReport(CheckerContext &Ctx,877                                        ExplodedNode *Node) {878  if (!AllocFirstBinding)879    // If we don't have any bindings, we won't be able to find any880    // better binding to report.881    return;882 883  // If the original region still contains the leaking symbol...884  if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) {885    // ...it is the best binding to report.886    AllocBindingToReport = AllocFirstBinding;887    return;888  }889 890  // At this point, we know that the original region doesn't contain the leaking891  // when the actual leak happens.  It means that it can be confusing for the892  // user to see such description in the message.893  //894  // Let's consider the following example:895  //   Object *Original = allocate(...);896  //   Object *New = Original;897  //   Original = allocate(...);898  //   Original->release();899  //900  // Complaining about a leaking object "stored into Original" might cause a901  // rightful confusion because 'Original' is actually released.902  // We should complain about 'New' instead.903  Bindings AllVarBindings =904      getAllVarBindingsForSymbol(Ctx.getStateManager(), Node, Sym);905 906  // While looking for the last var bindings, we can still find907  // `AllocFirstBinding` to be one of them.  In situations like this,908  // it would still be the easiest case to explain to our users.909  if (!AllVarBindings.empty() &&910      !llvm::is_contained(llvm::make_first_range(AllVarBindings),911                          AllocFirstBinding)) {912    // Let's pick one of them at random (if there is something to pick from).913    AllocBindingToReport = AllVarBindings[0].first;914 915    // Because 'AllocBindingToReport' is not the same as916    // 'AllocFirstBinding', we need to explain how the leaking object917    // got from one to another.918    //919    // NOTE: We use the actual SVal stored in AllocBindingToReport here because920    //       trackStoredValue compares SVal's and it can get trickier for921    //       something like derived regions if we want to construct SVal from922    //       Sym. Instead, we take the value that is definitely stored in that923    //       region, thus guaranteeing that trackStoredValue will work.924    bugreporter::trackStoredValue(AllVarBindings[0].second,925                                  AllocBindingToReport, *this);926  } else {927    AllocBindingToReport = AllocFirstBinding;928  }929}930 931RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,932                             ExplodedNode *N, SymbolRef Sym,933                             CheckerContext &Ctx)934    : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) {935 936  deriveAllocLocation(Ctx);937  findBindingToReport(Ctx, N);938 939  if (!AllocFirstBinding)940    deriveParamLocation(Ctx);941 942  createDescription(Ctx);943 944  addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport);945}946