blob: f67fb7eea7cb9d587b210834a69804fc76329437 [file] [log] [blame]
George Karpenkov70c2ee32018-08-17 21:41:07 +00001// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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-exception
George Karpenkov70c2ee32018-08-17 21:41:07 +00006//
7//===----------------------------------------------------------------------===//
8//
9// This file defines diagnostics for RetainCountChecker, which implements
10// 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
17using namespace clang;
18using namespace ento;
19using namespace retaincountchecker;
20
George Karpenkov2c2d0b62019-01-18 19:24:55 +000021StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
22 switch (BT) {
23 case UseAfterRelease:
24 return "Use-after-release";
25 case ReleaseNotOwned:
26 return "Bad release";
27 case DeallocNotOwned:
28 return "-dealloc sent to non-exclusively owned object";
29 case FreeNotOwned:
30 return "freeing non-exclusively owned object";
31 case OverAutorelease:
32 return "Object autoreleased too many times";
33 case ReturnNotOwnedForOwned:
34 return "Method should return an owned object";
35 case LeakWithinFunction:
36 return "Leak";
37 case LeakAtReturn:
38 return "Leak of returned object";
39 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000040 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000041}
42
43StringRef RefCountBug::getDescription() const {
44 switch (BT) {
45 case UseAfterRelease:
46 return "Reference-counted object is used after it is released";
47 case ReleaseNotOwned:
48 return "Incorrect decrement of the reference count of an object that is "
49 "not owned at this point by the caller";
50 case DeallocNotOwned:
51 return "-dealloc sent to object that may be referenced elsewhere";
52 case FreeNotOwned:
53 return "'free' called on an object that may be referenced elsewhere";
54 case OverAutorelease:
55 return "Object autoreleased too many times";
56 case ReturnNotOwnedForOwned:
57 return "Object with a +0 retain count returned to caller where a +1 "
58 "(owning) retain count is expected";
59 case LeakWithinFunction:
60 case LeakAtReturn:
61 return "";
62 }
Simon Pilgrimc6368062019-01-18 20:40:35 +000063 llvm_unreachable("Unknown RefCountBugType");
George Karpenkov2c2d0b62019-01-18 19:24:55 +000064}
65
66RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
67 : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
Rui Ueyama49a3ad22019-07-16 04:46:31 +000068 /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
George Karpenkova9e29562019-01-22 19:51:00 +000069 BT(BT), Checker(Checker) {}
George Karpenkov2c2d0b62019-01-18 19:24:55 +000070
George Karpenkov70c2ee32018-08-17 21:41:07 +000071static bool isNumericLiteralExpression(const Expr *E) {
72 // FIXME: This set of cases was copied from SemaExprObjC.
73 return isa<IntegerLiteral>(E) ||
74 isa<CharacterLiteral>(E) ||
75 isa<FloatingLiteral>(E) ||
76 isa<ObjCBoolLiteralExpr>(E) ||
77 isa<CXXBoolLiteralExpr>(E);
78}
79
George Karpenkovf893ea12018-11-30 02:17:44 +000080/// If type represents a pointer to CXXRecordDecl,
81/// and is not a typedef, return the decl name.
82/// Otherwise, return the serialization of type.
Haojian Wuceff7302018-11-30 09:23:01 +000083static std::string getPrettyTypeName(QualType QT) {
George Karpenkovf893ea12018-11-30 02:17:44 +000084 QualType PT = QT->getPointeeType();
85 if (!PT.isNull() && !QT->getAs<TypedefType>())
86 if (const auto *RD = PT->getAsCXXRecordDecl())
87 return RD->getName();
88 return QT.getAsString();
89}
90
George Karpenkovb3303d72018-11-30 02:17:05 +000091/// Write information about the type state change to {@code os},
92/// return whether the note should be generated.
93static bool shouldGenerateNote(llvm::raw_string_ostream &os,
George Karpenkov2c2d0b62019-01-18 19:24:55 +000094 const RefVal *PrevT,
95 const RefVal &CurrV,
George Karpenkov717c4c02019-01-10 18:15:17 +000096 bool DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +000097 // Get the previous type state.
98 RefVal PrevV = *PrevT;
99
100 // Specially handle -dealloc.
George Karpenkov717c4c02019-01-10 18:15:17 +0000101 if (DeallocSent) {
George Karpenkovb3303d72018-11-30 02:17:05 +0000102 // Determine if the object's reference count was pushed to zero.
103 assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
104 // We may not have transitioned to 'release' if we hit an error.
105 // This case is handled elsewhere.
106 if (CurrV.getKind() == RefVal::Released) {
107 assert(CurrV.getCombinedCounts() == 0);
108 os << "Object released by directly sending the '-dealloc' message";
109 return true;
110 }
111 }
112
113 // Determine if the typestate has changed.
114 if (!PrevV.hasSameState(CurrV))
115 switch (CurrV.getKind()) {
116 case RefVal::Owned:
117 case RefVal::NotOwned:
118 if (PrevV.getCount() == CurrV.getCount()) {
119 // Did an autorelease message get sent?
120 if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
121 return false;
122
123 assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
124 os << "Object autoreleased";
125 return true;
126 }
127
128 if (PrevV.getCount() > CurrV.getCount())
129 os << "Reference count decremented.";
130 else
131 os << "Reference count incremented.";
132
133 if (unsigned Count = CurrV.getCount())
134 os << " The object now has a +" << Count << " retain count.";
135
136 return true;
137
138 case RefVal::Released:
139 if (CurrV.getIvarAccessHistory() ==
140 RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
141 CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
142 os << "Strong instance variable relinquished. ";
143 }
144 os << "Object released.";
145 return true;
146
147 case RefVal::ReturnedOwned:
148 // Autoreleases can be applied after marking a node ReturnedOwned.
149 if (CurrV.getAutoreleaseCount())
150 return false;
151
152 os << "Object returned to caller as an owning reference (single "
153 "retain count transferred to caller)";
154 return true;
155
156 case RefVal::ReturnedNotOwned:
157 os << "Object returned to caller with a +0 retain count";
158 return true;
159
160 default:
161 return false;
162 }
163 return true;
164}
165
George Karpenkov5be959c2019-01-11 23:35:17 +0000166/// Finds argument index of the out paramter in the call {@code S}
167/// corresponding to the symbol {@code Sym}.
168/// If none found, returns None.
169static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
170 const LocationContext *LCtx,
171 SymbolRef &Sym,
172 Optional<CallEventRef<>> CE) {
173 if (!CE)
174 return None;
175
176 for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
177 if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
178 if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
179 if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
180 return Idx;
181
182 return None;
183}
184
Benjamin Kramer756ecb82019-02-11 14:52:15 +0000185static Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000186 if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
187 if (ME->getMemberDecl()->getNameAsString() != "alloc")
188 return None;
189 const Expr *This = ME->getBase()->IgnoreParenImpCasts();
190 if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
191 const ValueDecl *VD = DRE->getDecl();
192 if (VD->getNameAsString() != "metaClass")
193 return None;
194
195 if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
196 return RD->getNameAsString();
197
198 }
199 }
200 return None;
201}
202
Benjamin Kramer756ecb82019-02-11 14:52:15 +0000203static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000204 if (const auto *CE = dyn_cast<CallExpr>(S))
205 if (auto Out = findMetaClassAlloc(CE->getCallee()))
206 return *Out;
207 return getPrettyTypeName(QT);
208}
209
George Karpenkovff014862018-12-11 01:13:40 +0000210static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
211 const LocationContext *LCtx,
212 const RefVal &CurrV, SymbolRef &Sym,
213 const Stmt *S,
214 llvm::raw_string_ostream &os) {
George Karpenkov5be959c2019-01-11 23:35:17 +0000215 CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
George Karpenkov62db8862018-11-30 02:18:23 +0000216 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
217 // Get the name of the callee (if it is available)
218 // from the tracked SVal.
219 SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
220 const FunctionDecl *FD = X.getAsFunctionDecl();
221
222 // If failed, try to get it from AST.
223 if (!FD)
224 FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
225
226 if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
227 os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
228 } else if (FD) {
229 os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
230 } else {
231 os << "function call";
232 }
George Karpenkovff014862018-12-11 01:13:40 +0000233 } else if (isa<CXXNewExpr>(S)) {
234 os << "Operator 'new'";
George Karpenkov62db8862018-11-30 02:18:23 +0000235 } else {
236 assert(isa<ObjCMessageExpr>(S));
George Karpenkov62db8862018-11-30 02:18:23 +0000237 CallEventRef<ObjCMethodCall> Call =
238 Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
239
240 switch (Call->getMessageKind()) {
241 case OCM_Message:
242 os << "Method";
243 break;
244 case OCM_PropertyAccess:
245 os << "Property";
246 break;
247 case OCM_Subscript:
248 os << "Subscript";
249 break;
250 }
251 }
252
George Karpenkov5be959c2019-01-11 23:35:17 +0000253 Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
254 auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
255
256 // If index is not found, we assume that the symbol was returned.
257 if (!Idx) {
258 os << " returns ";
259 } else {
260 os << " writes ";
261 }
George Karpenkov3c1e0662019-01-10 18:28:10 +0000262
George Karpenkov7e3016d2019-01-10 18:13:46 +0000263 if (CurrV.getObjKind() == ObjKind::CF) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000264 os << "a Core Foundation object of type '"
George Karpenkov4f64b382019-01-10 18:15:57 +0000265 << Sym->getType().getAsString() << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000266 } else if (CurrV.getObjKind() == ObjKind::OS) {
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000267 os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
George Karpenkov4f64b382019-01-10 18:15:57 +0000268 << "' with a ";
George Karpenkov7e3016d2019-01-10 18:13:46 +0000269 } else if (CurrV.getObjKind() == ObjKind::Generalized) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000270 os << "an object of type '" << Sym->getType().getAsString()
George Karpenkov4f64b382019-01-10 18:15:57 +0000271 << "' with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000272 } else {
George Karpenkov7e3016d2019-01-10 18:13:46 +0000273 assert(CurrV.getObjKind() == ObjKind::ObjC);
George Karpenkov62db8862018-11-30 02:18:23 +0000274 QualType T = Sym->getType();
275 if (!isa<ObjCObjectPointerType>(T)) {
George Karpenkov3c1e0662019-01-10 18:28:10 +0000276 os << "an Objective-C object with a ";
George Karpenkov62db8862018-11-30 02:18:23 +0000277 } else {
278 const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
George Karpenkov3c1e0662019-01-10 18:28:10 +0000279 os << "an instance of " << PT->getPointeeType().getAsString()
George Karpenkov62db8862018-11-30 02:18:23 +0000280 << " with a ";
281 }
282 }
283
284 if (CurrV.isOwned()) {
285 os << "+1 retain count";
286 } else {
287 assert(CurrV.isNotOwned());
288 os << "+0 retain count";
289 }
George Karpenkov5be959c2019-01-11 23:35:17 +0000290
291 if (Idx) {
292 os << " into an out parameter '";
293 const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
294 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
295 /*Qualified=*/false);
296 os << "'";
297
298 QualType RT = (*CE)->getResultType();
299 if (!RT.isNull() && !RT->isVoidType()) {
300 SVal RV = (*CE)->getReturnValue();
301 if (CurrSt->isNull(RV).isConstrainedTrue()) {
302 os << " (assuming the call returns zero)";
303 } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
304 os << " (assuming the call returns non-zero)";
305 }
306
307 }
308 }
George Karpenkov62db8862018-11-30 02:18:23 +0000309}
310
311namespace clang {
312namespace ento {
313namespace retaincountchecker {
314
George Karpenkov0bb17c42019-01-10 18:16:25 +0000315class RefCountReportVisitor : public BugReporterVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000316protected:
317 SymbolRef Sym;
George Karpenkov62db8862018-11-30 02:18:23 +0000318
319public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000320 RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000321
322 void Profile(llvm::FoldingSetNodeID &ID) const override {
323 static int x = 0;
324 ID.AddPointer(&x);
325 ID.AddPointer(Sym);
326 }
327
Kristof Umann6d716ef2019-08-13 16:45:48 +0000328 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
329 BugReporterContext &BRC,
330 BugReport &BR) override;
George Karpenkov62db8862018-11-30 02:18:23 +0000331
Kristof Umann6d716ef2019-08-13 16:45:48 +0000332 PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
333 const ExplodedNode *N,
334 BugReport &BR) override;
George Karpenkov62db8862018-11-30 02:18:23 +0000335};
336
George Karpenkov0bb17c42019-01-10 18:16:25 +0000337class RefLeakReportVisitor : public RefCountReportVisitor {
George Karpenkov62db8862018-11-30 02:18:23 +0000338public:
George Karpenkov0bb17c42019-01-10 18:16:25 +0000339 RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
George Karpenkov62db8862018-11-30 02:18:23 +0000340
Kristof Umann6d716ef2019-08-13 16:45:48 +0000341 PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
342 const ExplodedNode *N,
343 BugReport &BR) override;
George Karpenkov62db8862018-11-30 02:18:23 +0000344};
345
346} // end namespace retaincountchecker
347} // end namespace ento
348} // end namespace clang
349
George Karpenkovff014862018-12-11 01:13:40 +0000350
351/// Find the first node with the parent stack frame.
352static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
353 const StackFrameContext *SC = Pred->getStackFrame();
354 if (SC->inTopFrame())
355 return nullptr;
356 const StackFrameContext *PC = SC->getParent()->getStackFrame();
357 if (!PC)
358 return nullptr;
359
360 const ExplodedNode *N = Pred;
361 while (N && N->getStackFrame() != PC) {
362 N = N->getFirstPred();
363 }
364 return N;
365}
366
367
368/// Insert a diagnostic piece at function exit
369/// if a function parameter is annotated as "os_consumed",
370/// but it does not actually consume the reference.
371static std::shared_ptr<PathDiagnosticEventPiece>
372annotateConsumedSummaryMismatch(const ExplodedNode *N,
373 CallExitBegin &CallExitLoc,
374 const SourceManager &SM,
375 CallEventManager &CEMgr) {
376
377 const ExplodedNode *CN = getCalleeNode(N);
378 if (!CN)
379 return nullptr;
380
381 CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
382
383 std::string sbuf;
384 llvm::raw_string_ostream os(sbuf);
385 ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
386 for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
387 const ParmVarDecl *PVD = Parameters[I];
388
389 if (!PVD->hasAttr<OSConsumedAttr>())
George Karpenkovf5085322018-12-21 02:16:23 +0000390 continue;
George Karpenkovff014862018-12-11 01:13:40 +0000391
392 if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
393 const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
394 const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
395
396 if (!CountBeforeCall || !CountAtExit)
397 continue;
398
399 unsigned CountBefore = CountBeforeCall->getCount();
400 unsigned CountAfter = CountAtExit->getCount();
401
402 bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
403 if (!AsExpected) {
404 os << "Parameter '";
405 PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
406 /*Qualified=*/false);
George Karpenkov79f03402018-12-21 19:13:28 +0000407 os << "' is marked as consuming, but the function did not consume "
George Karpenkovff014862018-12-11 01:13:40 +0000408 << "the reference\n";
409 }
410 }
411 }
412
413 if (os.str().empty())
414 return nullptr;
415
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000416 PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
George Karpenkovff014862018-12-11 01:13:40 +0000417 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
418}
419
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000420/// Annotate the parameter at the analysis entry point.
421static std::shared_ptr<PathDiagnosticEventPiece>
422annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
423 const SourceManager &SM) {
424 auto PP = N->getLocationAs<BlockEdge>();
425 if (!PP)
426 return nullptr;
427
428 const CFGBlock *Src = PP->getSrc();
429 const RefVal *CurrT = getRefBinding(N->getState(), Sym);
430
431 if (&Src->getParent()->getEntry() != Src || !CurrT ||
432 getRefBinding(N->getFirstPred()->getState(), Sym))
433 return nullptr;
434
435 const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
436 const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
437 PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);
438
439 std::string s;
440 llvm::raw_string_ostream os(s);
George Karpenkov77eae6d2019-01-30 02:11:04 +0000441 os << "Parameter '" << PVD->getNameAsString() << "' starts at +";
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000442 if (CurrT->getCount() == 1) {
443 os << "1, as it is marked as consuming";
444 } else {
445 assert(CurrT->getCount() == 0);
446 os << "0";
447 }
448 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
449}
450
Kristof Umann6d716ef2019-08-13 16:45:48 +0000451PathDiagnosticPieceRef RefCountReportVisitor::VisitNode(const ExplodedNode *N,
452 BugReporterContext &BRC,
453 BugReport &BR) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000454
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000455 const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
George Karpenkova9e29562019-01-22 19:51:00 +0000456 const auto *Checker =
457 static_cast<const RetainCountChecker *>(BT.getChecker());
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000458
459 bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
460 BT.getBugType() == RefCountBug::DeallocNotOwned;
461
George Karpenkovff014862018-12-11 01:13:40 +0000462 const SourceManager &SM = BRC.getSourceManager();
463 CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
George Karpenkovf5085322018-12-21 02:16:23 +0000464 if (auto CE = N->getLocationAs<CallExitBegin>())
George Karpenkovff014862018-12-11 01:13:40 +0000465 if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
466 return PD;
George Karpenkovff014862018-12-11 01:13:40 +0000467
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000468 if (auto PD = annotateStartParameter(N, Sym, SM))
469 return PD;
470
George Karpenkov70c2ee32018-08-17 21:41:07 +0000471 // FIXME: We will eventually need to handle non-statement-based events
472 // (__attribute__((cleanup))).
473 if (!N->getLocation().getAs<StmtPoint>())
474 return nullptr;
475
476 // Check if the type state has changed.
George Karpenkov62db8862018-11-30 02:18:23 +0000477 const ExplodedNode *PrevNode = N->getFirstPred();
478 ProgramStateRef PrevSt = PrevNode->getState();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000479 ProgramStateRef CurrSt = N->getState();
480 const LocationContext *LCtx = N->getLocationContext();
481
482 const RefVal* CurrT = getRefBinding(CurrSt, Sym);
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000483 if (!CurrT)
484 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000485
486 const RefVal &CurrV = *CurrT;
487 const RefVal *PrevT = getRefBinding(PrevSt, Sym);
488
489 // Create a string buffer to constain all the useful things we want
490 // to tell the user.
491 std::string sbuf;
492 llvm::raw_string_ostream os(sbuf);
493
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000494 if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
495 os << "Object is now not exclusively owned";
496 auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
497 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
498 }
499
George Karpenkov70c2ee32018-08-17 21:41:07 +0000500 // This is the allocation site since the previous node had no bindings
501 // for this symbol.
502 if (!PrevT) {
503 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
504
505 if (isa<ObjCIvarRefExpr>(S) &&
506 isSynthesizedAccessor(LCtx->getStackFrame())) {
507 S = LCtx->getStackFrame()->getCallSite();
508 }
509
510 if (isa<ObjCArrayLiteral>(S)) {
511 os << "NSArray literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000512 } else if (isa<ObjCDictionaryLiteral>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000513 os << "NSDictionary literal is an object with a +0 retain count";
George Karpenkovb3303d72018-11-30 02:17:05 +0000514 } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000515 if (isNumericLiteralExpression(BL->getSubExpr()))
516 os << "NSNumber literal is an object with a +0 retain count";
517 else {
518 const ObjCInterfaceDecl *BoxClass = nullptr;
519 if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
520 BoxClass = Method->getClassInterface();
521
522 // We should always be able to find the boxing class interface,
523 // but consider this future-proofing.
George Karpenkovb3303d72018-11-30 02:17:05 +0000524 if (BoxClass) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000525 os << *BoxClass << " b";
George Karpenkovb3303d72018-11-30 02:17:05 +0000526 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000527 os << "B";
George Karpenkovb3303d72018-11-30 02:17:05 +0000528 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000529
530 os << "oxed expression produces an object with a +0 retain count";
531 }
George Karpenkovb3303d72018-11-30 02:17:05 +0000532 } else if (isa<ObjCIvarRefExpr>(S)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000533 os << "Object loaded from instance variable";
George Karpenkovb3303d72018-11-30 02:17:05 +0000534 } else {
George Karpenkov62db8862018-11-30 02:18:23 +0000535 generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000536 }
537
George Karpenkovff014862018-12-11 01:13:40 +0000538 PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
George Karpenkov70c2ee32018-08-17 21:41:07 +0000539 return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
540 }
541
542 // Gather up the effects that were performed on the object at this
543 // program point
George Karpenkov717c4c02019-01-10 18:15:17 +0000544 bool DeallocSent = false;
545
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000546 const ProgramPointTag *Tag = N->getLocation().getTag();
George Karpenkova9e29562019-01-22 19:51:00 +0000547
548 if (Tag == &Checker->getCastFailTag()) {
549 os << "Assuming dynamic cast returns null due to type mismatch";
550 }
551
552 if (Tag == &Checker->getDeallocSentTag()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000553 // We only have summaries attached to nodes after evaluating CallExpr and
554 // ObjCMessageExprs.
555 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
556
557 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
558 // Iterate through the parameter expressions and see if the symbol
559 // was ever passed as an argument.
560 unsigned i = 0;
561
George Karpenkovb3303d72018-11-30 02:17:05 +0000562 for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000563
564 // Retrieve the value of the argument. Is it the symbol
565 // we are interested in?
566 if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
567 continue;
568
569 // We have an argument. Get the effect!
George Karpenkov717c4c02019-01-10 18:15:17 +0000570 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000571 }
572 } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
573 if (const Expr *receiver = ME->getInstanceReceiver()) {
574 if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
575 .getAsLocSymbol() == Sym) {
576 // The symbol we are tracking is the receiver.
George Karpenkov717c4c02019-01-10 18:15:17 +0000577 DeallocSent = true;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000578 }
579 }
580 }
581 }
582
George Karpenkov717c4c02019-01-10 18:15:17 +0000583 if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
George Karpenkovb3303d72018-11-30 02:17:05 +0000584 return nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000585
586 if (os.str().empty())
587 return nullptr; // We have nothing to say!
588
589 const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
590 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
591 N->getLocationContext());
592 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
593
594 // Add the range by scanning the children of the statement for any bindings
595 // to Sym.
596 for (const Stmt *Child : S->children())
597 if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
598 if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
599 P->addRange(Exp->getSourceRange());
600 break;
601 }
602
603 return std::move(P);
604}
605
606static Optional<std::string> describeRegion(const MemRegion *MR) {
607 if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
608 return std::string(VR->getDecl()->getName());
609 // Once we support more storage locations for bindings,
610 // this would need to be improved.
611 return None;
612}
613
614namespace {
615// Find the first node in the current function context that referred to the
616// tracked symbol and the memory location that value was stored to. Note, the
617// value is only reported if the allocation occurred in the same function as
618// the leak. The function can also return a location context, which should be
619// treated as interesting.
620struct AllocationInfo {
621 const ExplodedNode* N;
622 const MemRegion *R;
623 const LocationContext *InterestingMethodContext;
624 AllocationInfo(const ExplodedNode *InN,
625 const MemRegion *InR,
626 const LocationContext *InInterestingMethodContext) :
627 N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
628};
629} // end anonymous namespace
630
George Karpenkova1c3bb82018-11-30 02:17:31 +0000631static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
632 const ExplodedNode *N, SymbolRef Sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000633 const ExplodedNode *AllocationNode = N;
634 const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
635 const MemRegion *FirstBinding = nullptr;
636 const LocationContext *LeakContext = N->getLocationContext();
637
638 // The location context of the init method called on the leaked object, if
639 // available.
640 const LocationContext *InitMethodContext = nullptr;
641
642 while (N) {
643 ProgramStateRef St = N->getState();
644 const LocationContext *NContext = N->getLocationContext();
645
646 if (!getRefBinding(St, Sym))
647 break;
648
649 StoreManager::FindUniqueBinding FB(Sym);
650 StateMgr.iterBindings(St, FB);
651
652 if (FB) {
653 const MemRegion *R = FB.getRegion();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000654 // Do not show local variables belonging to a function other than
655 // where the error is reported.
George Karpenkov79ed11c2018-12-11 01:13:20 +0000656 if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
657 if (MR->getStackFrame() == LeakContext->getStackFrame())
658 FirstBinding = R;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000659 }
660
661 // AllocationNode is the last node in which the symbol was tracked.
662 AllocationNode = N;
663
664 // AllocationNodeInCurrentContext, is the last node in the current or
665 // parent context in which the symbol was tracked.
666 //
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000667 // Note that the allocation site might be in the parent context. For example,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000668 // the case where an allocation happens in a block that captures a reference
669 // to it and that reference is overwritten/dropped by another call to
670 // the block.
671 if (NContext == LeakContext || NContext->isParentOf(LeakContext))
672 AllocationNodeInCurrentOrParentContext = N;
673
674 // Find the last init that was called on the given symbol and store the
675 // init method's location context.
676 if (!InitMethodContext)
George Karpenkova1c3bb82018-11-30 02:17:31 +0000677 if (auto CEP = N->getLocation().getAs<CallEnter>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000678 const Stmt *CE = CEP->getCallExpr();
George Karpenkova1c3bb82018-11-30 02:17:31 +0000679 if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000680 const Stmt *RecExpr = ME->getInstanceReceiver();
681 if (RecExpr) {
682 SVal RecV = St->getSVal(RecExpr, NContext);
683 if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
684 InitMethodContext = CEP->getCalleeContext();
685 }
686 }
687 }
688
George Karpenkova1c3bb82018-11-30 02:17:31 +0000689 N = N->getFirstPred();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000690 }
691
692 // If we are reporting a leak of the object that was allocated with alloc,
693 // mark its init method as interesting.
694 const LocationContext *InterestingMethodContext = nullptr;
695 if (InitMethodContext) {
696 const ProgramPoint AllocPP = AllocationNode->getLocation();
697 if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
698 if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
699 if (ME->getMethodFamily() == OMF_alloc)
700 InterestingMethodContext = InitMethodContext;
701 }
702
703 // If allocation happened in a function different from the leak node context,
704 // do not report the binding.
705 assert(N && "Could not find allocation node");
George Karpenkova1c3bb82018-11-30 02:17:31 +0000706
707 if (AllocationNodeInCurrentOrParentContext &&
708 AllocationNodeInCurrentOrParentContext->getLocationContext() !=
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000709 LeakContext)
George Karpenkov70c2ee32018-08-17 21:41:07 +0000710 FirstBinding = nullptr;
George Karpenkov70c2ee32018-08-17 21:41:07 +0000711
Kristof Umann6d716ef2019-08-13 16:45:48 +0000712 return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
George Karpenkov70c2ee32018-08-17 21:41:07 +0000713 InterestingMethodContext);
714}
715
Kristof Umann6d716ef2019-08-13 16:45:48 +0000716PathDiagnosticPieceRef
George Karpenkov0bb17c42019-01-10 18:16:25 +0000717RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
Kristof Umann6d716ef2019-08-13 16:45:48 +0000718 const ExplodedNode *EndN, BugReport &BR) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000719 BR.markInteresting(Sym);
720 return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
721}
722
Kristof Umann6d716ef2019-08-13 16:45:48 +0000723PathDiagnosticPieceRef
George Karpenkov0bb17c42019-01-10 18:16:25 +0000724RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
Kristof Umann6d716ef2019-08-13 16:45:48 +0000725 const ExplodedNode *EndN, BugReport &BR) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000726
727 // Tell the BugReporterContext to report cases when the tracked symbol is
728 // assigned to different variables, etc.
729 BR.markInteresting(Sym);
730
731 // We are reporting a leak. Walk up the graph to get to the first node where
732 // the symbol appeared, and also get the first VarDecl that tracked object
733 // is stored to.
George Karpenkova1c3bb82018-11-30 02:17:31 +0000734 AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000735
736 const MemRegion* FirstBinding = AllocI.R;
737 BR.markInteresting(AllocI.InterestingMethodContext);
738
Kristof Umannfc76d852019-08-13 18:48:08 +0000739 const SourceManager& SM = BRC.getSourceManager();
George Karpenkov70c2ee32018-08-17 21:41:07 +0000740
741 // Compute an actual location for the leak. Sometimes a leak doesn't
742 // occur at an actual statement (e.g., transition between blocks; end
743 // of function) so we need to walk the graph and compute a real location.
744 const ExplodedNode *LeakN = EndN;
745 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
746
747 std::string sbuf;
748 llvm::raw_string_ostream os(sbuf);
749
750 os << "Object leaked: ";
751
752 Optional<std::string> RegionDescription = describeRegion(FirstBinding);
753 if (RegionDescription) {
754 os << "object allocated and stored into '" << *RegionDescription << '\'';
George Karpenkovb3303d72018-11-30 02:17:05 +0000755 } else {
George Karpenkov4f64b382019-01-10 18:15:57 +0000756 os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
757 << "'";
George Karpenkovb3303d72018-11-30 02:17:05 +0000758 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000759
760 // Get the retain count.
761 const RefVal* RV = getRefBinding(EndN->getState(), Sym);
762 assert(RV);
763
764 if (RV->getKind() == RefVal::ErrorLeakReturned) {
765 // FIXME: Per comments in rdar://6320065, "create" only applies to CF
766 // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
767 // to the caller for NS objects.
768 const Decl *D = &EndN->getCodeDecl();
769
770 os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
771 : " is returned from a function ");
772
George Karpenkova1c3bb82018-11-30 02:17:31 +0000773 if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000774 os << "that is annotated as CF_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000775 } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000776 os << "that is annotated as NS_RETURNS_NOT_RETAINED";
George Karpenkovb43772d2018-11-30 02:18:50 +0000777 } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
778 os << "that is annotated as OS_RETURNS_NOT_RETAINED";
George Karpenkova1c3bb82018-11-30 02:17:31 +0000779 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000780 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
781 if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
782 os << "managed by Automatic Reference Counting";
783 } else {
784 os << "whose name ('" << MD->getSelector().getAsString()
785 << "') does not start with "
786 "'copy', 'mutableCopy', 'alloc' or 'new'."
787 " This violates the naming convention rules"
788 " given in the Memory Management Guide for Cocoa";
789 }
George Karpenkov6e9fd132018-08-22 01:17:09 +0000790 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000791 const FunctionDecl *FD = cast<FunctionDecl>(D);
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000792 ObjKind K = RV->getObjKind();
793 if (K == ObjKind::ObjC || K == ObjKind::CF) {
794 os << "whose name ('" << *FD
795 << "') does not contain 'Copy' or 'Create'. This violates the "
796 "naming"
797 " convention rules given in the Memory Management Guide for "
798 "Core"
799 " Foundation";
800 } else if (RV->getObjKind() == ObjKind::OS) {
801 std::string FuncName = FD->getNameAsString();
802 os << "whose name ('" << FuncName
803 << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'";
804 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000805 }
806 }
George Karpenkovf893ea12018-11-30 02:17:44 +0000807 } else {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000808 os << " is not referenced later in this execution path and has a retain "
809 "count of +" << RV->getCount();
George Karpenkovf893ea12018-11-30 02:17:44 +0000810 }
George Karpenkov70c2ee32018-08-17 21:41:07 +0000811
812 return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
813}
814
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000815RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000816 ExplodedNode *n, SymbolRef sym,
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000817 bool isLeak)
818 : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
819 if (!isLeak)
Jonas Devlieghere2b3d49b2019-08-14 23:04:18 +0000820 addVisitor(std::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000821}
822
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000823RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000824 ExplodedNode *n, SymbolRef sym,
825 StringRef endText)
George Karpenkov62db8862018-11-30 02:18:23 +0000826 : BugReport(D, D.getDescription(), endText, n) {
827
Jonas Devlieghere2b3d49b2019-08-14 23:04:18 +0000828 addVisitor(std::make_unique<RefCountReportVisitor>(sym));
George Karpenkov62db8862018-11-30 02:18:23 +0000829}
830
George Karpenkov0bb17c42019-01-10 18:16:25 +0000831void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000832 const SourceManager& SMgr = Ctx.getSourceManager();
833
834 if (!sym->getOriginRegion())
835 return;
836
837 auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
838 if (Region) {
839 const Decl *PDecl = Region->getDecl();
840 if (PDecl && isa<ParmVarDecl>(PDecl)) {
George Karpenkov717c4c02019-01-10 18:15:17 +0000841 PathDiagnosticLocation ParamLocation =
842 PathDiagnosticLocation::create(PDecl, SMgr);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000843 Location = ParamLocation;
844 UniqueingLocation = ParamLocation;
845 UniqueingDecl = Ctx.getLocationContext()->getDecl();
846 }
847 }
848}
849
George Karpenkov0bb17c42019-01-10 18:16:25 +0000850void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
George Karpenkova1c3bb82018-11-30 02:17:31 +0000851 SymbolRef sym) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000852 // Most bug reports are cached at the location where they occurred.
853 // With leaks, we want to unique them by the location where they were
854 // allocated, and only report a single path. To do this, we need to find
855 // the allocation site of a piece of tracked memory, which we do via a
856 // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
857 // Note that this is *not* the trimmed graph; we are guaranteed, however,
858 // that all ancestor nodes that represent the allocation site have the
859 // same SourceLocation.
860 const ExplodedNode *AllocNode = nullptr;
861
862 const SourceManager& SMgr = Ctx.getSourceManager();
863
864 AllocationInfo AllocI =
George Karpenkova1c3bb82018-11-30 02:17:31 +0000865 GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000866
867 AllocNode = AllocI.N;
868 AllocBinding = AllocI.R;
869 markInteresting(AllocI.InterestingMethodContext);
870
871 // Get the SourceLocation for the allocation site.
872 // FIXME: This will crash the analyzer if an allocation comes from an
873 // implicit call (ex: a destructor call).
874 // (Currently there are no such allocations in Cocoa, though.)
875 AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
876
877 if (!AllocStmt) {
878 AllocBinding = nullptr;
879 return;
880 }
881
882 PathDiagnosticLocation AllocLocation =
883 PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
884 AllocNode->getLocationContext());
885 Location = AllocLocation;
886
887 // Set uniqieing info, which will be used for unique the bug reports. The
888 // leaks should be uniqued on the allocation site.
889 UniqueingLocation = AllocLocation;
890 UniqueingDecl = AllocNode->getLocationContext()->getDecl();
891}
892
George Karpenkov0bb17c42019-01-10 18:16:25 +0000893void RefLeakReport::createDescription(CheckerContext &Ctx) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000894 assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
895 Description.clear();
896 llvm::raw_string_ostream os(Description);
897 os << "Potential leak of an object";
898
899 Optional<std::string> RegionDescription = describeRegion(AllocBinding);
900 if (RegionDescription) {
901 os << " stored into '" << *RegionDescription << '\'';
George Karpenkovf893ea12018-11-30 02:17:44 +0000902 } else {
903
904 // If we can't figure out the name, just supply the type information.
George Karpenkov4f64b382019-01-10 18:15:57 +0000905 os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
George Karpenkov70c2ee32018-08-17 21:41:07 +0000906 }
907}
908
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000909RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
George Karpenkov0bb17c42019-01-10 18:16:25 +0000910 ExplodedNode *n, SymbolRef sym,
911 CheckerContext &Ctx)
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000912 : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
George Karpenkov70c2ee32018-08-17 21:41:07 +0000913
914 deriveAllocLocation(Ctx, sym);
915 if (!AllocBinding)
916 deriveParamLocation(Ctx, sym);
917
George Karpenkov936a9c92018-12-07 20:21:37 +0000918 createDescription(Ctx);
George Karpenkov70c2ee32018-08-17 21:41:07 +0000919
Jonas Devlieghere2b3d49b2019-08-14 23:04:18 +0000920 addVisitor(std::make_unique<RefLeakReportVisitor>(sym));
George Karpenkov70c2ee32018-08-17 21:41:07 +0000921}