Fix a crash in Retain Count checker error reporting

Fixes a crash in Retain Count checker error reporting logic by handing
the allocation statement retrieval from a BlockEdge program point.

Also added a simple CFG dump routine for debugging.

llvm-svn: 210960
This commit is contained in:
Anna Zaks 2014-06-13 23:47:38 +00:00
parent e287ef847a
commit a6fea1386f
4 changed files with 84 additions and 5 deletions

View File

@ -641,6 +641,8 @@ public:
CFG *getParent() const { return Parent; }
void dump() const;
void dump(const CFG *cfg, const LangOptions &LO, bool ShowColors = false) const;
void print(raw_ostream &OS, const CFG* cfg, const LangOptions &LO,
bool ShowColors) const;

View File

@ -4356,6 +4356,10 @@ void CFGBlock::dump(const CFG* cfg, const LangOptions &LO,
print(llvm::errs(), cfg, LO, ShowColors);
}
void CFGBlock::dump() const {
dump(getParent(), LangOptions(), false);
}
/// print - A simple pretty printer of a CFGBlock that outputs to an ostream.
/// Generally this will only be called from CFG::print.
void CFGBlock::print(raw_ostream &OS, const CFG* cfg,

View File

@ -2340,14 +2340,27 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
// Get the SourceLocation for the allocation site.
// FIXME: This will crash the analyzer if an allocation comes from an
// implicit call. (Currently there are no such allocations in Cocoa, though.)
const Stmt *AllocStmt;
// implicit call (ex: a destructor call).
// (Currently there are no such allocations in Cocoa, though.)
const Stmt *AllocStmt = 0;
ProgramPoint P = AllocNode->getLocation();
if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
AllocStmt = Exit->getCalleeContext()->getCallSite();
else
AllocStmt = P.castAs<PostStmt>().getStmt();
assert(AllocStmt && "All allocations must come from explicit calls");
else {
// We are going to get a BlockEdge when the leak and allocation happen in
// different, non-nested frames (contexts). For example, the case where an
// allocation happens in a block that captures a reference to it and
// that reference is overwritten/dropped by another call to the block.
if (Optional<BlockEdge> Edge = P.getAs<BlockEdge>()) {
if (Optional<CFGStmt> St = Edge->getDst()->front().getAs<CFGStmt>()) {
AllocStmt = St->getStmt();
}
}
else {
AllocStmt = P.castAs<PostStmt>().getStmt();
}
}
assert(AllocStmt && "Cannot find allocation statement");
PathDiagnosticLocation AllocLocation =
PathDiagnosticLocation::createBegin(AllocStmt, SMgr,

View File

@ -0,0 +1,60 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -verify -fblocks %s
@class NSString;
typedef long NSInteger;
typedef unsigned char BOOL;
@interface NSObject {}
+(id)alloc;
-(id)init;
-(id)autorelease;
-(id)copy;
-(id)retain;
@end
@interface NSNumber : NSObject
+ (NSNumber *)numberWithInteger:(NSInteger)value __attribute__((availability(ios,introduced=2.0)));
@end
NSInteger *inoutIntegerValueGlobal;
NSInteger *inoutIntegerValueGlobal2;
NSString *traitNameGlobal;
static BOOL cond;
static inline void reallyPerformAction(void (^integerHandler)(NSInteger *inoutIntegerValue, NSString *traitName)) {
integerHandler(inoutIntegerValueGlobal, traitNameGlobal);
integerHandler(inoutIntegerValueGlobal2,traitNameGlobal);
}
static inline BOOL performAction(NSNumber *(^action)(NSNumber *traitValue)) {
__attribute__((__blocks__(byref))) BOOL didFindTrait = 0;
reallyPerformAction(^(NSInteger *inoutIntegerValue,NSString *traitName) {
if (cond) {
NSNumber *traitValue = @(*inoutIntegerValue);
NSNumber *newTraitValue = action(traitValue);
if (traitValue != newTraitValue) {
*inoutIntegerValue = newTraitValue ? *inoutIntegerValue : *inoutIntegerValue;
}
didFindTrait = 1;
}
});
return didFindTrait;
}
void runTest() {
__attribute__((__blocks__(byref))) NSNumber *builtinResult = ((NSNumber *)0);
BOOL wasBuiltinTrait = performAction(^(NSNumber *traitValue) {
builtinResult = [traitValue retain]; // expected-warning {{Potential leak of an object}}
return traitValue;
});
if (wasBuiltinTrait) {
[builtinResult autorelease];
return;
} else {
return;
}
}