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.

llvm-svn: 62106
This commit is contained in:
Ted Kremenek 2009-01-12 21:45:02 +00:00
parent f538cfa254
commit 7e90422e95
5 changed files with 401 additions and 394 deletions

View File

@ -83,50 +83,45 @@ static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
// Type querying functions.
//===----------------------------------------------------------------------===//
static bool isCFRefType(QualType T) {
static bool hasPrefix(const char* s, const char* prefix) {
if (!prefix)
return true;
if (!T->isPointerType())
return false;
char c = *s;
char cP = *prefix;
// Check the typedef for the name "CF" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
while (c != '\0' && cP != '\0') {
if (c != cP) break;
c = *(++s);
cP = *(++prefix);
}
if (!TD)
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'C' || TDName[1] != 'F')
return false;
if (strstr(TDName, "Ref") == 0)
return false;
return true;
return cP == '\0';
}
static bool isCGRefType(QualType T) {
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 (!T->isPointerType())
if (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
const char* TDName = TD->getDecl()->getIdentifier()->getName();
return hasPrefix(TDName, prefix) && hasSuffix(TDName, "Ref");
}
if (!Ctx || !name)
return false;
// Check the typedef for the name "CG" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
if (!TD)
// Is the type void*?
const PointerType* PT = RetTy->getAsPointerType();
if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy))
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'C' || TDName[1] != 'G')
return false;
if (strstr(TDName, "Ref") == 0)
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<FunctionType>(FD->getType());
do {
if (FT) {
QualType T = FT->getResultType();
if (isCFRefType(T)) {
S = getCFSummary(FD, FName);
break;
}
if (isCGRefType(T)) {
S = getCGSummary(FD, FName );
break;
}
// FIXME: This should all be refactored into a chain of "summary lookup"
// filters.
if (strcmp(FName, "IOServiceGetMatchingServices") == 0) {
// FIXES: <rdar://problem/6326900>
// 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;
}
// We generate "stop" summaries for implicitly defined functions.
if (FD->isImplicit()) {
S = getPersistentStopSummary();
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);
FunctionType* FT = cast<FunctionType>(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: <rdar://problem/6326900>
// 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;
}
// For CoreGraphics ('CG') types.
if (isRefType(RetTy, "CG", &Ctx, FName)) {
if (isRetain(FD, FName))
S = getUnarySummary(FT, cfretain);
else
S = getCFCreateGetRuleSummary(FD, FName);
break;
}
// For the Disk Arbitration API (DiskArbitration/DADisk.h)
if (isRefType(RetTy, "DADisk") ||
isRefType(RetTy, "DADissenter") ||
isRefType(RetTy, "DASessionRef")) {
S = getCFCreateGetRuleSummary(FD, FName);
break;
}
break;
}
// 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<FunctionTypeProto>(FD->getType().getTypePtr());
if (FT) {
if (FT->getNumArgs() != 1)
return 0;
TypedefType* ArgT = dyn_cast<TypedefType>(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<FunctionTypeProto>(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<FunctionType>(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<FunctionType>(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);
}

View File

@ -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 <NSObject> {} @end
extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone);
typedef double NSTimeInterval;
@interface NSDate : NSObject <NSCopying, NSCoding> - (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;
}

View File

@ -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;
}

View File

@ -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}}
}

View File

@ -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 <NSObject> {
}
@end typedef float CGFloat;
typedef double NSTimeInterval;
@interface NSDate : NSObject <NSCopying, NSCoding> - (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}}
}