From 71cb4aa32fab71646f152dcfcf8e2a04607e25b1 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Fri, 13 Jun 2014 23:47:38 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@210960 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Analysis/CFG.h | 2 + lib/Analysis/CFG.cpp | 4 ++ .../Checkers/RetainCountChecker.cpp | 23 +++++-- test/Analysis/objc-radar17039661.m | 60 +++++++++++++++++++ 4 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 test/Analysis/objc-radar17039661.m diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index 7909fdc09c..891fb90691 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -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; diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 61e5f5acf0..d6361e8767 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -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, diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 0c130fe833..eb699d6940 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -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 Exit = P.getAs()) AllocStmt = Exit->getCalleeContext()->getCallSite(); - else - AllocStmt = P.castAs().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 Edge = P.getAs()) { + if (Optional St = Edge->getDst()->front().getAs()) { + AllocStmt = St->getStmt(); + } + } + else { + AllocStmt = P.castAs().getStmt(); + } + } + assert(AllocStmt && "Cannot find allocation statement"); PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, diff --git a/test/Analysis/objc-radar17039661.m b/test/Analysis/objc-radar17039661.m new file mode 100644 index 0000000000..ec4f19d2da --- /dev/null +++ b/test/Analysis/objc-radar17039661.m @@ -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; + } +} -- 2.40.0