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 @@ -616,7 +616,7 @@ return None; } -using Bindings = llvm::SmallVector; +using Bindings = llvm::SmallVector, 4>; class VarBindingsCollector : public StoreManager::BindingsHandler { SymbolRef Sym; @@ -633,7 +633,7 @@ return true; if (isa(R)) - Result.push_back(R); + Result.emplace_back(R, Val); return true; } @@ -968,11 +968,28 @@ // `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) + llvm::count_if(AllVarBindings, + [this](const std::pair Binding) { + return Binding.first == AllocFirstBinding; + }) == 0) { // Let's pick one of them at random (if there is something to pick from). - AllocBindingToReport = AllVarBindings[0]; - else + AllocBindingToReport = AllVarBindings[0].first; + + // Because 'AllocBindingToReport' is not the the same as + // 'AllocFirstBinding', we need to explain how the leaking object + // got from one to another. + // + // NOTE: We use the actual SVal stored in AllocBindingToReport here because + // FindLastStoreBRVisitor compares SVal's and it can get trickier for + // something like derived regions if we want to construct SVal from + // Sym. Instead, we take the value that is definitely stored in that + // region, thus guaranteeing that FindLastStoreBRVisitor will work. + addVisitor(std::make_unique( + AllVarBindings[0].second.castAs(), AllocBindingToReport, + false, bugreporter::TrackingKind::Thorough)); + } else { AllocBindingToReport = AllocFirstBinding; + } } RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, 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 @@ -21827,6 +21827,35 @@ + + kindevent + location + + line566 + col3 + file0 + + ranges + + + + line566 + col3 + file0 + + + line566 + col11 + file0 + + + + depth0 + extended_message + 'garply' initialized here + message + 'garply' initialized here + kindcontrol edges 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 @@ -5155,6 +5155,35 @@ message Method returns an instance of MyObj with a +1 retain count + + kindevent + location + + line215 + col3 + file0 + + ranges + + + + line215 + col3 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Value assigned to 'self' + message + Value assigned to 'self' + kindcontrol edges @@ -5189,6 +5218,35 @@ + + kindevent + location + + line216 + col3 + file0 + + ranges + + + + line216 + col3 + file0 + + + line216 + col13 + file0 + + + + depth1 + extended_message + Returning pointer (loaded from 'self') + message + Returning pointer (loaded from 'self') + kindevent location @@ -5252,6 +5310,35 @@ + + kindevent + location + + line337 + col3 + file0 + + ranges + + + + line337 + col3 + file0 + + + line337 + col13 + file0 + + + + depth0 + extended_message + 'Original' initialized here + message + 'Original' initialized here + kindcontrol edges @@ -5279,7 +5366,7 @@ line342 - col3 + col4 file0 @@ -5304,6 +5391,69 @@ line342 + col8 + file0 + + + + depth0 + extended_message + 'New' initialized here + message + 'New' initialized here + + + kindcontrol + edges + + + start + + + line342 + col3 + file0 + + + line342 + col4 + file0 + + + end + + + line345 + col3 + file0 + + + line345 + col3 + file0 + + + + + + + kindevent + location + + line345 + col3 + file0 + + ranges + + + + line345 + col3 + file0 + + + line345 col20 file0 @@ -5327,7 +5477,7 @@ issue_hash_function_offset1 location - line342 + line345 col3 file0 @@ -5343,10 +5493,10 @@ 221 336 337 - 339 - 340 - 341 342 + 343 + 344 + 345 @@ -5361,12 +5511,12 @@ start - line347 + line350 col3 file0 - line347 + line350 col4 file0 @@ -5374,12 +5524,12 @@ end - line347 + line350 col17 file0 - line347 + line350 col17 file0 @@ -5391,7 +5541,7 @@ kindevent location - line347 + line350 col17 file0 @@ -5399,12 +5549,12 @@ - line347 + line350 col17 file0 - line347 + line350 col37 file0 @@ -5493,6 +5643,35 @@ message Method returns an instance of MyObj with a +1 retain count + + kindevent + location + + line215 + col3 + file0 + + ranges + + + + line215 + col3 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Value assigned to 'self' + message + Value assigned to 'self' + kindcontrol edges @@ -5531,7 +5710,36 @@ kindevent location - line347 + line216 + col3 + file0 + + ranges + + + + line216 + col3 + file0 + + + line216 + col13 + file0 + + + + depth1 + extended_message + Returning pointer (loaded from 'self') + message + Returning pointer (loaded from 'self') + + + kindevent + location + + line350 col17 file0 @@ -5539,12 +5747,12 @@ - line347 + line350 col17 file0 - line347 + line350 col37 file0 @@ -5564,12 +5772,12 @@ start - line347 + line350 col17 file0 - line347 + line350 col17 file0 @@ -5577,12 +5785,138 @@ end - line347 + line350 + col3 + file0 + + + line350 + col4 + file0 + + + + + + + kindevent + location + + line350 + col3 + file0 + + ranges + + + + line350 + col3 + file0 + + + line350 + col13 + file0 + + + + depth0 + extended_message + 'Original' initialized here + message + 'Original' initialized here + + + kindcontrol + edges + + + start + + + line350 + col3 + file0 + + + line350 + col4 + file0 + + + end + + + line355 + col3 + file0 + + + line355 + col4 + file0 + + + + + + + kindevent + location + + line355 + col3 + file0 + + ranges + + + + line355 + col3 + file0 + + + line355 + col17 + file0 + + + + depth0 + extended_message + 'Intermediate' initialized here + message + 'Intermediate' initialized here + + + kindcontrol + edges + + + start + + + line355 + col3 + file0 + + + line355 + col4 + file0 + + + end + + + line356 col3 file0 - line347 + line356 col4 file0 @@ -5590,6 +5924,35 @@ + + kindevent + location + + line356 + col3 + file0 + + ranges + + + + line356 + col3 + file0 + + + line356 + col8 + file0 + + + + depth0 + extended_message + 'New' initialized here + message + 'New' initialized here + kindcontrol edges @@ -5598,12 +5961,12 @@ start - line347 + line356 col3 file0 - line347 + line356 col4 file0 @@ -5611,12 +5974,12 @@ end - line353 + line359 col3 file0 - line353 + line359 col3 file0 @@ -5628,7 +5991,7 @@ kindevent location - line353 + line359 col3 file0 @@ -5636,12 +5999,12 @@ - line353 + line359 col3 file0 - line353 + line359 col20 file0 @@ -5665,7 +6028,7 @@ issue_hash_function_offset1 location - line353 + line359 col3 file0 @@ -5679,13 +6042,13 @@ 219 220 221 - 346 - 347 349 350 - 351 - 352 - 353 + 355 + 356 + 357 + 358 + 359 @@ -5700,12 +6063,12 @@ start - line371 + line377 col3 file0 - line371 + line377 col4 file0 @@ -5713,12 +6076,12 @@ end - line371 + line377 col17 file0 - line371 + line377 col17 file0 @@ -5730,7 +6093,7 @@ kindevent location - line371 + line377 col17 file0 @@ -5738,12 +6101,12 @@ - line371 + line377 col17 file0 - line371 + line377 col37 file0 @@ -5832,6 +6195,35 @@ message Method returns an instance of MyObj with a +1 retain count + + kindevent + location + + line215 + col3 + file0 + + ranges + + + + line215 + col3 + file0 + + + line215 + col21 + file0 + + + + depth1 + extended_message + Value assigned to 'self' + message + Value assigned to 'self' + kindcontrol edges @@ -5870,7 +6262,36 @@ kindevent location - line371 + line216 + col3 + file0 + + ranges + + + + line216 + col3 + file0 + + + line216 + col13 + file0 + + + + depth1 + extended_message + Returning pointer (loaded from 'self') + message + Returning pointer (loaded from 'self') + + + kindevent + location + + line377 col17 file0 @@ -5878,12 +6299,12 @@ - line371 + line377 col17 file0 - line371 + line377 col37 file0 @@ -5903,12 +6324,12 @@ start - line371 + line377 col17 file0 - line371 + line377 col17 file0 @@ -5916,12 +6337,12 @@ end - line371 + line377 col3 file0 - line371 + line377 col4 file0 @@ -5929,6 +6350,35 @@ + + kindevent + location + + line377 + col3 + file0 + + ranges + + + + line377 + col3 + file0 + + + line377 + col13 + file0 + + + + depth0 + extended_message + 'Original' initialized here + message + 'Original' initialized here + kindcontrol edges @@ -5937,12 +6387,12 @@ start - line371 + line377 col3 file0 - line371 + line377 col4 file0 @@ -5950,12 +6400,75 @@ end - line376 + line383 + col3 + file0 + + + line383 + col5 + file0 + + + + + + + kindevent + location + + line383 + col3 + file0 + + ranges + + + + line383 + col3 + file0 + + + line383 + col16 + file0 + + + + depth0 + extended_message + Value assigned to 'New' + message + Value assigned to 'New' + + + kindcontrol + edges + + + start + + + line383 + col3 + file0 + + + line383 + col5 + file0 + + + end + + + line386 col3 file0 - line376 + line386 col3 file0 @@ -5967,7 +6480,7 @@ kindevent location - line376 + line386 col3 file0 @@ -5975,12 +6488,12 @@ - line376 + line386 col3 file0 - line376 + line386 col20 file0 @@ -6004,7 +6517,7 @@ issue_hash_function_offset1 location - line376 + line386 col3 file0 @@ -6018,21 +6531,22 @@ 219 220 221 - 357 - 358 - 359 - 360 363 364 365 366 - 367 + 369 370 371 + 372 373 - 374 - 375 376 + 377 + 382 + 383 + 384 + 385 + 386 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 @@ -25288,6 +25288,35 @@ message Object autoreleased + + kindevent + location + + line2286 + col3 + file0 + + ranges + + + + line2286 + col3 + file0 + + + line2286 + col18 + file0 + + + + depth0 + extended_message + 'second' initialized here + message + 'second' initialized here + kindcontrol edges @@ -25546,6 +25575,35 @@ message Object autoreleased + + kindevent + location + + line2306 + col3 + file0 + + ranges + + + + line2306 + col3 + file0 + + + line2306 + col18 + file0 + + + + depth0 + extended_message + 'alias' initialized here + message + 'alias' initialized here + kindcontrol edges 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 @@ -25357,6 +25357,35 @@ message Object autoreleased + + kindevent + location + + line2286 + col3 + file0 + + ranges + + + + line2286 + col3 + file0 + + + line2286 + col18 + file0 + + + + depth0 + extended_message + 'second' initialized here + message + 'second' initialized here + kindcontrol edges @@ -25615,6 +25644,35 @@ message Object autoreleased + + kindevent + location + + line2306 + col3 + file0 + + ranges + + + + line2306 + col3 + file0 + + + line2306 + col18 + file0 + + + + depth0 + extended_message + 'alias' initialized here + message + 'alias' initialized here + kindcontrol edges 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 @@ -526,19 +526,58 @@ void check_dynamic_cast_null_check() { OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}} - // expected-warning@-1{{Potential leak of an object}} - // expected-note@-2{{Object leaked}} - // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} + // expected-warning@-1{{Potential leak of an object}} + // expected-note@-2{{Object leaked}} + // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) return; arr->release(); } +void check_dynamic_cast_alias() { + OSObject *originalPtr = OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject}} + OSArray *newPtr = OSDynamicCast(OSArray, originalPtr); // expected-note {{'newPtr' initialized here}} + if (newPtr) { // expected-note {{'newPtr' is non-null}} + // expected-note@-1 {{Taking true branch}} + originalPtr = OSObject::generateObject(42); + (void)newPtr; + } + originalPtr->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}} +} + +void check_dynamic_cast_alias_cond() { + OSObject *originalPtr = OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject}} + OSArray *newPtr = 0; + if ((newPtr = OSDynamicCast(OSArray, originalPtr))) { // expected-note {{Value assigned to 'newPtr'}} + // expected-note@-1 {{'newPtr' is non-null}} + // expected-note@-2 {{Taking true branch}} + originalPtr = OSObject::generateObject(42); + (void)newPtr; + } + originalPtr->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}} +} + +void check_dynamic_cast_alias_intermediate() { + OSObject *originalPtr = OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject of type 'OSObject' with a +1 retain count}} + OSObject *intermediate = originalPtr; // TODO: add note here as well + OSArray *newPtr = 0; + if ((newPtr = OSDynamicCast(OSArray, intermediate))) { // expected-note {{Value assigned to 'newPtr'}} + // expected-note@-1 {{'newPtr' is non-null}} + // expected-note@-2 {{Taking true branch}} + intermediate = OSObject::generateObject(42); + (void)newPtr; + } + intermediate->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}} +} + void use_after_release() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} - arr->release(); // expected-note{{Object released}} - arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}} - // expected-note@-1{{Reference-counted object is used after it is released}} + arr->release(); // expected-note{{Object released}} + arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}} + // expected-note@-1{{Reference-counted object is used after it is released}} } void potential_leak() { @@ -642,6 +681,7 @@ { OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr'}} // expected-note@-1{{Returning from constructor for 'smart_ptr'}} + // expected-note@-2 {{'p' initialized here}} // 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'}} 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 @@ -335,8 +335,11 @@ @implementation LeakReassignmentTests +(void)testLeakAliasSimple { id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} - // expected-note@-1 {{Returning from 'initY'}} - id New = Original; + // expected-note@215 {{Value assigned to 'self'}} + // expected-note@216 {{Returning pointer (loaded from 'self')}} + // expected-note@-3 {{Returning from 'initY'}} + // expected-note@-4 {{'Original' initialized here}} + id New = Original; // expected-note {{'New' initialized here}} Original = [[MyObj alloc] initZ]; (void)New; [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}} @@ -345,9 +348,12 @@ +(void)testLeakAliasChain { id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} - // expected-note@-1 {{Returning from 'initY'}} - id Intermediate = Original; - id New = Intermediate; + // expected-note@215 {{Value assigned to 'self'}} + // expected-note@216 {{Returning pointer (loaded from 'self')}} + // expected-note@-3 {{Returning from 'initY'}} + // expected-note@-4 {{'Original' initialized here}} + id Intermediate = Original; // expected-note {{'Intermediate' initialized here}} + id New = Intermediate; // expected-note {{'New' initialized here}} Original = [[MyObj alloc] initZ]; (void)New; [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}} @@ -369,8 +375,12 @@ +(void)testLeakAliasDeathInExpr { id Original = [[MyObj alloc] initY]; // expected-note {{Calling 'initY'}} - // expected-note@-1 {{Returning from 'initY'}} - id New = Original; + // expected-note@215 {{Value assigned to 'self'}} + // expected-note@216 {{Returning pointer (loaded from 'self')}} + // expected-note@-3 {{Returning from 'initY'}} + // expected-note@-4 {{'Original' initialized here}} + id New = 0; + New = Original; // expected-note {{Value assigned to 'New'}} Original = [[MyObj alloc] initZ]; [self log:New with:[self calculate]]; [Original release]; // expected-warning {{Potential leak of an object stored into 'New'}}