From 96f1061fbe59faff5b266a3a04061cefcfe03e2f Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Mon, 3 Jun 2013 22:59:48 +0000 Subject: [PATCH] [analyzer; new edges] Include context for edges to sub-expressions. The current edge-generation algorithm sometimes creates edges from a top-level statement A to a sub-expression B.1 that's not at the start of B. This creates a "swoosh" effect where the arrow is drawn on top of the text at the start of B. In these cases, the results are clearer if we see an edge from A to B, then another one from B to B.1. Admittedly, this does create a /lot/ of arrows, some of which merely hop into a subexpression and then out again for a single note. The next commit will eliminate these if the subexpression is simple enough. This updates and reuses some of the infrastructure from the old edge- generation algorithm to find the "enclosing statement" context for a given expression. One change in particular marks the context of the LHS or RHS of a logical binary operator (&&, ||) as the entire operator expression, rather than the subexpression itself. This matches our behavior for ?:, and allows us to handle nested context information. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183159 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/StaticAnalyzer/Core/BugReporter.cpp | 431 +++++++---------- test/Analysis/edges-new.mm | 612 ++++++++++++++++++++++-- 2 files changed, 738 insertions(+), 305 deletions(-) diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 86b6daf030..2c6c657ba3 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -350,42 +350,39 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, return Loc; } -static bool IsNested(const Stmt *S, ParentMap &PM) { +static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) { if (isa(S) && PM.isConsumedExpr(cast(S))) - return true; + return PM.getParentIgnoreParens(S); const Stmt *Parent = PM.getParentIgnoreParens(S); + if (!Parent) + return 0; - if (Parent) - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::DoStmtClass: - case Stmt::WhileStmtClass: - return true; - default: - break; - } + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::DoStmtClass: + case Stmt::WhileStmtClass: + case Stmt::ObjCForCollectionStmtClass: + return Parent; + default: + break; + } - return false; + return 0; } -PathDiagnosticLocation -PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt *passed to getEnclosingStmtLocation"); - ParentMap &P = getParentMap(); - SourceManager &SMgr = getSourceManager(); - - while (IsNested(S, P)) { - const Stmt *Parent = P.getParentIgnoreParens(S); - - if (!Parent) - break; +static PathDiagnosticLocation +getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, + const LocationContext *LC) { + if (!S) + return PathDiagnosticLocation(); + while (const Stmt *Parent = getEnclosingParent(S, P)) { switch (Parent->getStmtClass()) { case Stmt::BinaryOperatorClass: { const BinaryOperator *B = cast(Parent); if (B->isLogicalOp()) - return PathDiagnosticLocation(S, SMgr, LC); + return PathDiagnosticLocation(Parent, SMgr, LC); break; } case Stmt::CompoundStmtClass: @@ -433,33 +430,15 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); - // Special case: DeclStmts can appear in for statement declarations, in which - // case the ForStmt is the context. - if (isa(S)) { - if (const Stmt *Parent = P.getParent(S)) { - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::ObjCForCollectionStmtClass: - return PathDiagnosticLocation(Parent, SMgr, LC); - default: - break; - } - } - } - else if (isa(S)) { - // Special case: the binary operator represents the initialization - // code in a for statement (this can happen when the variable being - // initialized is an old variable. - if (const ForStmt *FS = - dyn_cast_or_null(P.getParentIgnoreParens(S))) { - if (FS->getInit() == S) - return PathDiagnosticLocation(FS, SMgr, LC); - } - } - return PathDiagnosticLocation(S, SMgr, LC); } +PathDiagnosticLocation +PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { + assert(S && "Null Stmt passed to getEnclosingStmtLocation"); + return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC); +} + //===----------------------------------------------------------------------===// // "Visitors only" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// @@ -1837,7 +1816,7 @@ static const Stmt *getLocStmt(PathDiagnosticLocation L) { return L.asStmt(); } -static const Stmt *getStmtParent(const Stmt *S, ParentMap &PM) { +static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) { if (!S) return 0; @@ -1959,6 +1938,149 @@ void PathPieces::dump() const { } } +/// Adds synthetic edges from top-level statements to their subexpressions. +/// +/// This avoids a "swoosh" effect, where an edge from a top-level statement A +/// points to a sub-expression B.1 that's not at the start of B. In these cases, +/// we'd like to see an edge from A to B, then another one from B to B.1. +static void addContextEdges(PathPieces &pieces, SourceManager &SM, + const ParentMap &PM, const LocationContext *LCtx) { + PathPieces::iterator Prev = pieces.end(); + for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E; + Prev = I, ++I) { + PathDiagnosticControlFlowPiece *Piece = + dyn_cast(*I); + + if (!Piece) + continue; + + PathDiagnosticLocation SrcLoc = Piece->getStartLocation(); + const Stmt *Src = getLocStmt(SrcLoc); + PathDiagnosticLocation SrcContext = + getEnclosingStmtLocation(Src, SM, PM, LCtx); + + // Repeatedly split the edge as necessary. + // This is important for nested logical expressions (||, &&, ?:) where we + // want to show all the levels of context. + while (true) { + const Stmt *Dst = getLocStmt(Piece->getEndLocation()); + + // We are looking at an edge. Is the destination within a larger + // expression? + PathDiagnosticLocation DstContext = + getEnclosingStmtLocation(Dst, SM, PM, LCtx); + if (!DstContext.isValid() || DstContext.asStmt() == Dst) + break; + + // If the source is in the same context, we're already good. + if (SrcContext == DstContext) + break; + + // Update the subexpression node to point to the context edge. + Piece->setStartLocation(DstContext); + + // Try to extend the previous edge if it's at the same level as the source + // context. + if (Prev != E) { + PathDiagnosticControlFlowPiece *PrevPiece = + dyn_cast(*Prev); + + if (PrevPiece) { + if (const Stmt *PrevSrc = getLocStmt(PrevPiece->getStartLocation())) { + const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM); + if (PrevSrcParent == getStmtParent(getLocStmt(DstContext), PM)) { + PrevPiece->setEndLocation(DstContext); + break; + } + } + } + } + + // Otherwise, split the current edge into a context edge and a + // subexpression edge. Note that the context statement may itself have + // context. + Piece = new PathDiagnosticControlFlowPiece(SrcLoc, DstContext); + I = pieces.insert(I, Piece); + } + } +} + +/// \brief Move edges from a branch condition to a branch target +/// when the condition is simple. +/// +/// This restructures some of the work of addContextEdges. That function +/// creates edges this may destroy, but they work together to create a more +/// aesthetically set of edges around branches. After the call to +/// addContextEdges, we may have (1) an edge to the branch, (2) an edge from +/// the branch to the branch condition, and (3) an edge from the branch +/// condition to the branch target. We keep (1), but may wish to remove (2) +/// and move the source of (3) to the branch if the branch condition is simple. +/// +static void simplifySimpleBranches(PathPieces &pieces) { + for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) { + + PathDiagnosticControlFlowPiece *PieceI = + dyn_cast(*I); + + if (!PieceI) + continue; + + const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); + const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); + + if (!s1Start || !s1End) + continue; + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + break; + + PathDiagnosticControlFlowPiece *PieceNextI = 0; + + while (true) { + if (NextI == E) + break; + + PathDiagnosticEventPiece *EV = dyn_cast(*NextI); + if (EV) { + StringRef S = EV->getString(); + if (S == StrEnteringLoop || S == StrLoopBodyZero) { + ++NextI; + continue; + } + break; + } + + PieceNextI = dyn_cast(*NextI); + break; + } + + if (!PieceNextI) + continue; + + const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); + const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); + + if (!s2Start || !s2End || s1End != s2Start) + continue; + + // We only perform this transformation for specific branch kinds. + // We don't want to do this for do..while, for example. + if (!(isa(s1Start) || isa(s1Start) || + isa(s1Start) || isa(s1Start))) + continue; + + // Is s1End the branch condition? + if (!isConditionForTerminator(s1Start, s1End)) + continue; + + // Perform the hoisting by eliminating (2) and changing the start + // location of (3). + PieceNextI->setStartLocation(PieceI->getStartLocation()); + I = pieces.erase(I); + } +} + /// \brief Return true if X is contained by Y. static bool lexicalContains(ParentMap &PM, const Stmt *X, @@ -2206,6 +2328,12 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, } if (!hasChanges) { + // Adjust edges into subexpressions to make them more uniform + // and aesthetically pleasing. + addContextEdges(path, SM, PM, LC); + // Hoist edges originating from branch conditions to branches + // for simple branches. + simplifySimpleBranches(path); // Remove any puny edges left over after primary optimization pass. removePunyEdges(path, SM, PM); // Remove identical events. @@ -2215,203 +2343,6 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, return hasChanges; } -/// \brief Split edges incident on a branch condition into two edges. -/// -/// The first edge is incident on the branch statement, the second on the -/// condition. -static void splitBranchConditionEdges(PathPieces &pieces, - LocationContextMap &LCM, - SourceManager &SM) { - // Retrieve the parent map for this path. - const LocationContext *LC = LCM[&pieces]; - ParentMap &PM = LC->getParentMap(); - PathPieces::iterator Prev = pieces.end(); - for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; - Prev = I, ++I) { - // Adjust edges in subpaths. - if (PathDiagnosticCallPiece *Call = dyn_cast(*I)) { - splitBranchConditionEdges(Call->path, LCM, SM); - continue; - } - - PathDiagnosticControlFlowPiece *PieceI = - dyn_cast(*I); - - if (!PieceI) - continue; - - // We are looking at two edges. Is the second one incident - // on an expression (or subexpression) of a branch condition. - const Stmt *Dst = getLocStmt(PieceI->getEndLocation()); - const Stmt *Src = getLocStmt(PieceI->getStartLocation()); - - if (!Dst || !Src) - continue; - - const Stmt *Branch = 0; - const Stmt *S = Dst; - while (const Stmt *Parent = getStmtParent(S, PM)) { - if (const ForStmt *FS = dyn_cast(Parent)) { - const Stmt *Cond = FS->getCond(); - if (!Cond) - Cond = FS; - if (Cond == S) - Branch = FS; - break; - } - if (const WhileStmt *WS = dyn_cast(Parent)) { - if (WS->getCond()->IgnoreParens() == S) - Branch = WS; - break; - } - if (const IfStmt *IS = dyn_cast(Parent)) { - if (IS->getCond()->IgnoreParens() == S) - Branch = IS; - break; - } - if (const ObjCForCollectionStmt *OFS = - dyn_cast(Parent)) { - if (OFS->getElement() == S) - Branch = OFS; - break; - } - if (const BinaryOperator *BO = dyn_cast(Parent)) { - if (BO->isLogicalOp()) { - if (BO->getLHS()->IgnoreParens() == S) - Branch = BO; - break; - } - } - - S = Parent; - } - - // If 'Branch' is non-null we have found a match where we have an edge - // incident on the condition of a if/for/while statement. - if (!Branch) - continue; - - // If the current source of the edge is the if/for/while, then there is - // nothing left to be done. - if (Src == Branch) - continue; - - // Now look at the previous edge. We want to know if this was in the same - // "level" as the for statement. - const Stmt *BranchParent = getStmtParent(Branch, PM); - PathDiagnosticLocation L(Branch, SM, LC); - bool needsEdge = true; - - if (Prev != E) { - if (PathDiagnosticControlFlowPiece *P = - dyn_cast(*Prev)) { - const Stmt *PrevSrc = getLocStmt(P->getStartLocation()); - if (PrevSrc) { - const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM); - if (PrevSrcParent == BranchParent) { - P->setEndLocation(L); - needsEdge = false; - } - } - } - } - - if (needsEdge) { - PathDiagnosticControlFlowPiece *P = - new PathDiagnosticControlFlowPiece(PieceI->getStartLocation(), L); - pieces.insert(I, P); - } - - PieceI->setStartLocation(L); - } -} - -/// \brief Move edges from a branch condition to a branch target -/// when the condition is simple. -/// -/// This is the dual of splitBranchConditionEdges. That function creates -/// edges this may destroy, but they work together to create a more -/// aesthetically set of edges around branches. After the call to -/// splitBranchConditionEdges, we may have (1) an edge to the branch, -/// (2) an edge from the branch to the branch condition, and (3) an edge from -/// the branch condition to the branch target. We keep (1), but may wish -/// to remove (2) and move the source of (3) to the branch if the branch -/// condition is simple. -/// -static void simplifySimpleBranches(PathPieces &pieces) { - - - for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) { - // Adjust edges in subpaths. - if (PathDiagnosticCallPiece *Call = dyn_cast(*I)) { - simplifySimpleBranches(Call->path); - continue; - } - - PathDiagnosticControlFlowPiece *PieceI = - dyn_cast(*I); - - if (!PieceI) - continue; - - const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); - const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); - - if (!s1Start || !s1End) - continue; - - PathPieces::iterator NextI = I; ++NextI; - if (NextI == E) - break; - - PathDiagnosticControlFlowPiece *PieceNextI = 0; - - while (true) { - if (NextI == E) - break; - - PathDiagnosticEventPiece *EV = dyn_cast(*NextI); - if (EV) { - StringRef S = EV->getString(); - if (S == StrEnteringLoop || S == StrLoopBodyZero) { - ++NextI; - continue; - } - break; - } - - PieceNextI = dyn_cast(*NextI); - break; - } - - if (!PieceNextI) - continue; - - const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); - const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); - - if (!s2Start || !s2End || s1End != s2Start) - continue; - - // We only perform this transformation for specific branch kinds. - // We do want to do this for do..while, for example. - if (!(isa(s1Start) || isa(s1Start) || - isa(s1Start) || isa(s1Start))) - continue; - - // Is s1End the branch condition? - if (!isConditionForTerminator(s1Start, s1End)) - continue; - - // Perform the hoisting by eliminating (2) and changing the start - // location of (3). - PathDiagnosticLocation L = PieceI->getStartLocation(); - pieces.erase(I); - I = NextI; - PieceNextI->setStartLocation(L); - } -} - /// Drop the very first edge in a path, which should be a function entry edge. static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM, @@ -3121,14 +3052,6 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, OptimizedCallsSet OCS; while (optimizeEdges(PD.getMutablePieces(), SM, OCS, LCM)) {} - // Adjust edges into loop conditions to make them more uniform - // and aesthetically pleasing. - splitBranchConditionEdges(PD.getMutablePieces(), LCM, SM); - - // Hoist edges originating from branch conditions to branches - // for simple branches. - simplifySimpleBranches(PD.getMutablePieces()); - // Drop the very first function-entry edge. It's not really necessary // for top-level functions. dropFunctionEntryEdge(PD.getMutablePieces(), LCM, SM); diff --git a/test/Analysis/edges-new.mm b/test/Analysis/edges-new.mm index ebbb097dc7..36ecf8e42d 100644 --- a/test/Analysis/edges-new.mm +++ b/test/Analysis/edges-new.mm @@ -1031,6 +1031,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -1174,6 +1208,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line74 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line74 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line74 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line74 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -1388,6 +1456,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line81 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line81 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line81 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line81 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line81 // CHECK-NEXT: col24 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1557,6 +1659,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -2036,6 +2172,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line103 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line103 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line103 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line103 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line103 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -2116,6 +2286,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 // CHECK-NEXT: col23 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -2854,12 +3058,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line143 -// CHECK-NEXT: col24 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line143 -// CHECK-NEXT: col24 +// CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -6446,12 +6650,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line238 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line238 -// CHECK-NEXT: col7 +// CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -6467,12 +6671,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line238 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line238 -// CHECK-NEXT: col7 +// CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -6691,12 +6895,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line245 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line245 -// CHECK-NEXT: col7 +// CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -6712,12 +6916,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line245 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line245 -// CHECK-NEXT: col7 +// CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -6886,35 +7090,6 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line251 -// CHECK-NEXT: col11 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line251 -// CHECK-NEXT: col11 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line251 -// CHECK-NEXT: col11 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Assuming 'x' is not equal to 0 -// CHECK-NEXT: message -// CHECK-NEXT: Assuming 'x' is not equal to 0 -// CHECK-NEXT: -// CHECK-NEXT: // CHECK-NEXT: kindcontrol // CHECK-NEXT: edges // CHECK-NEXT: @@ -6923,12 +7098,75 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line251 -// CHECK-NEXT: col11 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line251 -// CHECK-NEXT: col11 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming 'x' is not equal to 0 +// CHECK-NEXT: message +// CHECK-NEXT: Assuming 'x' is not equal to 0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line251 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -7004,6 +7242,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line253 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line253 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line253 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line253 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line253 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -7228,6 +7500,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -7468,6 +7774,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line278 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line278 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line278 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line278 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -7518,6 +7858,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line280 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line280 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line280 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line280 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line280 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -7656,12 +8030,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line289 -// CHECK-NEXT: col5 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line289 -// CHECK-NEXT: col5 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -7778,6 +8152,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line305 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line305 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line305 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line305 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -7828,6 +8236,40 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line308 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line308 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line308 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line308 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line308 // CHECK-NEXT: col16 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -7983,12 +8425,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line309 -// CHECK-NEXT: col5 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line309 -// CHECK-NEXT: col5 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -8577,6 +9019,40 @@ void variousLoops(id input) { // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line368 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line368 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line368 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line368 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -8762,12 +9238,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line388 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line388 -// CHECK-NEXT: col7 +// CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -8997,12 +9473,46 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line398 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line398 -// CHECK-NEXT: col21 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -9188,12 +9698,12 @@ void variousLoops(id input) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line413 -// CHECK-NEXT: col10 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line413 -// CHECK-NEXT: col10 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -- 2.40.0