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

Reply via email to