diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -68,17 +68,20 @@ }; class RefLeakReport : public RefCountReport { - const MemRegion* AllocBinding; - const Stmt *AllocStmt; + const MemRegion *AllocFirstBinding = nullptr; + const MemRegion *AllocBindingToReport = nullptr; + const Stmt *AllocStmt = nullptr; PathDiagnosticLocation Location; // Finds the function declaration where a leak warning for the parameter // 'sym' should be raised. - void deriveParamLocation(CheckerContext &Ctx, SymbolRef sym); - // Finds the location where a leak warning for 'sym' should be raised. - void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym); + void deriveParamLocation(CheckerContext &Ctx); + // Finds the location where the leaking object is allocated. + void deriveAllocLocation(CheckerContext &Ctx); // Produces description of a leak warning which is printed on the console. void createDescription(CheckerContext &Ctx); + // Finds the binding that we should use in a leak warning. + void findBindingToReport(CheckerContext &Ctx, ExplodedNode *Node); public: RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -13,6 +13,8 @@ #include "RetainCountDiagnostics.h" #include "RetainCountChecker.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; @@ -337,15 +339,15 @@ class RefLeakReportVisitor : public RefCountReportVisitor { public: - RefLeakReportVisitor(SymbolRef Sym, const MemRegion *FirstBinding) - : RefCountReportVisitor(Sym), FirstBinding(FirstBinding) {} + RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding) + : RefCountReportVisitor(Sym), LastBinding(LastBinding) {} PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, const ExplodedNode *N, PathSensitiveBugReport &BR) override; private: - const MemRegion *FirstBinding; + const MemRegion *LastBinding; }; } // end namespace retaincountchecker @@ -614,6 +616,41 @@ return None; } +using Bindings = llvm::SmallVector; + +class VarBindingsCollector : public StoreManager::BindingsHandler { + SymbolRef Sym; + Bindings &Result; + +public: + VarBindingsCollector(SymbolRef Sym, Bindings &ToFill) + : Sym(Sym), Result(ToFill) {} + + bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R, + SVal Val) override { + SymbolRef SymV = Val.getAsLocSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (isa(R)) + Result.push_back(R); + + return true; + } +}; + +Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, + const ExplodedNode *Node, SymbolRef Sym) { + Bindings Result; + VarBindingsCollector Collector{Sym, Result}; + while (Result.empty() && Node) { + Manager.iterBindings(Node->getState(), Collector); + Node = Node->getFirstPred(); + } + + return Result; +} + namespace { // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the @@ -740,7 +777,7 @@ os << "Object leaked: "; - Optional RegionDescription = describeRegion(FirstBinding); + Optional RegionDescription = describeRegion(LastBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { @@ -749,7 +786,7 @@ } // Get the retain count. - const RefVal* RV = getRefBinding(EndN->getState(), Sym); + const RefVal *RV = getRefBinding(EndN->getState(), Sym); assert(RV); if (RV->getKind() == RefVal::ErrorLeakReturned) { @@ -790,14 +827,15 @@ " Foundation"; } else if (RV->getObjKind() == ObjKind::OS) { std::string FuncName = FD->getNameAsString(); - os << "whose name ('" << FuncName - << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'"; + os << "whose name ('" << FuncName << "') starts with '" + << StringRef(FuncName).substr(0, 3) << "'"; } } } } else { os << " is not referenced later in this execution path and has a retain " - "count of +" << RV->getCount(); + "count of +" + << RV->getCount(); } return std::make_shared(L, os.str()); @@ -819,16 +857,16 @@ addVisitor(std::make_unique(sym)); } -void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { - const SourceManager& SMgr = Ctx.getSourceManager(); +void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) { + const SourceManager &SMgr = Ctx.getSourceManager(); - if (!sym->getOriginRegion()) + if (!Sym->getOriginRegion()) return; - auto *Region = dyn_cast(sym->getOriginRegion()); + auto *Region = dyn_cast(Sym->getOriginRegion()); if (Region) { const Decl *PDecl = Region->getDecl(); - if (PDecl && isa(PDecl)) { + if (isa_and_nonnull(PDecl)) { PathDiagnosticLocation ParamLocation = PathDiagnosticLocation::create(PDecl, SMgr); Location = ParamLocation; @@ -838,8 +876,7 @@ } } -void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx, - SymbolRef sym) { +void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) { // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were // allocated, and only report a single path. To do this, we need to find @@ -850,13 +887,13 @@ // same SourceLocation. const ExplodedNode *AllocNode = nullptr; - const SourceManager& SMgr = Ctx.getSourceManager(); + const SourceManager &SMgr = Ctx.getSourceManager(); AllocationInfo AllocI = - GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + GetAllocationSite(Ctx.getStateManager(), getErrorNode(), Sym); AllocNode = AllocI.N; - AllocBinding = AllocI.R; + AllocFirstBinding = AllocI.R; markInteresting(AllocI.InterestingMethodContext); // Get the SourceLocation for the allocation site. @@ -866,13 +903,12 @@ AllocStmt = AllocNode->getStmtForDiagnostics(); if (!AllocStmt) { - AllocBinding = nullptr; + AllocFirstBinding = nullptr; return; } - PathDiagnosticLocation AllocLocation = - PathDiagnosticLocation::createBegin(AllocStmt, SMgr, - AllocNode->getLocationContext()); + PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin( + AllocStmt, SMgr, AllocNode->getLocationContext()); Location = AllocLocation; // Set uniqieing info, which will be used for unique the bug reports. The @@ -887,7 +923,8 @@ llvm::raw_string_ostream os(Description); os << "Potential leak of an object"; - Optional RegionDescription = describeRegion(AllocBinding); + Optional RegionDescription = + describeRegion(AllocBindingToReport); if (RegionDescription) { os << " stored into '" << *RegionDescription << '\''; } else { @@ -897,16 +934,59 @@ } } +void RefLeakReport::findBindingToReport(CheckerContext &Ctx, + ExplodedNode *Node) { + if (!AllocFirstBinding) + // If we don't have any bindings, we won't be able to find any + // better binding to report. + return; + + // If the original region still contains the leaking symbol... + if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) { + // ...it is the best binding to report. + AllocBindingToReport = AllocFirstBinding; + return; + } + + // At this point, we know that the original region doesn't contain the leaking + // when the actual leak happens. It means that it can be confusing for the + // user to see such description in the message. + // + // Let's consider the following example: + // Object *Original = allocate(...); + // Object *New = Original; + // Original = allocate(...); + // Original->release(); + // + // Complaining about a leaking object "stored into Original" might cause a + // rightful confusion because 'Original' is actually released. + // We should complain about 'New' instead. + Bindings AllVarBindings = + getAllVarBindingsForSymbol(Ctx.getStateManager(), Node, Sym); + + // While looking for the last var bindings, we can still find + // `AllocFirstBinding` to be one of them. In situations like this, + // it would still be the easiest case to explain to our users. + if (!AllVarBindings.empty() && + llvm::count(AllVarBindings, AllocFirstBinding) == 0) + // Let's pick one of them at random (if there is something to pick from). + AllocBindingToReport = AllVarBindings[0]; + else + AllocBindingToReport = AllocFirstBinding; +} + RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *N, SymbolRef Sym, CheckerContext &Ctx) : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) { - deriveAllocLocation(Ctx, Sym); - if (!AllocBinding) - deriveParamLocation(Ctx, Sym); + deriveAllocLocation(Ctx); + findBindingToReport(Ctx, N); + + if (!AllocFirstBinding) + deriveParamLocation(Ctx); createDescription(Ctx); - addVisitor(std::make_unique(Sym, AllocBinding)); + addVisitor(std::make_unique(Sym, AllocBindingToReport)); } diff --git a/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist b/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist --- a/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist +++ b/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist @@ -21946,12 +21946,12 @@ depth0 extended_message - Object leaked: object allocated and stored into 'foo' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'garply' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: object allocated and stored into 'foo' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'garply' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object stored into 'foo' + descriptionPotential leak of an object stored into 'garply' categoryMemory (Core Foundation/Objective-C/OSObject) typeLeak check_nameosx.cocoa.RetainCount diff --git a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist --- a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist +++ b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist @@ -5012,6 +5012,1030 @@ + + path + + + kindcontrol + edges + + + start + + + line337 + col3 + file0 + + + line337 + col4 + file0 + + + end + + + line337 + col17 + file0 + + + line337 + col17 + file0 + + + + + + + kindevent + location + + line337 + col17 + file0 + + ranges + + + + line337 + col17 + file0 + + + line337 + col37 + file0 + + + + depth0 + extended_message + Calling 'initY' + message + Calling 'initY' + + + kindevent + location + + line214 + col1 + file0 + + depth1 + extended_message + Entered call from 'testLeakAliasSimple' + message + Entered call from 'testLeakAliasSimple' + + + kindcontrol + edges + + + start + + + line214 + col1 + file0 + + + line214 + col1 + file0 + + + end + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + + + + + kindevent + location + + line215 + col10 + file0 + + ranges + + + + line215 + col10 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Method returns an instance of MyObj with a +1 retain count + message + Method returns an instance of MyObj with a +1 retain count + + + kindcontrol + edges + + + start + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + end + + + line216 + col3 + file0 + + + line216 + col8 + file0 + + + + + + + kindevent + location + + line337 + col17 + file0 + + ranges + + + + line337 + col17 + file0 + + + line337 + col37 + file0 + + + + depth0 + extended_message + Returning from 'initY' + message + Returning from 'initY' + + + kindcontrol + edges + + + start + + + line337 + col17 + file0 + + + line337 + col17 + file0 + + + end + + + line337 + col3 + file0 + + + line337 + col4 + file0 + + + + + + + kindcontrol + edges + + + start + + + line337 + col3 + file0 + + + line337 + col4 + file0 + + + end + + + line342 + col3 + file0 + + + line342 + col3 + file0 + + + + + + + kindevent + location + + line342 + col3 + file0 + + ranges + + + + line342 + col3 + file0 + + + line342 + col20 + file0 + + + + depth0 + extended_message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + + + descriptionPotential leak of an object stored into 'New' + categoryMemory (Core Foundation/Objective-C/OSObject) + typeLeak + check_nameosx.cocoa.RetainCount + + issue_hash_content_of_line_in_contextc6578d694dd90a2f586f52c9b53042c3 + issue_context_kindObjective-C method + issue_contexttestLeakAliasSimple + issue_hash_function_offset1 + location + + line342 + col3 + file0 + + ExecutedLines + + 0 + + 214 + 215 + 216 + 219 + 220 + 221 + 336 + 337 + 339 + 340 + 341 + 342 + + + + + path + + + kindcontrol + edges + + + start + + + line347 + col3 + file0 + + + line347 + col4 + file0 + + + end + + + line347 + col17 + file0 + + + line347 + col17 + file0 + + + + + + + kindevent + location + + line347 + col17 + file0 + + ranges + + + + line347 + col17 + file0 + + + line347 + col37 + file0 + + + + depth0 + extended_message + Calling 'initY' + message + Calling 'initY' + + + kindevent + location + + line214 + col1 + file0 + + depth1 + extended_message + Entered call from 'testLeakAliasChain' + message + Entered call from 'testLeakAliasChain' + + + kindcontrol + edges + + + start + + + line214 + col1 + file0 + + + line214 + col1 + file0 + + + end + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + + + + + kindevent + location + + line215 + col10 + file0 + + ranges + + + + line215 + col10 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Method returns an instance of MyObj with a +1 retain count + message + Method returns an instance of MyObj with a +1 retain count + + + kindcontrol + edges + + + start + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + end + + + line216 + col3 + file0 + + + line216 + col8 + file0 + + + + + + + kindevent + location + + line347 + col17 + file0 + + ranges + + + + line347 + col17 + file0 + + + line347 + col37 + file0 + + + + depth0 + extended_message + Returning from 'initY' + message + Returning from 'initY' + + + kindcontrol + edges + + + start + + + line347 + col17 + file0 + + + line347 + col17 + file0 + + + end + + + line347 + col3 + file0 + + + line347 + col4 + file0 + + + + + + + kindcontrol + edges + + + start + + + line347 + col3 + file0 + + + line347 + col4 + file0 + + + end + + + line353 + col3 + file0 + + + line353 + col3 + file0 + + + + + + + kindevent + location + + line353 + col3 + file0 + + ranges + + + + line353 + col3 + file0 + + + line353 + col20 + file0 + + + + depth0 + extended_message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + + + descriptionPotential leak of an object stored into 'New' + categoryMemory (Core Foundation/Objective-C/OSObject) + typeLeak + check_nameosx.cocoa.RetainCount + + issue_hash_content_of_line_in_context076457f7cf482449386f129554f48f68 + issue_context_kindObjective-C method + issue_contexttestLeakAliasChain + issue_hash_function_offset1 + location + + line353 + col3 + file0 + + ExecutedLines + + 0 + + 214 + 215 + 216 + 219 + 220 + 221 + 346 + 347 + 349 + 350 + 351 + 352 + 353 + + + + + path + + + kindcontrol + edges + + + start + + + line371 + col3 + file0 + + + line371 + col4 + file0 + + + end + + + line371 + col17 + file0 + + + line371 + col17 + file0 + + + + + + + kindevent + location + + line371 + col17 + file0 + + ranges + + + + line371 + col17 + file0 + + + line371 + col37 + file0 + + + + depth0 + extended_message + Calling 'initY' + message + Calling 'initY' + + + kindevent + location + + line214 + col1 + file0 + + depth1 + extended_message + Entered call from 'testLeakAliasDeathInExpr' + message + Entered call from 'testLeakAliasDeathInExpr' + + + kindcontrol + edges + + + start + + + line214 + col1 + file0 + + + line214 + col1 + file0 + + + end + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + + + + + kindevent + location + + line215 + col10 + file0 + + ranges + + + + line215 + col10 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Method returns an instance of MyObj with a +1 retain count + message + Method returns an instance of MyObj with a +1 retain count + + + kindcontrol + edges + + + start + + + line215 + col3 + file0 + + + line215 + col6 + file0 + + + end + + + line216 + col3 + file0 + + + line216 + col8 + file0 + + + + + + + kindevent + location + + line371 + col17 + file0 + + ranges + + + + line371 + col17 + file0 + + + line371 + col37 + file0 + + + + depth0 + extended_message + Returning from 'initY' + message + Returning from 'initY' + + + kindcontrol + edges + + + start + + + line371 + col17 + file0 + + + line371 + col17 + file0 + + + end + + + line371 + col3 + file0 + + + line371 + col4 + file0 + + + + + + + kindcontrol + edges + + + start + + + line371 + col3 + file0 + + + line371 + col4 + file0 + + + end + + + line376 + col3 + file0 + + + line376 + col3 + file0 + + + + + + + kindevent + location + + line376 + col3 + file0 + + ranges + + + + line376 + col3 + file0 + + + line376 + col20 + file0 + + + + depth0 + extended_message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + message + Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1 + + + descriptionPotential leak of an object stored into 'New' + categoryMemory (Core Foundation/Objective-C/OSObject) + typeLeak + check_nameosx.cocoa.RetainCount + + issue_hash_content_of_line_in_context1ead75ff85eb09fa93374d0083f531e5 + issue_context_kindObjective-C method + issue_contexttestLeakAliasDeathInExpr + issue_hash_function_offset1 + location + + line376 + col3 + file0 + + ExecutedLines + + 0 + + 214 + 215 + 216 + 219 + 220 + 221 + 357 + 358 + 359 + 360 + 363 + 364 + 365 + 366 + 367 + 370 + 371 + 373 + 374 + 375 + 376 + + + files diff --git a/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist b/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist --- a/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist +++ b/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist @@ -25407,12 +25407,12 @@ depth0 extended_message - Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'second' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'second' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object stored into 'obj' + descriptionPotential leak of an object stored into 'second' categoryMemory (Core Foundation/Objective-C/OSObject) typeLeak check_nameosx.cocoa.RetainCount @@ -25665,12 +25665,12 @@ depth0 extended_message - Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'alias' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'alias' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object stored into 'arr' + descriptionPotential leak of an object stored into 'alias' categoryMemory (Core Foundation/Objective-C/OSObject) typeLeak check_nameosx.cocoa.RetainCount diff --git a/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist b/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist --- a/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist +++ b/clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist @@ -25476,12 +25476,12 @@ depth0 extended_message - Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'second' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'second' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object stored into 'obj' + descriptionPotential leak of an object stored into 'second' categoryMemory (Core Foundation/Objective-C/OSObject) typeLeak check_nameosx.cocoa.RetainCount @@ -25734,12 +25734,12 @@ depth0 extended_message - Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'alias' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'alias' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object stored into 'arr' + descriptionPotential leak of an object stored into 'alias' categoryMemory (Core Foundation/Objective-C/OSObject) typeLeak check_nameosx.cocoa.RetainCount diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -641,7 +641,7 @@ OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} { OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr'}} - // expected-note@-1{{Returning from constructor for 'smart_ptr'}} + // expected-note@-1{{Returning from constructor for 'smart_ptr'}} // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}} // expected-note@os_smart_ptr.h:13{{Taking true branch}} // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} @@ -653,9 +653,9 @@ // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}} // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} - // expected-note@-6{{Returning from '~smart_ptr'}} -} // expected-warning{{Potential leak of an object stored into 'obj'}} -// expected-note@-1{{Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1}} + // expected-note@-6{{Returning from '~smart_ptr'}} +} // expected-warning{{Potential leak of an object stored into 'p'}} +// expected-note@-1{{Object leaked: object allocated and stored into 'p' is not referenced later in this execution path and has a retain count of +1}} void test_smart_ptr_no_leak() { OSObject *obj = new OSObject; diff --git a/clang/test/Analysis/retain-release-path-notes.m b/clang/test/Analysis/retain-release-path-notes.m --- a/clang/test/Analysis/retain-release-path-notes.m +++ b/clang/test/Analysis/retain-release-path-notes.m @@ -212,7 +212,7 @@ } -(id)initY { - self = [super init]; //expected-note {{Method returns an instance of MyObj with a +1 retain count}} + self = [super init]; // expected-note 4 {{Method returns an instance of MyObj with a +1 retain count}} return self; } @@ -327,5 +327,54 @@ @end +int seed(); +@interface LeakReassignmentTests : MyObj +@end + +@implementation LeakReassignmentTests ++(void)testLeakAliasSimple { + id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} + // expected-note@-1 {{Returning from 'initY'}} + id New = Original; + Original = [[MyObj alloc] initZ]; + (void)New; + [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}} +} + ++(void)testLeakAliasChain { + id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} + // expected-note@-1 {{Returning from 'initY'}} + id Intermediate = Original; + id New = Intermediate; + Original = [[MyObj alloc] initZ]; + (void)New; + [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}} +} + ++(void)log:(id)Obj with:(int)Num { + Num *= 42; + if (Obj ) + Num /= 2; +} + ++(int)calculate { + int x = 10; + int y = 25; + x += y * x + seed(); + return y - x * y; +} ++(void)testLeakAliasDeathInExpr { + id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} + // expected-note@-1 {{Returning from 'initY'}} + id New = Original; + Original = [[MyObj alloc] initZ]; + [self log:New with:[self calculate]]; + [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'New' is not referenced later in this execution path and has a retain count of +1}} +} + +@end diff --git a/clang/test/Analysis/retain-release.m b/clang/test/Analysis/retain-release.m --- a/clang/test/Analysis/retain-release.m +++ b/clang/test/Analysis/retain-release.m @@ -2282,7 +2282,7 @@ void testAutoreleaseReturnsInput() { extern CFTypeRef CFCreateSomething(); - CFTypeRef obj = CFCreateSomething(); // expected-warning{{Potential leak of an object stored into 'obj'}} + CFTypeRef obj = CFCreateSomething(); // expected-warning{{Potential leak of an object stored into 'second'}} CFTypeRef second = CFAutorelease(obj); CFRetain(second); } @@ -2302,7 +2302,7 @@ } void autoreleaseReturningTypedObject() { - CFArrayRef arr = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Potential leak of an object stored into 'arr'}} + CFArrayRef arr = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Potential leak of an object stored into 'alias'}} CFArrayRef alias = (CFArrayRef)CFAutorelease((CFTypeRef)arr); CFRetain(alias); }