std::shared_ptr<PathDiagnosticPiece> RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *EndN, BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. BR.markInteresting(Sym); // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); const MemRegion* FirstBinding = AllocI.R; BR.markInteresting(AllocI.InterestingMethodContext); SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't // occur at an actual statement (e.g., transition between blocks; end // of function) so we need to walk the graph and compute a real location. const ExplodedNode *LeakN = EndN; PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "Object leaked: "; Optional<std::string> RegionDescription = describeRegion(FirstBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) << "'"; } // Get the retain count. const RefVal* RV = getRefBinding(EndN->getState(), Sym); assert(RV); if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF // objects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " : " is returned from a function "); if (D->hasAttr<CFReturnsNotRetainedAttr>()) { os << "that is annotated as CF_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { os << "that is annotated as NS_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { os << "that is annotated as OS_RETURNS_NOT_RETAINED"; } else { if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { os << "managed by Automatic Reference Counting"; } else { os << "whose name ('" << MD->getSelector().getAsString() << "') does not start with " "'copy', 'mutableCopy', 'alloc' or 'new'." " This violates the naming convention rules" " given in the Memory Management Guide for Cocoa"; } } else { const FunctionDecl *FD = cast<FunctionDecl>(D); os << "whose name ('" << *FD << "') does not contain 'Copy' or 'Create'. This violates the naming" " convention rules given in the Memory Management Guide for Core" " Foundation"; } } } else { os << " is not referenced later in this execution path and has a retain " "count of +" << RV->getCount(); } return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); }