From 1261938ec42b0a1b82bec5fe901b7fc02a23d9a1 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 12 Jan 2009 21:45:02 +0000 Subject: retain/release checker: - Refactor a bunch of logic in the retain/release checker, making it more condense and easier to read. - Add support for "Create" methods in the DiskArbitration framework retain/release tests: - Rename CFDate.m to retain-release.m, and move test from CFString.c to retain-release.m - Add DiskArbitration framework tests cases. - Add/refine and few more retain/release GC test cases. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62106 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/CFRefCount.cpp | 300 +++++++++++++++++------------------------ test/Analysis/CFDate.m | 143 -------------------- test/Analysis/CFDateGC.m | 41 ++++-- test/Analysis/CFString.c | 60 --------- test/Analysis/retain-release.m | 239 ++++++++++++++++++++++++++++++++ 5 files changed, 395 insertions(+), 388 deletions(-) delete mode 100644 test/Analysis/CFDate.m delete mode 100644 test/Analysis/CFString.c create mode 100644 test/Analysis/retain-release.m diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index b96b8cce36..d2d7ab8d04 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -83,50 +83,45 @@ static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) { // Type querying functions. //===----------------------------------------------------------------------===// -static bool isCFRefType(QualType T) { - - if (!T->isPointerType()) - return false; - - // Check the typedef for the name "CF" and the substring "Ref". - TypedefType* TD = dyn_cast(T.getTypePtr()); - - if (!TD) - return false; - - const char* TDName = TD->getDecl()->getIdentifier()->getName(); - assert (TDName); +static bool hasPrefix(const char* s, const char* prefix) { + if (!prefix) + return true; - if (TDName[0] != 'C' || TDName[1] != 'F') - return false; + char c = *s; + char cP = *prefix; - if (strstr(TDName, "Ref") == 0) - return false; + while (c != '\0' && cP != '\0') { + if (c != cP) break; + c = *(++s); + cP = *(++prefix); + } - return true; + return cP == '\0'; } -static bool isCGRefType(QualType T) { - - if (!T->isPointerType()) - return false; - - // Check the typedef for the name "CG" and the substring "Ref". - TypedefType* TD = dyn_cast(T.getTypePtr()); - - if (!TD) - return false; - - const char* TDName = TD->getDecl()->getIdentifier()->getName(); - assert (TDName); +static bool hasSuffix(const char* s, const char* suffix) { + const char* loc = strstr(s, suffix); + return loc && strcmp(suffix, loc) == 0; +} + +static bool isRefType(QualType RetTy, const char* prefix, + ASTContext* Ctx = 0, const char* name = 0) { - if (TDName[0] != 'C' || TDName[1] != 'G') + if (TypedefType* TD = dyn_cast(RetTy.getTypePtr())) { + const char* TDName = TD->getDecl()->getIdentifier()->getName(); + return hasPrefix(TDName, prefix) && hasSuffix(TDName, "Ref"); + } + + if (!Ctx || !name) return false; - - if (strstr(TDName, "Ref") == 0) + + // Is the type void*? + const PointerType* PT = RetTy->getAsPointerType(); + if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy)) return false; - - return true; + + // Does the name start with the prefix? + return hasPrefix(name, prefix); } //===----------------------------------------------------------------------===// @@ -507,15 +502,11 @@ class VISIBILITY_HIDDEN RetainSummaryManager { enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; public: - RetainSummary* getUnarySummary(FunctionDecl* FD, UnaryFuncKind func); - - RetainSummary* getNSSummary(FunctionDecl* FD, const char* FName); - RetainSummary* getCFSummary(FunctionDecl* FD, const char* FName); - RetainSummary* getCGSummary(FunctionDecl* FD, const char* FName); + RetainSummary* getUnarySummary(FunctionType* FT, UnaryFuncKind func); RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD); RetainSummary* getCFSummaryGetRule(FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName); + RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName); RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff, ArgEffect ReceiverEff = DoNothing, @@ -713,6 +704,16 @@ bool RetainSummaryManager::isTrackedObjectType(QualType T) { // Summary creation for functions (largely uses of Core Foundation). //===----------------------------------------------------------------------===// +static bool isRetain(FunctionDecl* FD, const char* FName) { + const char* loc = strstr(FName, "Retain"); + return loc && loc[sizeof("Retain")-1] == '\0'; +} + +static bool isRelease(FunctionDecl* FD, const char* FName) { + const char* loc = strstr(FName, "Release"); + return loc && loc[sizeof("Release")-1] == '\0'; +} + RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { SourceLocation Loc = FD->getLocation(); @@ -727,50 +728,90 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { return I->second; // No summary. Generate one. - const char* FName = FD->getIdentifier()->getName(); + RetainSummary *S = 0; - RetainSummary *S = 0; - FunctionType* FT = dyn_cast(FD->getType()); - do { - if (FT) { - - QualType T = FT->getResultType(); - - if (isCFRefType(T)) { - S = getCFSummary(FD, FName); + // We generate "stop" summaries for implicitly defined functions. + if (FD->isImplicit()) { + S = getPersistentStopSummary(); + break; + } + + FunctionType* FT = cast(FD->getType()); + const char* FName = FD->getIdentifier()->getName(); + + // Inspect the result type. + QualType RetTy = FT->getResultType(); + + // FIXME: This should all be refactored into a chain of "summary lookup" + // filters. + if (strcmp(FName, "IOServiceGetMatchingServices") == 0) { + // FIXES: + // This should be addressed using a API table. This strcmp is also + // a little gross, but there is no need to super optimize here. + assert (ScratchArgs.empty()); + ScratchArgs.push_back(std::make_pair(1, DecRef)); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + break; + } + + // Handle: id NSMakeCollectable(CFTypeRef) + if (strcmp(FName, "NSMakeCollectable") == 0) { + S = (RetTy == Ctx.getObjCIdType()) + ? getUnarySummary(FT, cfmakecollectable) + : getPersistentStopSummary(); + + break; + } + + if (RetTy->isPointerType()) { + // For CoreFoundation ('CF') types. + if (isRefType(RetTy, "CF", &Ctx, FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else if (strstr(FName, "MakeCollectable")) + S = getUnarySummary(FT, cfmakecollectable); + else + S = getCFCreateGetRuleSummary(FD, FName); + break; } - - if (isCGRefType(T)) { - S = getCGSummary(FD, FName ); + + // For CoreGraphics ('CG') types. + if (isRefType(RetTy, "CG", &Ctx, FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else + S = getCFCreateGetRuleSummary(FD, FName); + break; } - - // FIXME: This should all be refactored into a chain of "summary lookup" - // filters. - if (strcmp(FName, "IOServiceGetMatchingServices") == 0) { - // FIXES: - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - assert (ScratchArgs.empty()); - ScratchArgs.push_back(std::make_pair(1, DecRef)); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + + // For the Disk Arbitration API (DiskArbitration/DADisk.h) + if (isRefType(RetTy, "DADisk") || + isRefType(RetTy, "DADissenter") || + isRefType(RetTy, "DASessionRef")) { + S = getCFCreateGetRuleSummary(FD, FName); break; } + + break; } - - // Ignore the prefix '_' - while (*FName == '_') ++FName; - if (FName[0] == 'C') { - if (FName[1] == 'F') - S = getCFSummary(FD, FName); - else if (FName[1] == 'G') - S = getCGSummary(FD, FName); + // Check for release functions, the only kind of functions that we care + // about that don't return a pointer type. + if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { + if (isRelease(FD, FName+2)) + S = getUnarySummary(FT, cfrelease); + else { + // For CoreFoundation and CoreGraphics functions we assume they + // follow the ownership idiom strictly and thus do not cause + // ownership to "escape". + assert (ScratchArgs.empty()); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, + DoNothing); + } } - else if (FName[0] == 'N' && FName[1] == 'S') - S = getNSSummary(FD, FName); } while (0); @@ -778,58 +819,6 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { return S; } -RetainSummary* RetainSummaryManager::getNSSummary(FunctionDecl* FD, - const char* FName) { - FName += 2; - - if (strcmp(FName, "MakeCollectable") == 0) - return getUnarySummary(FD, cfmakecollectable); - - return 0; -} - -static bool isRetain(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Retain"); - return loc && loc[sizeof("Retain")-1] == '\0'; -} - -static bool isRelease(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Release"); - return loc && loc[sizeof("Release")-1] == '\0'; -} - -RetainSummary* RetainSummaryManager::getCFSummary(FunctionDecl* FD, - const char* FName) { - if (FName[0] == 'C' && FName[1] == 'F') - FName += 2; - - if (isRetain(FD, FName)) - return getUnarySummary(FD, cfretain); - - if (isRelease(FD, FName)) - return getUnarySummary(FD, cfrelease); - - if (strcmp(FName, "MakeCollectable") == 0) - return getUnarySummary(FD, cfmakecollectable); - - return getCFCreateGetRuleSummary(FD, FName); -} - -RetainSummary* RetainSummaryManager::getCGSummary(FunctionDecl* FD, - const char* FName) { - - if (FName[0] == 'C' && FName[1] == 'G') - FName += 2; - - if (isRelease(FD, FName)) - return getUnarySummary(FD, cfrelease); - - if (isRetain(FD, FName)) - return getUnarySummary(FD, cfretain); - - return getCFCreateGetRuleSummary(FD, FName); -} - RetainSummary* RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName) { @@ -844,29 +833,17 @@ RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, } RetainSummary* -RetainSummaryManager::getUnarySummary(FunctionDecl* FD, UnaryFuncKind func) { - - FunctionTypeProto* FT = - dyn_cast(FD->getType().getTypePtr()); - - if (FT) { - - if (FT->getNumArgs() != 1) - return 0; - - TypedefType* ArgT = dyn_cast(FT->getArgType(0).getTypePtr()); - - if (!ArgT) - return 0; - - if (!ArgT->isPointerType()) - return NULL; - } +RetainSummaryManager::getUnarySummary(FunctionType* FT, UnaryFuncKind func) { + // Sanity check that this is *really* a unary function. This can + // happen if people do weird things. + FunctionTypeProto* FTP = dyn_cast(FT); + if (!FTP || FTP->getNumArgs() != 1) + return getPersistentStopSummary(); assert (ScratchArgs.empty()); switch (func) { - case cfretain: { + case cfretain: { ScratchArgs.push_back(std::make_pair(0, IncRef)); return getPersistentSummary(RetEffect::MakeAlias(0), DoNothing, DoNothing); @@ -893,17 +870,6 @@ RetainSummaryManager::getUnarySummary(FunctionDecl* FD, UnaryFuncKind func) { } RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { - - FunctionType* FT = - dyn_cast(FD->getType().getTypePtr()); - - if (FT) { - QualType ResTy = FT->getResultType(); - - if (!isCFRefType(ResTy) && !isCGRefType(ResTy)) - return getPersistentSummary(RetEffect::MakeNoRet()); - } - assert (ScratchArgs.empty()); if (FD->getIdentifier() == CFDictionaryCreateII) { @@ -915,26 +881,6 @@ RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { } RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) { - - FunctionType* FT = - dyn_cast(FD->getType().getTypePtr()); - - if (FT) { - QualType RetTy = FT->getResultType(); - - // FIXME: For now we assume that all pointer types returned are referenced - // counted. Since this is the "Get" rule, we assume non-ownership, which - // works fine for things that are not reference counted. We do this because - // some generic data structures return "void*". We need something better - // in the future. - - if (!isCFRefType(RetTy) && !RetTy->isPointerType()) - return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - - // FIXME: Add special-cases for functions that retain/release. For now - // just handle the default case. - assert (ScratchArgs.empty()); return getPersistentSummary(RetEffect::MakeNotOwned(), DoNothing, DoNothing); } diff --git a/test/Analysis/CFDate.m b/test/Analysis/CFDate.m deleted file mode 100644 index ed076c680b..0000000000 --- a/test/Analysis/CFDate.m +++ /dev/null @@ -1,143 +0,0 @@ -// RUN: clang -checker-cfref -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 including Foundation.h directly makes this test case both svelte and -// portable to non-Mac platforms. -//===----------------------------------------------------------------------===// - -typedef const struct __CFAllocator * CFAllocatorRef; -typedef double CFTimeInterval; -typedef CFTimeInterval CFAbsoluteTime; -typedef const struct __CFDate * CFDateRef; -extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); -typedef struct objc_object {} *id; -typedef signed char BOOL; -typedef unsigned int NSUInteger; -typedef struct _NSZone NSZone; -@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; -@protocol NSObject - (BOOL)isEqual:(id)object; - (id)retain; - (oneway void)release; @end -@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end -@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end -@interface NSObject {} @end -extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); -typedef double NSTimeInterval; -@interface NSDate : NSObject - (NSTimeInterval)timeIntervalSinceReferenceDate; @end -@class NSString, NSArray, NSTimeZone; - -//===----------------------------------------------------------------------===// -// Test cases. -//===----------------------------------------------------------------------===// - -CFAbsoluteTime f1() { - CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); - CFDateRef date = CFDateCreate(0, t); - CFRetain(date); - CFRelease(date); - CFDateGetAbsoluteTime(date); // no-warning - CFRelease(date); - t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}} - return t; -} - -CFAbsoluteTime f2() { - CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); - CFDateRef date = CFDateCreate(0, t); - [((NSDate*) date) retain]; - CFRelease(date); - CFDateGetAbsoluteTime(date); // no-warning - [((NSDate*) date) release]; - t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}} - return t; -} - - -NSDate* global_x; - - // Test to see if we supresss an error when we store the pointer - // to a global. - -CFAbsoluteTime f3() { - CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); - CFDateRef date = CFDateCreate(0, t); - [((NSDate*) date) retain]; - CFRelease(date); - CFDateGetAbsoluteTime(date); // no-warning - global_x = (NSDate*) date; - [((NSDate*) date) release]; - t = CFDateGetAbsoluteTime(date); // no-warning - return t; -} - -// Test to see if we supresss an error when we store the pointer -// to a struct. - -struct foo { - NSDate* f; -}; - -CFAbsoluteTime f4() { - struct foo x; - - CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); - CFDateRef date = CFDateCreate(0, t); - [((NSDate*) date) retain]; - CFRelease(date); - CFDateGetAbsoluteTime(date); // no-warning - x.f = (NSDate*) date; - [((NSDate*) date) release]; - t = CFDateGetAbsoluteTime(date); // no-warning - return t; -} - -// Test a leak. - -CFAbsoluteTime f5(int x) { - CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); - CFDateRef date = CFDateCreate(0, t); - - if (x) - CFRelease(date); - - return t; // expected-warning{{leak}} -} - -// Test a leak involving the return. - -CFDateRef f6(int x) { - CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); - CFRetain(date); - return date; // expected-warning{{leak}} -} - -// Test a leak involving an overwrite. - -CFDateRef f7() { - CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); - CFRetain(date); //expected-warning{{leak}} - date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); - return date; -} - -// Generalization of Create rule. MyDateCreate returns a CFXXXTypeRef, and -// has the word create. - -CFDateRef MyDateCreate(); - -CFDateRef f8() { - CFDateRef date = MyDateCreate(); - CFRetain(date); - return date; // expected-warning{{leak}} -} - -CFDateRef f9() { - CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); - int *p = 0; - // test that the checker assumes that CFDateCreate returns a non-null - // pointer - if (!date) *p = 1; // no-warning - return date; -} diff --git a/test/Analysis/CFDateGC.m b/test/Analysis/CFDateGC.m index 311371399a..04d2209a3c 100644 --- a/test/Analysis/CFDateGC.m +++ b/test/Analysis/CFDateGC.m @@ -10,24 +10,28 @@ //===----------------------------------------------------------------------===// typedef const void * CFTypeRef; +void CFRelease(CFTypeRef cf); +CFTypeRef CFRetain(CFTypeRef cf); +CFTypeRef CFMakeCollectable(CFTypeRef cf); typedef const struct __CFAllocator * CFAllocatorRef; typedef double CFTimeInterval; typedef CFTimeInterval CFAbsoluteTime; typedef const struct __CFDate * CFDateRef; extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); +extern CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate); +typedef struct objc_object {} *id; typedef signed char BOOL; -typedef unsigned int NSUInteger; -typedef struct _NSZone NSZone; static __inline__ __attribute__((always_inline)) id NSMakeCollectable(CFTypeRef cf) {} -@protocol NSObject - (BOOL)isEqual:(id)object; - (oneway void)release; @end -extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); -CFTypeRef CFMakeCollectable(CFTypeRef cf); +@protocol NSObject - (BOOL)isEqual:(id)object; +- (oneway void)release; +@end +@class NSArray; //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// -CFAbsoluteTime f1() { +CFAbsoluteTime f1_use_after_release() { CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); CFDateRef date = CFDateCreate(0, t); CFRetain(date); @@ -38,7 +42,20 @@ CFAbsoluteTime f1() { return t; } -CFAbsoluteTime f1b() { +// The following two test cases verifies that CFMakeCollectable is a no-op +// in non-GC mode and a "release" in GC mode. +CFAbsoluteTime f2_leak() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + CFRetain(date); + [(id) CFMakeCollectable(date) release]; + CFDateGetAbsoluteTime(date); // no-warning + CFRelease(date); + t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}} + return t; +} + +CFAbsoluteTime f2_noleak() { CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); CFDateRef date = CFDateCreate(0, t); CFRetain(date); @@ -49,4 +66,12 @@ CFAbsoluteTime f1b() { return t; } - +// The following test case verifies that we "stop tracking" a retained object +// when it is passed as an argument to an implicitly defined function. +CFAbsoluteTime f4() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + CFRetain(date); + some_implicitly_defined_function_stop_tracking(date); // no-warning + return t; +} diff --git a/test/Analysis/CFString.c b/test/Analysis/CFString.c deleted file mode 100644 index cb53bb1c66..0000000000 --- a/test/Analysis/CFString.c +++ /dev/null @@ -1,60 +0,0 @@ -// RUN: clang -checker-cfref -pedantic -verify %s - -//===----------------------------------------------------------------------===// -// The following code is reduced using delta-debugging from -// CoreFoundation.h (Mac OS X). -// -// It includes the basic definitions for the test cases below. -// Not directly including CoreFoundation.h directly makes this test case -// both svelte and portable to non-Mac platforms. -//===----------------------------------------------------------------------===// - -typedef unsigned long UInt32; -typedef unsigned char Boolean; -typedef signed long CFIndex; -typedef const void * CFTypeRef; -typedef const struct __CFString * CFStringRef; -typedef struct { CFIndex location; } CFRange; -typedef const struct __CFAllocator * CFAllocatorRef; -extern void CFRelease(CFTypeRef cf); -typedef Boolean (*CFArrayEqualCallBack)(const void *value1, const void *value2); -typedef struct { CFArrayEqualCallBack equal; } CFArrayCallBacks; -extern const CFArrayCallBacks kCFTypeArrayCallBacks; -typedef const struct __CFArray * CFArrayRef; -typedef struct __CFArray * CFMutableArrayRef; -extern CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks); -extern const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); -extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value); -typedef UInt32 CFStringEncoding; -enum { kCFStringEncodingMacRoman = 0, kCFStringEncodingWindowsLatin1 = 0x0500, kCFStringEncodingISOLatin1 = 0x0201, kCFStringEncodingNextStepLatin = 0x0B01, kCFStringEncodingASCII = 0x0600, kCFStringEncodingUnicode = 0x0100, kCFStringEncodingUTF8 = 0x08000100, kCFStringEncodingNonLossyASCII = 0x0BFF , kCFStringEncodingUTF16 = 0x0100, kCFStringEncodingUTF16BE = 0x10000100, kCFStringEncodingUTF16LE = 0x14000100, kCFStringEncodingUTF32 = 0x0c000100, kCFStringEncodingUTF32BE = 0x18000100, kCFStringEncodingUTF32LE = 0x1c000100 }; -extern CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); - -//===----------------------------------------------------------------------===// -// Test cases. -//===----------------------------------------------------------------------===// - -void f1() { - - // Create the array. - CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); - - // Create a string. - CFStringRef s1 = CFStringCreateWithCString(0, "hello world", - kCFStringEncodingUTF8); - - // Add the string to the array. - CFArrayAppendValue(A, s1); - - // Decrement the reference count. - CFRelease(s1); // no-warning - - // Get the string. We don't own it. - s1 = (CFStringRef) CFArrayGetValueAtIndex(A, 0); - - // Release the array. - CFRelease(A); // no-warning - - // Release the string. This is a bug. - CFRelease(s1); // expected-warning{{Incorrect decrement of the reference count}} -} - diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m new file mode 100644 index 0000000000..b548f2d26b --- /dev/null +++ b/test/Analysis/retain-release.m @@ -0,0 +1,239 @@ +// RUN: clang -checker-cfref -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 including Foundation.h directly makes this test case both svelte and +// portable to non-Mac platforms. +//===----------------------------------------------------------------------===// + +typedef unsigned int __darwin_natural_t; +typedef unsigned long UInt32; +typedef signed long CFIndex; +typedef const void * CFTypeRef; +typedef const struct __CFString * CFStringRef; +typedef const struct __CFAllocator * CFAllocatorRef; +extern const CFAllocatorRef kCFAllocatorDefault; +extern CFTypeRef CFRetain(CFTypeRef cf); +extern void CFRelease(CFTypeRef cf); +typedef struct { +} +CFArrayCallBacks; +extern const CFArrayCallBacks kCFTypeArrayCallBacks; +typedef const struct __CFArray * CFArrayRef; +typedef struct __CFArray * CFMutableArrayRef; +extern CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks); +extern const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); +typedef const struct __CFDictionary * CFDictionaryRef; +typedef UInt32 CFStringEncoding; +enum { +kCFStringEncodingMacRoman = 0, kCFStringEncodingWindowsLatin1 = 0x0500, kCFStringEncodingISOLatin1 = 0x0201, kCFStringEncodingNextStepLatin = 0x0B01, kCFStringEncodingASCII = 0x0600, kCFStringEncodingUnicode = 0x0100, kCFStringEncodingUTF8 = 0x08000100, kCFStringEncodingNonLossyASCII = 0x0BFF , kCFStringEncodingUTF16 = 0x0100, kCFStringEncodingUTF16BE = 0x10000100, kCFStringEncodingUTF16LE = 0x14000100, kCFStringEncodingUTF32 = 0x0c000100, kCFStringEncodingUTF32BE = 0x18000100, kCFStringEncodingUTF32LE = 0x1c000100 }; +extern CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); +typedef double CFTimeInterval; +typedef CFTimeInterval CFAbsoluteTime; +typedef const struct __CFDate * CFDateRef; +extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); +extern CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate); +typedef __darwin_natural_t natural_t; +typedef natural_t mach_port_name_t; +typedef mach_port_name_t mach_port_t; +typedef signed char BOOL; +typedef struct _NSZone NSZone; +@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; +@protocol NSObject - (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; +@end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; +@end @interface NSObject { +} +@end typedef float CGFloat; +typedef double NSTimeInterval; +@interface NSDate : NSObject - (NSTimeInterval)timeIntervalSinceReferenceDate; +@end enum { +NSObjCNoType = 0, NSObjCVoidType = 'v', NSObjCCharType = 'c', NSObjCShortType = 's', NSObjCLongType = 'l', NSObjCLonglongType = 'q', NSObjCFloatType = 'f', NSObjCDoubleType = 'd', NSObjCBoolType = 'B', NSObjCSelectorType = ':', NSObjCObjectType = '@', NSObjCStructType = '{', NSObjCPointerType = '^', NSObjCStringType = '*', NSObjCArrayType = '[', NSObjCUnionType = '(', NSObjCBitfield = 'b' } +__attribute__((deprecated)); +typedef int kern_return_t; +typedef kern_return_t mach_error_t; +typedef mach_port_t io_object_t; +typedef io_object_t io_service_t; +typedef struct __DASession * DASessionRef; +extern DASessionRef DASessionCreate( CFAllocatorRef allocator ); +typedef struct __DADisk * DADiskRef; +extern DADiskRef DADiskCreateFromBSDName( CFAllocatorRef allocator, DASessionRef session, const char * name ); +extern DADiskRef DADiskCreateFromIOMedia( CFAllocatorRef allocator, DASessionRef session, io_service_t media ); +extern CFDictionaryRef DADiskCopyDescription( DADiskRef disk ); +extern DADiskRef DADiskCopyWholeDisk( DADiskRef disk ); +@interface NSAppleEventManager : NSObject { +} +@end enum { +kDAReturnSuccess = 0, kDAReturnError = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x01, kDAReturnBusy = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x02, kDAReturnBadArgument = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x03, kDAReturnExclusiveAccess = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x04, kDAReturnNoResources = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x05, kDAReturnNotFound = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x06, kDAReturnNotMounted = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x07, kDAReturnNotPermitted = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x08, kDAReturnNotPrivileged = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x09, kDAReturnNotReady = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0A, kDAReturnNotWritable = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0B, kDAReturnUnsupported = (((0x3e)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0C }; +typedef mach_error_t DAReturn; +typedef const struct __DADissenter * DADissenterRef; +extern DADissenterRef DADissenterCreate( CFAllocatorRef allocator, DAReturn status, CFStringRef string ); + +//===----------------------------------------------------------------------===// +// Test cases. +//===----------------------------------------------------------------------===// + +CFAbsoluteTime f1() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + CFRetain(date); + CFRelease(date); + CFDateGetAbsoluteTime(date); // no-warning + CFRelease(date); + t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}} + return t; +} + +CFAbsoluteTime f2() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + [((NSDate*) date) retain]; + CFRelease(date); + CFDateGetAbsoluteTime(date); // no-warning + [((NSDate*) date) release]; + t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}} + return t; +} + + +NSDate* global_x; + +// Test to see if we supresss an error when we store the pointer +// to a global. + +CFAbsoluteTime f3() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + [((NSDate*) date) retain]; + CFRelease(date); + CFDateGetAbsoluteTime(date); // no-warning + global_x = (NSDate*) date; + [((NSDate*) date) release]; + t = CFDateGetAbsoluteTime(date); // no-warning + return t; +} + +// Test to see if we supresss an error when we store the pointer +// to a struct. + +struct foo { + NSDate* f; +}; + +CFAbsoluteTime f4() { + struct foo x; + + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + [((NSDate*) date) retain]; + CFRelease(date); + CFDateGetAbsoluteTime(date); // no-warning + x.f = (NSDate*) date; + [((NSDate*) date) release]; + t = CFDateGetAbsoluteTime(date); // no-warning + return t; +} + +// Test a leak. + +CFAbsoluteTime f5(int x) { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + + if (x) + CFRelease(date); + + return t; // expected-warning{{leak}} +} + +// Test a leak involving the return. + +CFDateRef f6(int x) { + CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + CFRetain(date); + return date; // expected-warning{{leak}} +} + +// Test a leak involving an overwrite. + +CFDateRef f7() { + CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + CFRetain(date); //expected-warning{{leak}} + date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + return date; +} + +// Generalization of Create rule. MyDateCreate returns a CFXXXTypeRef, and +// has the word create. +CFDateRef MyDateCreate(); + +CFDateRef f8() { + CFDateRef date = MyDateCreate(); + CFRetain(date); + return date; // expected-warning{{leak}} +} + +CFDateRef f9() { + CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + int *p = 0; + // test that the checker assumes that CFDateCreate returns a non-null + // pointer + if (!date) *p = 1; // no-warning + return date; +} + +// Handle DiskArbitration API: +// +// http://developer.apple.com/DOCUMENTATION/DARWIN/Reference/DiscArbitrationFramework/ +// +void f10(io_service_t media, DADiskRef d, CFStringRef s) { + DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, 0, "hello"); + if (disk) NSLog(@"ok"); // expected-warning{{leak}} + + disk = DADiskCreateFromIOMedia(kCFAllocatorDefault, 0, media); + if (disk) NSLog(@"ok"); // expected-warning{{leak}} + + CFDictionaryRef dict = DADiskCopyDescription(d); + if (dict) NSLog(@"ok"); // expected-warning{{leak}} + + disk = DADiskCopyWholeDisk(d); + if (disk) NSLog(@"ok"); // expected-warning{{leak}} + + DADissenterRef dissenter = DADissenterCreate(kCFAllocatorDefault, + kDAReturnSuccess, s); + if (dissenter) NSLog(@"ok"); // expected-warning{{leak}} + + DASessionRef session = DASessionCreate(kCFAllocatorDefault); + if (session) NSLog(@"ok"); // expected-warning{{leak}} +} + +// Test retain/release checker with CFString and CFMutableArray. +void f11() { + // Create the array. + CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); + + // Create a string. + CFStringRef s1 = CFStringCreateWithCString(0, "hello world", + kCFStringEncodingUTF8); + + // Add the string to the array. + CFArrayAppendValue(A, s1); + + // Decrement the reference count. + CFRelease(s1); // no-warning + + // Get the string. We don't own it. + s1 = (CFStringRef) CFArrayGetValueAtIndex(A, 0); + + // Release the array. + CFRelease(A); // no-warning + + // Release the string. This is a bug. + CFRelease(s1); // expected-warning{{Incorrect decrement of the reference count}} +} + -- cgit v1.2.3-18-g5258