[analyzer] Mark symbol values as dead in the environment.

This allows us to properly remove dead bindings at the end of the top-level
stack frame, using the ReturnStmt, if there is one, to keep the return value
live. This in turn removes the need for a check::EndPath callback in leak
checkers.

This does cause some changes in the path notes for leak checkers. Previously,
a leak would be reported at the location of the closing brace in a function.
Now, it gets reported at the last statement. This matches the way leaks are
currently reported for inlined functions, but is less than ideal for both.

llvm-svn: 168066
This commit is contained in:
Jordan Rose 2012-11-15 19:11:27 +00:00
parent 38990beed8
commit b5b0fc196e
7 changed files with 262 additions and 184 deletions

View File

@ -154,26 +154,33 @@ public:
const ExplodedGraph& getGraph() const { return G; }
/// \brief Run the analyzer's garbage collection - remove dead symbols and
/// bindings.
/// bindings from the state.
///
/// \param Node - The predecessor node, from which the processing should
/// start.
/// \param Out - The returned set of output nodes.
/// \param ReferenceStmt - Run garbage collection using the symbols,
/// which are live before the given statement.
/// \param LC - The location context of the ReferenceStmt.
/// \param DiagnosticStmt - the statement used to associate the diagnostic
/// message, if any warnings should occur while removing the dead (leaks
/// are usually reported here).
/// \param K - In some cases it is possible to use PreStmt kind. (Do
/// not use it unless you know what you are doing.)
/// If the ReferenceStmt is NULL, everything is this and parent contexts is
/// considered live.
/// If the stack frame context is NULL, everything on stack is considered
/// dead.
/// Checkers can participate in this process with two callbacks:
/// \c checkLiveSymbols and \c checkDeadSymbols. See the CheckerDocumentation
/// class for more information.
///
/// \param Node The predecessor node, from which the processing should start.
/// \param Out The returned set of output nodes.
/// \param ReferenceStmt The statement which is about to be processed.
/// Everything needed for this statement should be considered live.
/// A null statement means that everything in child LocationContexts
/// is dead.
/// \param LC The location context of the \p ReferenceStmt. A null location
/// context means that we have reached the end of analysis and that
/// all statements and local variables should be considered dead.
/// \param DiagnosticStmt Used as a location for any warnings that should
/// occur while removing the dead (e.g. leaks). By default, the
/// \p ReferenceStmt is used.
/// \param K Denotes whether this is a pre- or post-statement purge. This
/// must only be ProgramPoint::PostStmtPurgeDeadSymbolsKind if an
/// entire location context is being cleared, in which case the
/// \p ReferenceStmt must either be a ReturnStmt or \c NULL. Otherwise,
/// it must be ProgramPoint::PreStmtPurgeDeadSymbolsKind (the default)
/// and \p ReferenceStmt must be valid (non-null).
void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out,
const Stmt *ReferenceStmt, const StackFrameContext *LC,
const Stmt *DiagnosticStmt,
const Stmt *ReferenceStmt, const LocationContext *LC,
const Stmt *DiagnosticStmt = 0,
ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);
/// processCFGElement - Called by CoreEngine. Used to generate new successor

View File

@ -241,6 +241,10 @@ EnvironmentManager::removeDeadBindings(Environment Env,
// Mark all symbols in the block expr's value live.
RSScaner.scan(X);
continue;
} else {
SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
for (; SI != SE; ++SI)
SymReaper.maybeDead(*SI);
}
}

View File

@ -268,22 +268,39 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
const Stmt *ReferenceStmt,
const StackFrameContext *LC,
const LocationContext *LC,
const Stmt *DiagnosticStmt,
ProgramPoint::Kind K) {
assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
ReferenceStmt == 0)
ReferenceStmt == 0 || isa<ReturnStmt>(ReferenceStmt))
&& "PostStmt is not generally supported by the SymbolReaper yet");
assert(LC && "Must pass the current (or expiring) LocationContext");
if (!DiagnosticStmt) {
DiagnosticStmt = ReferenceStmt;
assert(DiagnosticStmt && "Required for clearing a LocationContext");
}
NumRemoveDeadBindings++;
CleanedState = Pred->getState();
SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
// LC is the location context being destroyed, but SymbolReaper wants a
// location context that is still live. (If this is the top-level stack
// frame, this will be null.)
if (!ReferenceStmt) {
assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind &&
"Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext");
LC = LC->getParent();
}
const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : 0;
SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager());
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
// Create a state in which dead bindings are removed from the environment
// and the store. TODO: The function should just return new env and store,
// not a new state.
const StackFrameContext *SFC = LC->getCurrentStackFrame();
CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
// Process any special transfer function for dead symbols.
@ -345,8 +362,7 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
EntryNode = Pred;
ExplodedNodeSet CleanedStates;
if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
removeDead(EntryNode, CleanedStates, currStmt,
Pred->getStackFrame(), currStmt);
removeDead(EntryNode, CleanedStates, currStmt, Pred->getLocationContext());
} else
CleanedStates.Add(EntryNode);

View File

@ -168,27 +168,24 @@ static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
NodeBuilder Bldr(Pred, Dst, BC);
// Find the last statement in the function and the corresponding basic block.
const Stmt *LastSt = 0;
const CFGBlock *Blk = 0;
llvm::tie(LastSt, Blk) = getLastStmt(Pred);
if (!Blk || !LastSt) {
Dst.Add(Pred);
return;
}
// If the last statement is return, everything it references should stay live.
if (isa<ReturnStmt>(LastSt))
return;
// Here, we call the Symbol Reaper with 0 stack context telling it to clean up
// everything on the stack. We use LastStmt as a diagnostic statement, with
// which the PreStmtPurgeDead point will be associated.
currBldrCtx = &BC;
removeDead(Pred, Dst, 0, 0, LastSt,
// which the program point will be associated. However, we only want to use
// LastStmt as a reference for what to clean up if it's a ReturnStmt;
// otherwise, everything is dead.
SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt),
Pred->getLocationContext(), LastSt,
ProgramPoint::PostStmtPurgeDeadSymbolsKind);
currBldrCtx = 0;
}
static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
@ -290,11 +287,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
currBldrCtx = &Ctx;
// Here, we call the Symbol Reaper with 0 statement and caller location
// Here, we call the Symbol Reaper with 0 statement and callee location
// context, telling it to clean up everything in the callee's context
// (and it's children). We use LastStmt as a diagnostic statement, which
// which the PreStmtPurge Dead point will be associated.
removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt,
// (and its children). We use LastSt as a diagnostic statement, which
// which the program point will be associated.
removeDead(BindedRetNode, CleanedNodes, 0, calleeCtx, LastSt,
ProgramPoint::PostStmtPurgeDeadSymbolsKind);
currBldrCtx = 0;
} else {

View File

@ -517,13 +517,13 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>22</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>21</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>22</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>21</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@ -534,8 +534,8 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>22</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>21</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
@ -550,11 +550,11 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>type</key><string>Memory leak</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>myArrayAllocation</string>
// CHECK-NEXT: <key>issue_hash</key><integer>4</integer>
// CHECK-NEXT: <key>issue_hash</key><integer>3</integer>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>22</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>21</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>
@ -1301,13 +1301,13 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>46</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>45</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>46</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>45</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@ -1318,8 +1318,8 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>46</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>45</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
@ -1334,11 +1334,11 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>type</key><string>Memory leak</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>test_wrapper</string>
// CHECK-NEXT: <key>issue_hash</key><integer>3</integer>
// CHECK-NEXT: <key>issue_hash</key><integer>2</integer>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>46</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>45</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>
@ -2636,6 +2636,40 @@ void use_function_with_leak7() {
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>9</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>26</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>9</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@ -2648,13 +2682,13 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>87</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>87</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@ -2665,8 +2699,8 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>87</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
@ -2681,11 +2715,11 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>type</key><string>Memory leak</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>use_ret</string>
// CHECK-NEXT: <key>issue_hash</key><integer>3</integer>
// CHECK-NEXT: <key>issue_hash</key><integer>2</integer>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>87</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>86</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>
@ -4448,45 +4482,11 @@ void use_function_with_leak7() {
// CHECK-NEXT: <string>Returned allocated memory</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>169</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>169</integer>
// CHECK-NEXT: <key>col</key><integer>23</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>170</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>170</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>170</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>169</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
@ -4501,11 +4501,11 @@ void use_function_with_leak7() {
// CHECK-NEXT: <key>type</key><string>Memory leak</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>use_function_with_leak7</string>
// CHECK-NEXT: <key>issue_hash</key><integer>2</integer>
// CHECK-NEXT: <key>issue_hash</key><integer>1</integer>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>170</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>169</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>

View File

@ -1030,6 +1030,11 @@ void *test(void *ptr) {
return newPtr;
}
char *testLeakWithinReturn(char *str) {
return strdup(strdup(str)); // expected-warning{{leak}}
}
// ----------------------------------------------------------------------------
// False negatives.

View File

@ -1199,6 +1199,40 @@ void rdar8331641(int x) {
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@ -1232,13 +1266,13 @@ void rdar8331641(int x) {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>58</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>58</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@ -1249,10 +1283,25 @@ void rdar8331641(int x) {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>58</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>14</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Object leaked: object allocated and stored into &apos;value&apos; is not referenced later in this execution path and has a retain count of +1</string>
@ -1265,11 +1314,11 @@ void rdar8331641(int x) {
// CHECK-NEXT: <key>type</key><string>Leak</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>rdar8331641</string>
// CHECK-NEXT: <key>issue_hash</key><integer>6</integer>
// CHECK-NEXT: <key>issue_hash</key><integer>5</integer>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>58</integer>
// CHECK-NEXT: <key>col</key><integer>1</integer>
// CHECK-NEXT: <key>line</key><integer>57</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>