NoQ created this revision. Herald added a subscriber: xazax.hun. RetainCountChecker's warning message "`Incorrect decrement of the reference count of an object that is not owned at this point by the caller`" does not explicitly mention the caller, which may be confusing when there is a nested block, especially when the block is hard to notice. It should be obvious to which caller it refers. The patch tries to improve on that.
By the way, plist-based tests in `retain-release.m` are disabled since r163536 (~2012), and need to be updated. It's trivial to re-enable them but annoying to maintain - would we prefer to re-enable or delete them or replace with `-analyzer-output=text` tests? https://reviews.llvm.org/D36750 Files: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp test/Analysis/retain-release.m
Index: test/Analysis/retain-release.m =================================================================== --- test/Analysis/retain-release.m +++ test/Analysis/retain-release.m @@ -2300,6 +2300,23 @@ CFRelease(obj); } +//===----------------------------------------------------------------------===// +// When warning within blocks make it obvious that warnings refer to blocks. +//===----------------------------------------------------------------------===// + +int useBlock(int (^block)()); +void rdar31699502_hardToNoticeBlocks() { + if (useBlock(^{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSArray *array = [NSArray array]; + [array release]; // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the current block}} + [pool drain]; + return 0; + })) { + return; + } +} + // CHECK: <key>diagnostics</key> // CHECK-NEXT: <array> // CHECK-NEXT: <dict> Index: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -86,6 +86,7 @@ ErrorDeallocGC, // Calling -dealloc with GC enabled. ErrorUseAfterRelease, // Object used after released. ErrorReleaseNotOwned, // Release of an object that was not owned. + ErrorReleaseNotOwnedByBlock, // Release of an object not owned by a block. ERROR_LEAK_START, ErrorLeak, // A memory leak due to excessive reference counts. ErrorLeakReturned, // A memory leak due to the returning method not having @@ -331,6 +332,9 @@ Out << "Release of Not-Owned [ERROR]"; break; + case ErrorReleaseNotOwnedByBlock: + Out << "Release of Not-Owned by Block [ERROR]"; + case RefVal::ErrorOverAutorelease: Out << "Over-autoreleased"; break; @@ -1714,6 +1718,17 @@ } }; + class BadReleaseByBlock : public CFRefBug { + public: + BadReleaseByBlock(const CheckerBase *checker) + : CFRefBug(checker, "Bad release") {} + + const char *getDescription() const override { + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the current block"; + } + }; + class DeallocGC : public CFRefBug { public: DeallocGC(const CheckerBase *checker) @@ -2510,7 +2525,8 @@ check::RegionChanges, eval::Assume, eval::Call > { - mutable std::unique_ptr<CFRefBug> useAfterRelease, releaseNotOwned; + mutable std::unique_ptr<CFRefBug> useAfterRelease; + mutable std::unique_ptr<CFRefBug> releaseNotOwned, releaseNotOwnedByBlock; mutable std::unique_ptr<CFRefBug> deallocGC, deallocNotOwned; mutable std::unique_ptr<CFRefBug> overAutorelease, returnNotOwnedForOwned; mutable std::unique_ptr<CFRefBug> leakWithinFunction, leakAtReturn; @@ -3299,7 +3315,10 @@ return removeRefBinding(state, sym); V = V.releaseViaIvar() ^ RefVal::Released; } else { - V = V ^ RefVal::ErrorReleaseNotOwned; + if (isa<BlockDecl>(C.getLocationContext()->getDecl())) + V = V ^ RefVal::ErrorReleaseNotOwnedByBlock; + else + V = V ^ RefVal::ErrorReleaseNotOwned; hasErr = V.getKind(); } break; @@ -3349,6 +3368,11 @@ releaseNotOwned.reset(new BadRelease(this)); BT = releaseNotOwned.get(); break; + case RefVal::ErrorReleaseNotOwnedByBlock: + if (!releaseNotOwnedByBlock) + releaseNotOwnedByBlock.reset(new BadReleaseByBlock(this)); + BT = releaseNotOwnedByBlock.get(); + break; case RefVal::ErrorDeallocGC: if (!deallocGC) deallocGC.reset(new DeallocGC(this));
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits