Fix a crash in Retain Count checker error reporting
authorAnna Zaks <ganna@apple.com>
Fri, 13 Jun 2014 23:47:38 +0000 (23:47 +0000)
committerAnna Zaks <ganna@apple.com>
Fri, 13 Jun 2014 23:47:38 +0000 (23:47 +0000)
Fixes a crash in Retain Count checker error reporting logic by handing
the allocation statement retrieval from a BlockEdge program point.

Also added a simple CFG dump routine for debugging.

llvm-svn: 210960

clang/include/clang/Analysis/CFG.h
clang/lib/Analysis/CFG.cpp
clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
clang/test/Analysis/objc-radar17039661.m [new file with mode: 0644]

index 7909fdc..891fb90 100644 (file)
@@ -641,6 +641,8 @@ public:
 
   CFG *getParent() const { return Parent; }
 
+  void dump() const;
+
   void dump(const CFG *cfg, const LangOptions &LO, bool ShowColors = false) const;
   void print(raw_ostream &OS, const CFG* cfg, const LangOptions &LO,
              bool ShowColors) const;
index 61e5f5a..d6361e8 100644 (file)
@@ -4356,6 +4356,10 @@ void CFGBlock::dump(const CFG* cfg, const LangOptions &LO,
   print(llvm::errs(), cfg, LO, ShowColors);
 }
 
+void CFGBlock::dump() const {
+  dump(getParent(), LangOptions(), false);
+}
+
 /// print - A simple pretty printer of a CFGBlock that outputs to an ostream.
 ///   Generally this will only be called from CFG::print.
 void CFGBlock::print(raw_ostream &OS, const CFG* cfg,
index 0c130fe..eb699d6 100644 (file)
@@ -2340,14 +2340,27 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
 
   // Get the SourceLocation for the allocation site.
   // FIXME: This will crash the analyzer if an allocation comes from an
-  // implicit call. (Currently there are no such allocations in Cocoa, though.)
-  const Stmt *AllocStmt;
+  // implicit call (ex: a destructor call).
+  // (Currently there are no such allocations in Cocoa, though.)
+  const Stmt *AllocStmt = 0;
   ProgramPoint P = AllocNode->getLocation();
   if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
     AllocStmt = Exit->getCalleeContext()->getCallSite();
-  else
-    AllocStmt = P.castAs<PostStmt>().getStmt();
-  assert(AllocStmt && "All allocations must come from explicit calls");
+  else {
+    // We are going to get a BlockEdge when the leak and allocation happen in
+    // different, non-nested frames (contexts). For example, the case where an
+    // allocation happens in a block that captures a reference to it and
+    // that reference is overwritten/dropped by another call to the block.
+    if (Optional<BlockEdge> Edge = P.getAs<BlockEdge>()) {
+      if (Optional<CFGStmt> St = Edge->getDst()->front().getAs<CFGStmt>()) {
+        AllocStmt = St->getStmt();
+      }
+    }
+    else {
+      AllocStmt = P.castAs<PostStmt>().getStmt();
+    }
+  }
+  assert(AllocStmt && "Cannot find allocation statement");
 
   PathDiagnosticLocation AllocLocation =
     PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
diff --git a/clang/test/Analysis/objc-radar17039661.m b/clang/test/Analysis/objc-radar17039661.m
new file mode 100644 (file)
index 0000000..ec4f19d
--- /dev/null
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -verify -fblocks %s
+
+@class NSString;
+typedef long NSInteger;
+typedef unsigned char BOOL;
+@interface NSObject {}
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+-(id)retain;
+@end
+@interface NSNumber : NSObject
++ (NSNumber *)numberWithInteger:(NSInteger)value __attribute__((availability(ios,introduced=2.0)));
+@end
+
+NSInteger *inoutIntegerValueGlobal;
+NSInteger *inoutIntegerValueGlobal2;
+NSString *traitNameGlobal;
+static BOOL cond;
+
+static inline void reallyPerformAction(void (^integerHandler)(NSInteger *inoutIntegerValue, NSString *traitName)) {
+  integerHandler(inoutIntegerValueGlobal, traitNameGlobal);
+  integerHandler(inoutIntegerValueGlobal2,traitNameGlobal);
+}
+
+static inline BOOL performAction(NSNumber *(^action)(NSNumber *traitValue)) {
+  __attribute__((__blocks__(byref))) BOOL didFindTrait = 0;
+  reallyPerformAction(^(NSInteger *inoutIntegerValue,NSString *traitName) {
+
+    if (cond) {
+
+      NSNumber *traitValue = @(*inoutIntegerValue);
+
+      NSNumber *newTraitValue = action(traitValue);
+
+      if (traitValue != newTraitValue) {
+        *inoutIntegerValue = newTraitValue ? *inoutIntegerValue : *inoutIntegerValue;
+      }
+      didFindTrait = 1;
+    }
+
+  });
+  return didFindTrait;
+}
+
+void runTest() {
+  __attribute__((__blocks__(byref))) NSNumber *builtinResult = ((NSNumber *)0);
+  BOOL wasBuiltinTrait = performAction(^(NSNumber *traitValue) {
+    builtinResult = [traitValue retain]; // expected-warning {{Potential leak of an object}}
+
+    return traitValue;
+  });
+  if (wasBuiltinTrait) {
+    [builtinResult autorelease];
+    return;
+  } else {
+    return;
+  }
+}