forked from OSchip/llvm-project
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:
parent
f538cfa254
commit
7e90422e95
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
@ -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}}
|
||||
}
|
||||
|
Loading…
Reference in New Issue