[analyzer] RetainCountChecker: add support for CFAutorelease.

<rdar://problems/13710586&13710643>

llvm-svn: 192113
This commit is contained in:
Jordan Rose 2013-10-07 17:16:52 +00:00
parent 027dfade54
commit 7741132f47
3 changed files with 2016 additions and 1091 deletions

View File

@ -550,7 +550,7 @@ class RetainSummaryManager {
/// data in ScratchArgs.
ArgEffects getArgEffects();
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable };
const RetainSummary *getUnarySummary(const FunctionType* FT,
UnaryFuncKind func);
@ -804,6 +804,10 @@ static bool isRelease(const FunctionDecl *FD, StringRef FName) {
return FName.endswith("Release");
}
static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
return FName.endswith("Autorelease");
}
static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
// FIXME: Remove FunctionDecl parameter.
// FIXME: Is it really okay if MakeCollectable isn't a suffix?
@ -1063,12 +1067,19 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
if (RetTy->isPointerType()) {
// For CoreFoundation ('CF') types.
if (cocoa::isRefType(RetTy, "CF", FName)) {
if (isRetain(FD, FName))
if (isRetain(FD, FName)) {
S = getUnarySummary(FT, cfretain);
else if (isMakeCollectable(FD, FName))
} else if (isAutorelease(FD, FName)) {
S = getUnarySummary(FT, cfautorelease);
// The headers use cf_consumed, but we can fully model CFAutorelease
// ourselves.
AllowAnnotations = false;
} else if (isMakeCollectable(FD, FName)) {
S = getUnarySummary(FT, cfmakecollectable);
else
AllowAnnotations = false;
} else {
S = getCFCreateGetRuleSummary(FD);
}
break;
}
@ -1171,9 +1182,10 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
ArgEffect Effect;
switch (func) {
case cfretain: Effect = IncRef; break;
case cfrelease: Effect = DecRef; break;
case cfmakecollectable: Effect = MakeCollectable; break;
case cfretain: Effect = IncRef; break;
case cfrelease: Effect = DecRef; break;
case cfautorelease: Effect = Autorelease; break;
case cfmakecollectable: Effect = MakeCollectable; break;
}
ScratchArgs = AF.add(ScratchArgs, 0, Effect);
@ -3104,11 +3116,13 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
canEval = II->isStr("NSMakeCollectable");
} else if (ResultTy->isPointerType()) {
// Handle: (CF|CG)Retain
// CFAutorelease
// CFMakeCollectable
// It's okay to be a little sloppy here (CGMakeCollectable doesn't exist).
if (cocoa::isRefType(ResultTy, "CF", FName) ||
cocoa::isRefType(ResultTy, "CG", FName)) {
canEval = isRetain(FD, FName) || isMakeCollectable(FD, FName);
canEval = isRetain(FD, FName) || isAutorelease(FD, FName) ||
isMakeCollectable(FD, FName);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -63,9 +63,12 @@ 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);
extern CFTypeRef CFMakeCollectable(CFTypeRef cf);
extern CFTypeRef CFAutorelease(CFTypeRef CF_CONSUMED cf);
typedef struct {
}
CFArrayCallBacks;
@ -2004,6 +2007,87 @@ static int Cond;
}
@end
//===----------------------------------------------------------------------===//
// CFAutorelease
//===----------------------------------------------------------------------===//
CFTypeRef getAutoreleasedCFType() {
extern CFTypeRef CFCreateSomething();
return CFAutorelease(CFCreateSomething()); // no-warning
}
CFTypeRef getIncorrectlyAutoreleasedCFType() {
extern CFTypeRef CFGetSomething();
return CFAutorelease(CFGetSomething()); // expected-warning{{Object autoreleased too many times}}
}
CFTypeRef createIncorrectlyAutoreleasedCFType() {
extern CFTypeRef CFCreateSomething();
return CFAutorelease(CFCreateSomething()); // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
void useAfterAutorelease() {
extern CFTypeRef CFCreateSomething();
CFTypeRef obj = CFCreateSomething();
CFAutorelease(obj);
extern void useCF(CFTypeRef);
useCF(obj); // no-warning
}
void useAfterRelease() {
// Sanity check that the previous example would have warned with CFRelease.
extern CFTypeRef CFCreateSomething();
CFTypeRef obj = CFCreateSomething();
CFRelease(obj);
extern void useCF(CFTypeRef);
useCF(obj); // expected-warning{{Reference-counted object is used after it is released}}
}
void testAutoreleaseReturnsInput() {
extern CFTypeRef CFCreateSomething();
CFTypeRef obj = CFCreateSomething(); // expected-warning{{Potential leak of an object stored into 'obj'}}
CFTypeRef second = CFAutorelease(obj);
CFRetain(second);
}
CFTypeRef testAutoreleaseReturnsInputSilent() {
extern CFTypeRef CFCreateSomething();
CFTypeRef obj = CFCreateSomething();
CFTypeRef alias = CFAutorelease(obj);
CFRetain(alias);
CFRelease(obj);
return obj; // no-warning
}
void autoreleaseTypedObject() {
CFArrayRef arr = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks);
CFAutorelease((CFTypeRef)arr); // no-warning
}
void autoreleaseReturningTypedObject() {
CFArrayRef arr = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Potential leak of an object stored into 'arr'}}
CFArrayRef alias = (CFArrayRef)CFAutorelease((CFTypeRef)arr);
CFRetain(alias);
}
CFArrayRef autoreleaseReturningTypedObjectSilent() {
CFArrayRef arr = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks);
CFArrayRef alias = (CFArrayRef)CFAutorelease((CFTypeRef)arr);
CFRetain(alias);
CFRelease(arr);
return alias; // no-warning
}
void autoreleaseObjC() {
id obj = [@1 retain];
CFAutorelease(obj); // no-warning
id anotherObj = @1;
CFAutorelease(anotherObj);
} // expected-warning{{Object autoreleased too many times}}
//===----------------------------------------------------------------------===//
// <rdar://problem/13783514> xpc_connection_set_finalizer_f
//===----------------------------------------------------------------------===//