Fix major regression in RetainCountChecker. DefaultSummaries were not being used when they were meant to be. Fixes <rdar://problem/10241614>.

llvm-svn: 141250
This commit is contained in:
Ted Kremenek 2011-10-05 23:54:29 +00:00
parent c4aa22cdd4
commit f3e3f6645e
2 changed files with 135 additions and 90 deletions

View File

@ -481,19 +481,19 @@ struct isPodLike<ObjCSummaryKey> { static const bool value = true; };
namespace {
class ObjCSummaryCache {
typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy;
typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy;
MapTy M;
public:
ObjCSummaryCache() {}
RetainSummary* find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
Selector S) {
// Lookup the method using the decl for the class @interface. If we
// have no decl, lookup using the class name.
return D ? find(D, S) : find(ClsName, S);
}
RetainSummary* find(const ObjCInterfaceDecl *D, Selector S) {
const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
@ -518,12 +518,12 @@ public:
// Cache the summary with original key to make the next lookup faster
// and return the iterator.
RetainSummary *Summ = I->second;
const RetainSummary *Summ = I->second;
M[K] = Summ;
return Summ;
}
RetainSummary* find(IdentifierInfo* II, Selector S) {
const RetainSummary * find(IdentifierInfo* II, Selector S) {
// FIXME: Class method lookup. Right now we dont' have a good way
// of going between IdentifierInfo* and the class hierarchy.
MapTy::iterator I = M.find(ObjCSummaryKey(II, S));
@ -534,11 +534,11 @@ public:
return I == M.end() ? NULL : I->second;
}
RetainSummary*& operator[](ObjCSummaryKey K) {
const RetainSummary *& operator[](ObjCSummaryKey K) {
return M[K];
}
RetainSummary*& operator[](Selector S) {
const RetainSummary *& operator[](Selector S) {
return M[ ObjCSummaryKey(S) ];
}
};
@ -555,7 +555,7 @@ class RetainSummaryManager {
// Typedefs.
//==-----------------------------------------------------------------==//
typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*>
typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *>
FuncSummariesTy;
typedef ObjCSummaryCache ObjCMethodSummariesTy;
@ -602,7 +602,7 @@ class RetainSummaryManager {
RetEffect ObjCInitRetE;
RetainSummary DefaultSummary;
RetainSummary* StopSummary;
const RetainSummary *StopSummary;
//==-----------------------------------------------------------------==//
// Methods.
@ -617,23 +617,28 @@ class RetainSummaryManager {
public:
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func);
const RetainSummary *getDefaultSummary() {
return &DefaultSummary;
}
const RetainSummary * getUnarySummary(const FunctionType* FT,
UnaryFuncKind func);
RetainSummary* getCFSummaryCreateRule(const FunctionDecl *FD);
RetainSummary* getCFSummaryGetRule(const FunctionDecl *FD);
RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl *FD);
const RetainSummary * getCFSummaryCreateRule(const FunctionDecl *FD);
const RetainSummary * getCFSummaryGetRule(const FunctionDecl *FD);
const RetainSummary * getCFCreateGetRuleSummary(const FunctionDecl *FD);
RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff,
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = MayEscape);
const RetainSummary * getPersistentSummary(ArgEffects AE, RetEffect RetEff,
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = MayEscape);
RetainSummary* getPersistentSummary(RetEffect RE,
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = MayEscape) {
const RetainSummary * getPersistentSummary(RetEffect RE,
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = MayEscape) {
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
}
RetainSummary *getPersistentStopSummary() {
const RetainSummary *getPersistentStopSummary() {
if (StopSummary)
return StopSummary;
@ -643,28 +648,28 @@ public:
return StopSummary;
}
RetainSummary *getInitMethodSummary(QualType RetTy);
const RetainSummary *getInitMethodSummary(QualType RetTy);
void InitializeClassMethodSummaries();
void InitializeMethodSummaries();
private:
void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) {
ObjCClassMethodSummaries[S] = Summ;
}
void addNSObjectMethSummary(Selector S, RetainSummary *Summ) {
void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) {
ObjCMethodSummaries[S] = Summ;
}
void addClassMethSummary(const char* Cls, const char* nullaryName,
RetainSummary *Summ) {
const RetainSummary *Summ) {
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
Selector S = GetNullarySelector(nullaryName, Ctx);
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
void addInstMethSummary(const char* Cls, const char* nullaryName,
RetainSummary *Summ) {
const RetainSummary *Summ) {
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
Selector S = GetNullarySelector(nullaryName, Ctx);
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
@ -680,26 +685,26 @@ private:
}
void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries,
RetainSummary* Summ, va_list argp) {
const RetainSummary * Summ, va_list argp) {
Selector S = generateSelector(argp);
Summaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
void addInstMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
va_end(argp);
}
void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) {
void addClsMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp);
va_end(argp);
}
void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) {
void addClsMethSummary(IdentifierInfo *II, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp);
@ -731,29 +736,31 @@ public:
InitializeMethodSummaries();
}
RetainSummary* getSummary(const FunctionDecl *FD);
const RetainSummary * getSummary(const FunctionDecl *FD);
RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
const ProgramState *state,
const LocationContext *LC);
const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
const ProgramState *state,
const LocationContext *LC);
RetainSummary* getInstanceMethodSummary(const ObjCMessage &msg,
const ObjCInterfaceDecl *ID) {
const RetainSummary * getInstanceMethodSummary(const ObjCMessage &msg,
const ObjCInterfaceDecl *ID) {
return getInstanceMethodSummary(msg.getSelector(), 0,
ID, msg.getMethodDecl(), msg.getType(Ctx));
}
RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy);
const RetainSummary * getInstanceMethodSummary(Selector S,
IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy);
RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy);
const RetainSummary *getClassMethodSummary(Selector S,
IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy);
RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
const ObjCInterfaceDecl *Class = 0;
if (!msg.isInstanceMessage())
Class = msg.getReceiverInterface();
@ -766,7 +773,7 @@ public:
/// getMethodSummary - This version of getMethodSummary is used to query
/// the summary for the current method being analyzed.
RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
// FIXME: Eventually this should be unneeded.
const ObjCInterfaceDecl *ID = MD->getClassInterface();
Selector S = MD->getSelector();
@ -779,13 +786,13 @@ public:
return getClassMethodSummary(S, ClsName, ID, MD, ResultTy);
}
RetainSummary* getCommonMethodSummary(const ObjCMethodDecl *MD,
Selector S, QualType RetTy);
const RetainSummary * getCommonMethodSummary(const ObjCMethodDecl *MD,
Selector S, QualType RetTy);
void updateSummaryFromAnnotations(RetainSummary *&Summ,
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD);
void updateSummaryFromAnnotations(RetainSummary *&Summ,
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD);
bool isGCEnabled() const { return GCEnabled; }
@ -794,8 +801,8 @@ public:
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
RetainSummary *copySummary(RetainSummary *OldSumm) {
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
const RetainSummary *copySummary(const RetainSummary *OldSumm) {
RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(*OldSumm);
return Summ;
}
@ -807,28 +814,34 @@ public:
// and then copied into managed memory.
class RetainSummaryTemplate {
RetainSummaryManager &Manager;
RetainSummary *&RealSummary;
const RetainSummary *&RealSummary;
const RetainSummary *BaseSummary;
RetainSummary ScratchSummary;
bool Accessed;
public:
RetainSummaryTemplate(RetainSummary *&real, const RetainSummary &base,
RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base,
RetainSummaryManager &manager)
: Manager(manager), RealSummary(real), ScratchSummary(base), Accessed(false)
{}
: Manager(manager),
RealSummary(real),
BaseSummary(&base),
ScratchSummary(base),
Accessed(false) {}
~RetainSummaryTemplate() {
if (!RealSummary && Accessed)
if (Accessed)
RealSummary = Manager.copySummary(&ScratchSummary);
else if (!RealSummary)
RealSummary = BaseSummary;
}
RetainSummary &operator*() {
Accessed = true;
return RealSummary ? *RealSummary : ScratchSummary;
return ScratchSummary;
}
RetainSummary *operator->() {
Accessed = true;
return RealSummary ? RealSummary : &ScratchSummary;
return &ScratchSummary;
}
};
@ -844,12 +857,12 @@ ArgEffects RetainSummaryManager::getArgEffects() {
return AE;
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff,
ArgEffect ReceiverEff,
ArgEffect DefaultEff) {
// Create the summary and return it.
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff);
return Summ;
}
@ -872,14 +885,14 @@ static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
return FName.find("MakeCollectable") != StringRef::npos;
}
RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl *FD) {
const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
if (I != FuncSummaries.end())
return I->second;
// No summary? Generate one.
RetainSummary *S = 0;
const RetainSummary *S = 0;
do {
// We generate "stop" summaries for implicitly defined functions.
@ -1068,7 +1081,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl *FD) {
return S;
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
if (coreFoundation::followsCreateRule(FD))
return getCFSummaryCreateRule(FD);
@ -1076,7 +1089,7 @@ RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
return getCFSummaryGetRule(FD);
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getUnarySummary(const FunctionType* FT,
UnaryFuncKind func) {
@ -1100,14 +1113,14 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
assert (ScratchArgs.isEmpty());
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
assert (ScratchArgs.isEmpty());
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
@ -1118,7 +1131,7 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
RetainSummary*
const RetainSummary *
RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
assert(ScratchArgs.isEmpty());
// 'init' methods conceptually return a newly allocated object and claim
@ -1127,11 +1140,11 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
coreFoundation::isCFObjectRef(RetTy))
return getPersistentSummary(ObjCInitRetE, DecRefMsg);
return 0;
return getDefaultSummary();
}
void
RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary *&Summ,
RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD) {
if (!FD)
return;
@ -1179,8 +1192,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary *&Summ,
}
void
RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary *&Summ,
const ObjCMethodDecl *MD) {
RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD) {
if (!MD)
return;
@ -1233,7 +1246,7 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary *&Summ,
}
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD,
Selector S, QualType RetTy) {
@ -1286,12 +1299,12 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD,
}
if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing)
return 0;
return getDefaultSummary();
return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape);
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
const ProgramState *state,
const LocationContext *LC) {
@ -1334,7 +1347,7 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
return getInstanceMethodSummary(msg, ID);
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getInstanceMethodSummary(Selector S,
IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
@ -1342,7 +1355,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
QualType RetTy) {
// Look up a summary in our summary cache.
RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S);
const RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S);
if (!Summ) {
assert(ScratchArgs.isEmpty());
@ -1363,14 +1376,14 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
return Summ;
}
RetainSummary*
const RetainSummary *
RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy) {
assert(ClsName && "Class name must be specified.");
RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S);
const RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S);
if (!Summ) {
Summ = getCommonMethodSummary(MD, S, RetTy);
@ -1402,7 +1415,7 @@ void RetainSummaryManager::InitializeClassMethodSummaries() {
// used for delegates that can release the object. When we have better
// inter-procedural analysis we can potentially do something better. This
// workaround is to remove false positives.
RetainSummary *Summ =
const RetainSummary *Summ =
getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
@ -1427,7 +1440,7 @@ void RetainSummaryManager::InitializeMethodSummaries() {
// Create the "init" selector. It just acts as a pass-through for the
// receiver.
RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
const RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
// awakeAfterUsingCoder: behaves basically like an 'init' method. It
@ -1436,13 +1449,13 @@ void RetainSummaryManager::InitializeMethodSummaries() {
InitSumm);
// The next methods are allocators.
RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
RetainSummary *CFAllocSumm =
const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
const RetainSummary *CFAllocSumm =
getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
// Create the "retain" selector.
RetEffect NoRet = RetEffect::MakeNoRet();
RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg);
const RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg);
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
// Create the "release" selector.
@ -1471,7 +1484,7 @@ void RetainSummaryManager::InitializeMethodSummaries() {
// Thus, we need to track an NSWindow's display status.
// This is tracked in <rdar://problem/6062711>.
// See also http://llvm.org/bugs/show_bug.cgi?id=3714.
RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
const RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
StopTracking,
StopTracking);
@ -2561,7 +2574,7 @@ void RetainCountChecker::checkPostStmt(const CallExpr *CE,
SVal L = state->getSVal(Callee);
RetainSummaryManager &Summaries = getSummaryManager(C);
RetainSummary *Summ = 0;
const RetainSummary *Summ = 0;
// FIXME: Better support for blocks. For now we stop tracking anything
// that is passed to blocks.
@ -2575,9 +2588,8 @@ void RetainCountChecker::checkPostStmt(const CallExpr *CE,
Summ = Summaries.getSummary(MD);
}
// If we didn't get a summary, this function doesn't affect retain counts.
if (!Summ)
return;
Summ = Summaries.getDefaultSummary();
checkSummary(*Summ, CallOrObjCMessage(CE, state), C);
}
@ -2589,7 +2601,7 @@ void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
return;
RetainSummaryManager &Summaries = getSummaryManager(C);
RetainSummary *Summ = Summaries.getSummary(Ctor);
const RetainSummary *Summ = Summaries.getSummary(Ctor);
// If we didn't get a summary, this constructor doesn't affect retain counts.
if (!Summ)
@ -2606,7 +2618,7 @@ void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
RetainSummaryManager &Summaries = getSummaryManager(C);
RetainSummary *Summ;
const RetainSummary *Summ;
if (Msg.isInstanceMessage()) {
const LocationContext *LC = Pred->getLocationContext();
Summ = Summaries.getInstanceMethodSummary(Msg, state, LC);

View File

@ -146,7 +146,9 @@ NSFastEnumerationState;
typedef double NSTimeInterval;
@interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate;
@end typedef unsigned short unichar;
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length;
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
- (NSUInteger)length;
- (NSString *)stringByAppendingString:(NSString *)aString;
- ( const char *)UTF8String;
- (id)initWithUTF8String:(const char *)nullTerminatedCString;
+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
@ -268,6 +270,12 @@ extern void CGContextDrawLinearGradient(CGContextRef context,
CGGradientDrawingOptions options);
extern CGColorSpaceRef CGColorSpaceCreateDeviceRGB(void);
@interface NSMutableArray : NSObject
- (void)addObject:(id)object;
+ (id)array;
@end
//===----------------------------------------------------------------------===//
// Test cases.
//===----------------------------------------------------------------------===//
@ -1571,3 +1579,28 @@ void rdar6582778_2(void) {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
global = CFDateCreate(0, t); // no-warning
}
// <rdar://problem/10232019> - Test that objects passed to containers
// are marked "escaped".
void rdar10232019() {
NSMutableArray *array = [NSMutableArray array];
NSString *string = [[NSString alloc] initWithUTF8String:"foo"];
[array addObject:string];
[string release];
NSString *otherString = [string stringByAppendingString:@"bar"]; // no-warning
NSLog(@"%@", otherString);
}
void rdar10232019_positive() {
NSMutableArray *array = [NSMutableArray array];
NSString *string = [[NSString alloc] initWithUTF8String:"foo"];
[string release];
NSString *otherString = [string stringByAppendingString:@"bar"]; // expected-warning {{Reference-counted object is used after it is release}}
NSLog(@"%@", otherString);
}