[cfe-commits] r69982 - in /cfe/trunk: lib/Analysis/CFRefCount.cpp test/Analysis/delegates.m

Ted Kremenek kremenek at apple.com
Fri Apr 24 10:50:37 PDT 2009


Author: kremenek
Date: Fri Apr 24 12:50:11 2009
New Revision: 69982

URL: http://llvm.org/viewvc/llvm-project?rev=69982&view=rev
Log:
retain/release checker:
- Fix summary lookup for class methods to now use the (optional)
  ObjCInterfaceDecl associated with a message expression. This removes a
  long-standing FIXME.
- Partial fix for <rdar://problem/6062730> by stop tracking objects that
  are passed to [NSObject performSelector].  These methods are often used
  for delegates, which the analyzer doesn't reason about well yet.

Added:
    cfe/trunk/test/Analysis/delegates.m
Modified:
    cfe/trunk/lib/Analysis/CFRefCount.cpp

Modified: cfe/trunk/lib/Analysis/CFRefCount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFRefCount.cpp?rev=69982&r1=69981&r2=69982&view=diff

==============================================================================
--- cfe/trunk/lib/Analysis/CFRefCount.cpp (original)
+++ cfe/trunk/lib/Analysis/CFRefCount.cpp Fri Apr 24 12:50:11 2009
@@ -659,34 +659,51 @@
     Selector S = GetNullarySelector(nullaryName, Ctx);
     ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)]  = Summ;
   }
-    
-  void addInstMethSummary(const char* Cls, RetainSummary* Summ, va_list argp) {
-    
-    IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
+  
+  Selector generateSelector(va_list argp) {
     llvm::SmallVector<IdentifierInfo*, 10> II;
-    
+
     while (const char* s = va_arg(argp, const char*))
       II.push_back(&Ctx.Idents.get(s));
-    
-    Selector S = Ctx.Selectors.getSelector(II.size(), &II[0]);
-    ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
+
+    return Ctx.Selectors.getSelector(II.size(), &II[0]);    
+  }
+  
+  void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries,
+                        RetainSummary* Summ, va_list argp) {
+    Selector S = generateSelector(argp);
+    Summaries[ObjCSummaryKey(ClsII, S)] = Summ;
   }
   
   void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
     va_list argp;
     va_start(argp, Summ);
-    addInstMethSummary(Cls, Summ, argp);
+    addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
     va_end(argp);    
   }
-          
+  
+  void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) {
+    va_list argp;
+    va_start(argp, Summ);
+    addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp);
+    va_end(argp);
+  }
+  
+  void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) {
+    va_list argp;
+    va_start(argp, Summ);
+    addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp);
+    va_end(argp);
+  }
+
   void addPanicSummary(const char* Cls, ...) {
     RetainSummary* Summ = getPersistentSummary(0, RetEffect::MakeNoRet(),
                                                DoNothing,  DoNothing, true);
     va_list argp;
     va_start (argp, Cls);
-    addInstMethSummary(Cls, Summ, argp);
+    addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
     va_end(argp);
-  }  
+  }
   
 public:
   
@@ -1100,23 +1117,30 @@
 }
 
 RetainSummary*
-RetainSummaryManager::getClassMethodSummary(ObjCMessageExpr* ME) {
-  
-  // FIXME: Eventually we should properly do class method summaries, but
-  // it requires us being able to walk the type hierarchy.  Unfortunately,
-  // we cannot do this with just an IdentifierInfo* for the class name.  
-  IdentifierInfo* ClsName = ME->getClassName();
+RetainSummaryManager::getClassMethodSummary(ObjCMessageExpr *ME) {
+
   Selector S = ME->getSelector();
+  ObjCMethodSummariesTy::iterator I;
   
-  // Look up a summary in our cache of Selectors -> Summaries.
-  ObjCMethodSummariesTy::iterator I = ObjCClassMethodSummaries.find(ClsName, S);
+  if (ObjCInterfaceDecl *ID = ME->getClassInfo().first) {
+    // Lookup the method using the decl for the class @interface.
+    I = ObjCClassMethodSummaries.find(ID, S); 
+  }
+  else {
+    // Fallback to using the class name.
+    IdentifierInfo *ClsName = ME->getClassName();
+  
+    // Look up a summary in our cache of Selectors -> Summaries.
+    I = ObjCClassMethodSummaries.find(ClsName, S);
+  }
   
   if (I != ObjCClassMethodSummaries.end())
     return I->second;
   
   RetainSummary* Summ =
     getCommonMethodSummary(ME, S.getIdentifierInfoForSlot(0)->getName());
-  ObjCClassMethodSummaries[ObjCSummaryKey(ClsName, S)] = Summ;
+
+  ObjCClassMethodSummaries[ObjCSummaryKey(ME->getClassName(), S)] = Summ;
   return Summ;
 }
 
@@ -1146,6 +1170,28 @@
                     GetUnarySelector("addObject", Ctx),
                     getPersistentSummary(RetEffect::MakeNoRet(),
                                          DoNothing, Autorelease));
+  
+  // Create the summaries for [NSObject performSelector...].  We treat
+  // these as 'stop tracking' for the arguments because they are often
+  // used for delegates that can release the object.  When we have better
+  // inter-procedural analysis we can potentially do something better.  This
+  // workaround is to remove false positives.
+  Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
+  IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
+  addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
+                    "afterDelay", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
+                    "afterDelay", "inModes", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
+                    "withObject", "waitUntilDone", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
+                    "withObject", "waitUntilDone", "modes", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
+                    "withObject", "waitUntilDone", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
+                    "withObject", "waitUntilDone", "modes", NULL);
+  addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground",
+                    "withObject", NULL);
 }
 
 void RetainSummaryManager::InitializeMethodSummaries() {
@@ -2012,6 +2058,7 @@
   else
     Summ = Summaries.getClassMethodSummary(ME);
 
+
   EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
               ME->arg_begin(), ME->arg_end(), Pred);
 }

Added: cfe/trunk/test/Analysis/delegates.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/delegates.m?rev=69982&view=auto

==============================================================================
--- cfe/trunk/test/Analysis/delegates.m (added)
+++ cfe/trunk/test/Analysis/delegates.m Fri Apr 24 12:50:11 2009
@@ -0,0 +1,114 @@
+// RUN: clang-cc -analyze -checker-cfref -analyzer-store=basic -verify %s &&
+// RUN: clang-cc -analyze -checker-cfref -analyzer-store=region -verify %s
+
+
+//===----------------------------------------------------------------------===//
+// The following code is reduced using delta-debugging from
+// Foundation.h (Mac OS X).
+//
+// It includes the basic definitions for the test cases below.
+// Not directly including Foundation.h directly makes this test case 
+// both svelte and portable to non-Mac platforms.
+//===----------------------------------------------------------------------===//
+
+typedef const void * CFTypeRef;
+typedef const struct __CFString * CFStringRef;
+typedef const struct __CFAllocator * CFAllocatorRef;
+extern const CFAllocatorRef kCFAllocatorDefault;
+extern CFTypeRef CFRetain(CFTypeRef cf);
+void CFRelease(CFTypeRef cf);
+typedef const struct __CFDictionary * CFDictionaryRef;
+const void *CFDictionaryGetValue(CFDictionaryRef theDict, const void *key);
+extern CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, ...);
+typedef signed char BOOL;
+typedef int NSInteger;
+typedef unsigned int NSUInteger;
+typedef struct objc_selector *SEL;
+ at class NSString, Protocol;
+extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
+typedef NSInteger NSComparisonResult;
+typedef struct _NSZone NSZone;
+ at class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+ at protocol NSObject
+- (BOOL)isEqual:(id)object;
+- (oneway void)release;
+- (Class)class;
+- (id)retain;
+ at end
+ at protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+ at end
+ at protocol NSMutableCopying
+- (id)mutableCopyWithZone:(NSZone *)zone;
+ at end
+ at protocol NSCoding
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+ at end
+ at interface NSObject <NSObject> {}
+- (id)init;
++ (id)alloc;
++ (Class)class;
+- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
+ at end
+extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone);
+typedef struct {} NSFastEnumerationState;
+ at protocol NSFastEnumeration
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
+ at end
+ at class NSString;
+typedef struct _NSRange {} NSRange;
+ at interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
+ at end
+ at interface NSMutableArray : NSArray
+- (void)addObject:(id)anObject;
+- (id)initWithCapacity:(NSUInteger)numItems;
+ at end
+typedef unsigned short unichar;
+ at class NSData, NSArray, NSDictionary, NSCharacterSet, NSData, NSURL, NSError, NSLocale;
+typedef NSUInteger NSStringCompareOptions;
+ at interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>    - (NSUInteger)length;
+- (NSComparisonResult)compare:(NSString *)string;
+- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask;
+- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)compareRange;
+- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)compareRange locale:(id)locale;
+- (NSComparisonResult)caseInsensitiveCompare:(NSString *)string;
+- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator;
+ at end
+ at interface NSSimpleCString : NSString {} @end
+ at interface NSConstantString : NSSimpleCString @end
+extern void *_NSConstantStringClassReference;
+
+//===----------------------------------------------------------------------===//
+// Test cases.
+//===----------------------------------------------------------------------===//
+
+//  <rdar://problem/6062730>
+// The analyzer doesn't perform any inter-procedural analysis, so delegates
+// involving [NSObject performSelector...] tend to lead to false positives.
+// For now the analyzer just stops tracking the reference count of the
+// receiver until we have better support for delegates.
+
+ at interface test_6062730 : NSObject
++ (void)postNotification:(NSString *)str;
+- (void)foo;
+- (void)bar;
+ at end
+
+ at implementation test_6062730
+- (void) foo {
+  NSString *str = [[NSString alloc] init];
+  [test_6062730 performSelectorOnMainThread:@selector(postNotification:) withObject:str waitUntilDone:1];
+}
+
+- (void) bar {
+  NSString *str = [[NSString alloc] init]; // expected-warning{{leak}}
+  // FIXME: We need to resolve [self class] to 'test_6062730'.
+  [[self class] performSelectorOnMainThread:@selector(postNotification:) withObject:str waitUntilDone:1];
+}
+
++ (void) postNotification:(NSString *)str {
+  [str release]; // no-warning
+}
+ at end
+





More information about the cfe-commits mailing list