aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2012-03-16 23:24:20 +0000
committerAnna Zaks <ganna@apple.com>2012-03-16 23:24:20 +0000
commit56a938ff85a444eb3d30d2634d92ce5b1f6fae56 (patch)
tree9d85719ed51903e12e39c5f7d4db93f727f37763
parent076add680e281709cf081052be0dcb822dc8f37d (diff)
[analyzer] Create symbol-aware stack hints (building upon r152837).
The symbol-aware stack hint combines the checker-provided message with the information about how the symbol was passed to the callee: as a parameter or a return value. For malloc, the generated messages look like this : "Returning from 'foo'; released memory via 1st parameter" "Returning from 'foo'; allocated memory via 1st parameter" "Returning from 'foo'; allocated memory returned" "Returning from 'foo'; reallocation of 1st parameter failed" (We are yet to handle cases when the symbol is a field in a struct or an array element.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152962 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h71
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp33
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp91
-rw-r--r--test/Analysis/malloc-plist.c922
5 files changed, 1063 insertions, 92 deletions
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
index fea02caafd..b40bc71010 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
@@ -41,6 +41,8 @@ class Stmt;
namespace ento {
class ExplodedNode;
+class SymExpr;
+typedef const SymExpr* SymbolRef;
//===----------------------------------------------------------------------===//
// High-level interface for handlers of path-sensitive diagnostics.
@@ -357,6 +359,52 @@ public:
virtual void Profile(llvm::FoldingSetNodeID &ID) const;
};
+/// \brief Interface for classes constructing Stack hints.
+///
+/// If a PathDiagnosticEvent occurs in a different frame than the final
+/// diagnostic the hints can be used to summarise the effect of the call.
+class StackHintGenerator {
+public:
+ virtual ~StackHintGenerator() = 0;
+
+ /// \brief Construct the Diagnostic message for the given ExplodedNode.
+ virtual std::string getMessage(const ExplodedNode *N) = 0;
+};
+
+/// \brief Constructs a Stack hint for the given symbol.
+///
+/// The class knows how to construct the stack hint message based on
+/// traversing the CallExpr associated with the call and checking if the given
+/// symbol is returned or is one of the arguments.
+/// The hint can be customized by redefining 'getMessageForX()' methods.
+class StackHintGeneratorForSymbol : public StackHintGenerator {
+private:
+ SymbolRef Sym;
+ std::string Msg;
+
+public:
+ StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
+ virtual ~StackHintGeneratorForSymbol() {}
+
+ /// \brief Search the call expression for the symbol Sym and dispatch the
+ /// 'getMessageForX()' methods to construct a specific message.
+ virtual std::string getMessage(const ExplodedNode *N);
+
+ /// Prints the ordinal form of the given integer,
+ /// only valid for ValNo : ValNo > 0.
+ void printOrdinal(unsigned ValNo, llvm::raw_svector_ostream &Out);
+
+ /// Produces the message of the following form:
+ /// 'Msg via Nth parameter'
+ virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
+ virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
+ return Msg + " returned";
+ }
+ virtual std::string getMessageForSymbolNotFound() {
+ return Msg;
+ }
+};
+
class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
llvm::Optional<bool> IsPrunable;
@@ -364,16 +412,14 @@ class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
/// supply a message that will be used to construct an extra hint on the
/// returns from all the calls on the stack from this event to the final
/// diagnostic.
- /// TODO: This should be a callback that constructs a string given the
- /// ExplodedNode, which would allow the checkers to refer to the expression.
- std::string CallStackMessage;
+ llvm::OwningPtr<StackHintGenerator> CallStackHint;
public:
PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
StringRef s, bool addPosRange = true,
- StringRef callStackMsg = "")
+ StackHintGenerator *stackHint = 0)
: PathDiagnosticSpotPiece(pos, s, Event, addPosRange),
- CallStackMessage(callStackMsg) {}
+ CallStackHint(stackHint) {}
~PathDiagnosticEventPiece();
@@ -391,11 +437,16 @@ public:
return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
}
- StringRef getCallStackMessage() {
- if (!CallStackMessage.empty())
- return CallStackMessage;
- else
- return StringRef();
+ bool hasCallStackHint() {
+ return (CallStackHint != 0);
+ }
+
+ /// Produce the hint for the given node. The node contains
+ /// information about the call for which the diagnostic can be generated.
+ StringRef getCallStackMessage(const ExplodedNode *N) {
+ if (CallStackHint)
+ return CallStackHint->getMessage(N);
+ return "";
}
static inline bool classof(const PathDiagnosticPiece *P) {
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index e071626eb6..2926fd5c04 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -243,6 +243,29 @@ private:
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR);
+ private:
+ class StackHintGeneratorForReallocationFailed
+ : public StackHintGeneratorForSymbol {
+ public:
+ StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
+ : StackHintGeneratorForSymbol(S, M) {}
+
+ virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "; reallocation of ";
+ // Printed parameters start at 1, not 0.
+ printOrdinal(++ArgIndex, os);
+ os << " parameter failed";
+
+ return os.str();
+ }
+
+ virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
+ return "; reallocation of returned value failed";
+ }
+ };
};
};
} // end anonymous namespace
@@ -1249,7 +1272,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
const Stmt *S = 0;
const char *Msg = 0;
- const char *StackMsg = 0;
+ StackHintGeneratorForSymbol *StackHint = 0;
// Retrieve the associated statement.
ProgramPoint ProgLoc = N->getLocation();
@@ -1269,14 +1292,15 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
if (Mode == Normal) {
if (isAllocated(RS, RSPrev, S)) {
Msg = "Memory is allocated";
- StackMsg = ", which allocated memory";
+ StackHint = new StackHintGeneratorForSymbol(Sym, "; allocated memory");
} else if (isReleased(RS, RSPrev, S)) {
Msg = "Memory is released";
- StackMsg = ", which released memory";
+ StackHint = new StackHintGeneratorForSymbol(Sym, "; released memory");
} else if (isReallocFailedCheck(RS, RSPrev, S)) {
Mode = ReallocationFailed;
Msg = "Reallocation failed";
- StackMsg = ", where reallocation failed";
+ StackHint = new StackHintGeneratorForReallocationFailed(Sym,
+ "; reallocation failed");
}
// We are in a special mode if a reallocation failed later in the path.
@@ -1296,18 +1320,18 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
if (!(FunName.equals("realloc") || FunName.equals("reallocf")))
return 0;
Msg = "Attempt to reallocate memory";
- StackMsg = ", which attempted to reallocate memory";
+ StackHint = new StackHintGeneratorForSymbol(Sym, "; reallocated memory");
Mode = Normal;
}
if (!Msg)
return 0;
- assert(StackMsg);
+ assert(StackHint);
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return new PathDiagnosticEventPiece(Pos, Msg, true, StackMsg);
+ return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint);
}
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index b59405bcd2..363a6dffee 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -380,22 +380,29 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
//===----------------------------------------------------------------------===//
// "Minimal" path diagnostic generation algorithm.
//===----------------------------------------------------------------------===//
+typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair;
+typedef SmallVector<StackDiagPair, 6> StackDiagVector;
+
static void updateStackPiecesWithMessage(PathDiagnosticPiece *P,
- llvm::SmallVector<PathDiagnosticCallPiece*, 6> &CallStack) {
+ StackDiagVector &CallStack) {
// If the piece contains a special message, add it to all the call
// pieces on the active stack.
if (PathDiagnosticEventPiece *ep =
dyn_cast<PathDiagnosticEventPiece>(P)) {
- StringRef stackMsg = ep->getCallStackMessage();
- if (!stackMsg.empty())
- for (llvm::SmallVector<PathDiagnosticCallPiece*, 6>::iterator
- I = CallStack.begin(), E = CallStack.end(); I != E; ++I)
+ if (ep->hasCallStackHint())
+ for (StackDiagVector::iterator I = CallStack.begin(),
+ E = CallStack.end(); I != E; ++I) {
+ PathDiagnosticCallPiece *CP = I->first;
+ const ExplodedNode *N = I->second;
+ StringRef stackMsg = ep->getCallStackMessage(N);
+
// The last message on the path to final bug is the most important
// one. Since we traverse the path backwards, do not add the message
// if one has been previously added.
- if (!(*I)->hasCallStackMessage())
- (*I)->setCallStackMessage(stackMsg);
+ if (!CP->hasCallStackMessage())
+ CP->setCallStackMessage(stackMsg);
+ }
}
}
@@ -410,7 +417,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
const ExplodedNode *NextNode = N->pred_empty()
? NULL : *(N->pred_begin());
- llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack;
+ StackDiagVector CallStack;
while (NextNode) {
N = NextNode;
@@ -424,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
PD.getActivePath().push_front(C);
PD.pushActivePath(&C->path);
- CallStack.push_back(C);
+ CallStack.push_back(StackDiagPair(C, N));
continue;
}
@@ -446,7 +453,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
}
C->setCallee(*CE, SMgr);
if (!CallStack.empty()) {
- assert(CallStack.back() == C);
+ assert(CallStack.back().first == C);
CallStack.pop_back();
}
continue;
@@ -1047,7 +1054,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
const ExplodedNode *N) {
EdgeBuilder EB(PD, PDB);
const SourceManager& SM = PDB.getSourceManager();
- llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack;
+ StackDiagVector CallStack;
const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
while (NextNode) {
@@ -1068,7 +1075,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticCallPiece::construct(N, *CE, SM);
PD.getActivePath().push_front(C);
PD.pushActivePath(&C->path);
- CallStack.push_back(C);
+ CallStack.push_back(StackDiagPair(C, N));
break;
}
@@ -1104,7 +1111,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
EB.addContext(CE->getCallExpr());
if (!CallStack.empty()) {
- assert(CallStack.back() == C);
+ assert(CallStack.back().first == C);
CallStack.pop_back();
}
break;
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index cd08f9ceb7..2a2b9c615e 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -659,3 +659,94 @@ void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
ID.AddString(*I);
}
+
+StackHintGenerator::~StackHintGenerator() {}
+
+std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
+ ProgramPoint P = N->getLocation();
+ const CallExit *CExit = dyn_cast<CallExit>(&P);
+ assert(CExit && "Stack Hints should be constructed at CallExit points.");
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
+ if (!CE)
+ return "";
+
+ // Get the successor node to make sure the return statement is evaluated and
+ // CE is set to the result value.
+ N = *N->succ_begin();
+ if (!N)
+ return getMessageForSymbolNotFound();
+
+ // Check if one of the parameters are set to the interesting symbol.
+ ProgramStateRef State = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+ unsigned ArgIndex = 0;
+ for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+ E = CE->arg_end(); I != E; ++I, ++ArgIndex){
+ SVal SV = State->getSVal(*I, LCtx);
+
+ // Check if the variable corresponding to the symbol is passed by value.
+ SymbolRef AS = SV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+
+ // Check if the parameter is a pointer to the symbol.
+ if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) {
+ SVal PSV = State->getSVal(Reg->getRegion());
+ SymbolRef AS = PSV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+ }
+ }
+
+ // Check if we are returning the interesting symbol.
+ SVal SV = State->getSVal(CE, LCtx);
+ SymbolRef RetSym = SV.getAsLocSymbol();
+ if (RetSym == Sym) {
+ return getMessageForReturn(CE);
+ }
+
+ return getMessageForSymbolNotFound();
+}
+
+/// TODO: This is copied from clang diagnostics. Maybe we could just move it to
+/// some common place. (Same as HandleOrdinalModifier.)
+void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo,
+ llvm::raw_svector_ostream &Out) {
+ assert(ValNo != 0 && "ValNo must be strictly positive!");
+
+ // We could use text forms for the first N ordinals, but the numeric
+ // forms are actually nicer in diagnostics because they stand out.
+ Out << ValNo;
+
+ // It is critically important that we do this perfectly for
+ // user-written sequences with over 100 elements.
+ switch (ValNo % 100) {
+ case 11:
+ case 12:
+ case 13:
+ Out << "th"; return;
+ default:
+ switch (ValNo % 10) {
+ case 1: Out << "st"; return;
+ case 2: Out << "nd"; return;
+ case 3: Out << "rd"; return;
+ default: Out << "th"; return;
+ }
+ }
+}
+
+std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << Msg << " via ";
+ // Printed parameters start at 1, not 0.
+ printOrdinal(++ArgIndex, os);
+ os << " parameter";
+
+ return os.str();
+}
diff --git a/test/Analysis/malloc-plist.c b/test/Analysis/malloc-plist.c
index f41c0c3a5b..d08ed07d18 100644
--- a/test/Analysis/malloc-plist.c
+++ b/test/Analysis/malloc-plist.c
@@ -47,6 +47,7 @@ void test_wrapper() {
}
// Test what happens when the same call frees and allocated memory.
+// Also tests the stack hint for parameters, when they are passed directly or via pointer.
void my_free(void *x) {
free(x);
}
@@ -62,6 +63,30 @@ void *test_double_action_call() {
return buf;
}
+// Test stack hint for 'reallocation failed'.
+char *my_realloc(char *buf) {
+ char *tmp;
+ tmp = (char*)realloc(buf, 0x1000000);
+ if (!tmp) {
+ return tmp;
+ }
+ return tmp;
+}
+void reallocIntra() {
+ char *buf = (char *)malloc(100);
+ buf = my_realloc(buf);
+ free(buf);
+}
+
+// Test stack hint when returning a result.
+static char *malloc_wrapper_ret() {
+ return (char*)malloc(12);
+}
+void use_ret() {
+ char *v;
+ v = malloc_wrapper_ret();
+}
+
// CHECK: <?xml version="1.0" encoding="UTF-8"?>
// CHECK: <plist version="1.0">
// CHECK: <dict>
@@ -1104,9 +1129,9 @@ void *test_double_action_call() {
// CHECK: </array>
// CHECK: <key>depth</key><integer>1</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Returning from &apos;wrapper&apos;, which allocated memory</string>
+// CHECK: <string>Returning from &apos;wrapper&apos;; allocated memory returned</string>
// CHECK: <key>message</key>
-// CHECK: <string>Returning from &apos;wrapper&apos;, which allocated memory</string>
+// CHECK: <string>Returning from &apos;wrapper&apos;; allocated memory returned</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
@@ -1178,12 +1203,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>line</key><integer>61</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>line</key><integer>61</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1191,12 +1216,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1208,7 +1233,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1216,12 +1241,12 @@ void *test_double_action_call() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>28</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1237,7 +1262,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>53</integer>
+// CHECK: <key>line</key><integer>54</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1255,12 +1280,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>53</integer>
+// CHECK: <key>line</key><integer>54</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>53</integer>
+// CHECK: <key>line</key><integer>54</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1268,12 +1293,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1289,12 +1314,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1302,12 +1327,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1319,7 +1344,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1327,12 +1352,12 @@ void *test_double_action_call() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1352,12 +1377,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1365,12 +1390,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1386,12 +1411,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1399,12 +1424,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1416,7 +1441,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1424,12 +1449,12 @@ void *test_double_action_call() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>17</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1445,7 +1470,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1463,12 +1488,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>1</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1476,12 +1501,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>52</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>52</integer>
// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1493,7 +1518,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>52</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1501,12 +1526,12 @@ void *test_double_action_call() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>52</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>52</integer>
// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1522,7 +1547,7 @@ void *test_double_action_call() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1530,12 +1555,12 @@ void *test_double_action_call() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>17</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1543,9 +1568,9 @@ void *test_double_action_call() {
// CHECK: </array>
// CHECK: <key>depth</key><integer>2</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Returning from &apos;my_free&apos;, which released memory</string>
+// CHECK: <string>Returning from &apos;my_free&apos;; released memory via 1st parameter</string>
// CHECK: <key>message</key>
-// CHECK: <string>Returning from &apos;my_free&apos;, which released memory</string>
+// CHECK: <string>Returning from &apos;my_free&apos;; released memory via 1st parameter</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
@@ -1555,12 +1580,12 @@ void *test_double_action_call() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>17</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1568,12 +1593,12 @@ void *test_double_action_call() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: