r322612 - [analyzer] support a mode to only show relevant lines in HTML diagnostics

George Karpenkov via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 16 18:59:12 PST 2018


Author: george.karpenkov
Date: Tue Jan 16 18:59:11 2018
New Revision: 322612

URL: http://llvm.org/viewvc/llvm-project?rev=322612&view=rev
Log:
[analyzer] support a mode to only show relevant lines in HTML diagnostics

HTML diagnostics can be an overwhelming blob of pages of code.
This patch adds a checkbox which filters this list down to only the
lines *relevant* to the counterexample by e.g. skipping branches which
analyzer has assumed to be infeasible at a time.

The resulting amount of output is much smaller, and often fits on one
screen, and also provides a much more readable diagnostics.

Differential Revision: https://reviews.llvm.org/D41378

Added:
    cfe/trunk/test/Analysis/html_diagnostics/
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/header.h
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/macros_same_file.c
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multifile.c
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multiline_func_def.c
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/objcmethods.m
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/simple_conditional.c
    cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/unused_header.c
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
    cfe/trunk/lib/Rewrite/HTMLRewrite.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h?rev=322612&r1=322611&r2=322612&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h Tue Jan 16 18:59:11 2018
@@ -23,6 +23,8 @@
 #include <deque>
 #include <iterator>
 #include <list>
+#include <map>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -733,6 +735,9 @@ public:
   void Profile(llvm::FoldingSetNodeID &ID) const override;
 };
 
+/// File IDs mapped to sets of line numbers.
+typedef std::map<unsigned, std::set<unsigned>> FilesToLineNumsMap;
+
 /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
 ///  diagnostic.  It represents an ordered-collection of PathDiagnosticPieces,
 ///  each which represent the pieces of the path.
@@ -756,12 +761,16 @@ class PathDiagnostic : public llvm::Fold
   PathDiagnosticLocation UniqueingLoc;
   const Decl *UniqueingDecl;
 
+  /// Lines executed in the path.
+  std::unique_ptr<FilesToLineNumsMap> ExecutedLines;
+
   PathDiagnostic() = delete;
 public:
   PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue,
                  StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
                  StringRef category, PathDiagnosticLocation LocationToUnique,
-                 const Decl *DeclToUnique);
+                 const Decl *DeclToUnique,
+                 std::unique_ptr<FilesToLineNumsMap> ExecutedLines);
 
   ~PathDiagnostic();
   
@@ -830,6 +839,12 @@ public:
   meta_iterator meta_end() const { return OtherDesc.end(); }
   void addMeta(StringRef s) { OtherDesc.push_back(s); }
 
+  typedef FilesToLineNumsMap::const_iterator filesmap_iterator;
+  filesmap_iterator executedLines_begin() const {
+    return ExecutedLines->begin();
+  }
+  filesmap_iterator executedLines_end() const { return ExecutedLines->end(); }
+
   PathDiagnosticLocation getLocation() const {
     assert(Loc.isValid() && "No report location set yet!");
     return Loc;

Modified: cfe/trunk/lib/Rewrite/HTMLRewrite.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Rewrite/HTMLRewrite.cpp?rev=322612&r1=322611&r2=322612&view=diff
==============================================================================
--- cfe/trunk/lib/Rewrite/HTMLRewrite.cpp (original)
+++ cfe/trunk/lib/Rewrite/HTMLRewrite.cpp Tue Jan 16 18:59:11 2018
@@ -210,9 +210,9 @@ static void AddLineNumber(RewriteBuffer
   SmallString<256> Str;
   llvm::raw_svector_ostream OS(Str);
 
-  OS << "<tr><td class=\"num\" id=\"LN"
-     << LineNo << "\">"
-     << LineNo << "</td><td class=\"line\">";
+  OS << "<tr class=\"codeline\" data-linenumber=\"" << LineNo << "\">"
+     << "<td class=\"num\" id=\"LN" << LineNo << "\">" << LineNo
+     << "</td><td class=\"line\">";
 
   if (B == E) { // Handle empty lines.
     OS << " </td></tr>";
@@ -263,7 +263,10 @@ void html::AddLineNumbers(Rewriter& R, F
   }
 
   // Add one big table tag that surrounds all of the code.
-  RB.InsertTextBefore(0, "<table class=\"code\">\n");
+  std::string s;
+  llvm::raw_string_ostream os(s);
+  os << "<table class=\"code\" data-fileid=\"" << FID.getHashValue() << "\">\n";
+  RB.InsertTextBefore(0, os.str());
   RB.InsertTextAfter(FileEnd - FileBeg, "</table>");
 }
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp?rev=322612&r1=322611&r2=322612&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp Tue Jan 16 18:59:11 2018
@@ -3509,6 +3509,66 @@ void BugReporter::FlushReport(BugReportE
   }
 }
 
+/// Insert all lines participating in the function signature \p Signature
+/// into \p ExecutedLines.
+static void populateExecutedLinesWithFunctionSignature(
+    const Decl *Signature, SourceManager &SM,
+    std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) {
+
+  SourceRange SignatureSourceRange;
+  const Stmt* Body = Signature->getBody();
+  if (auto FD = dyn_cast<FunctionDecl>(Signature)) {
+    SignatureSourceRange = FD->getSourceRange();
+  } else if (auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
+    SignatureSourceRange = OD->getSourceRange();
+  } else {
+    return;
+  }
+  SourceLocation Start = SignatureSourceRange.getBegin();
+  SourceLocation End = Body ? Body->getSourceRange().getBegin()
+    : SignatureSourceRange.getEnd();
+  unsigned StartLine = SM.getExpansionLineNumber(Start);
+  unsigned EndLine = SM.getExpansionLineNumber(End);
+
+  FileID FID = SM.getFileID(SM.getExpansionLoc(Start));
+  for (unsigned Line = StartLine; Line <= EndLine; Line++)
+    ExecutedLines->operator[](FID.getHashValue()).insert(Line);
+}
+
+/// \return all executed lines including function signatures on the path
+/// starting from \p N.
+static std::unique_ptr<FilesToLineNumsMap>
+findExecutedLines(SourceManager &SM, const ExplodedNode *N) {
+  auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>();
+
+  while (N) {
+    if (N->getFirstPred() == nullptr) {
+
+      // First node: show signature of the entrance point.
+      const Decl *D = N->getLocationContext()->getDecl();
+      populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines);
+
+    } else if (auto CE = N->getLocationAs<CallEnter>()) {
+
+      // Inlined function: show signature.
+      const Decl* D = CE->getCalleeContext()->getDecl();
+      populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines);
+
+    } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+
+      // Otherwise: show lines associated with the processed statement.
+      SourceLocation Loc = S->getSourceRange().getBegin();
+      SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
+      FileID FID = SM.getFileID(ExpansionLoc);
+      unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc);
+      ExecutedLines->operator[](FID.getHashValue()).insert(LineNo);
+    }
+
+    N = N->getFirstPred();
+  }
+  return ExecutedLines;
+}
+
 void BugReporter::FlushReport(BugReport *exampleReport,
                               PathDiagnosticConsumer &PD,
                               ArrayRef<BugReport*> bugReports) {
@@ -3517,13 +3577,13 @@ void BugReporter::FlushReport(BugReport
   // Probably doesn't make a difference in practice.
   BugType& BT = exampleReport->getBugType();
 
-  std::unique_ptr<PathDiagnostic> D(new PathDiagnostic(
+  auto D = llvm::make_unique<PathDiagnostic>(
       exampleReport->getBugType().getCheckName(),
       exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(),
       exampleReport->getDescription(),
       exampleReport->getShortDescription(/*Fallback=*/false), BT.getCategory(),
-      exampleReport->getUniqueingLocation(),
-      exampleReport->getUniqueingDecl()));
+      exampleReport->getUniqueingLocation(), exampleReport->getUniqueingDecl(),
+      findExecutedLines(getSourceManager(), exampleReport->getErrorNode()));
 
   if (exampleReport->isPathSensitive()) {
     // Generate the full path diagnostic, using the generation scheme

Modified: cfe/trunk/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp?rev=322612&r1=322611&r2=322612&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp Tue Jan 16 18:59:11 2018
@@ -94,6 +94,13 @@ public:
 
   /// \return Javascript for navigating the HTML report using j/k keys.
   std::string generateKeyboardNavigationJavascript();
+
+private:
+  /// \return JavaScript for an option to only show relevant lines.
+  std::string showRelevantLinesJavascript(const PathDiagnostic &D);
+
+  /// \return Executed lines from \p D in JSON format.
+  std::string serializeExecutedLines(const PathDiagnostic &D);
 };
 
 } // end anonymous namespace
@@ -343,6 +350,10 @@ void HTMLDiagnostics::FinalizeHTML(const
   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
                      generateKeyboardNavigationJavascript());
 
+  // Checkbox and javascript for filtering the output to the counterexample.
+  R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+                     showRelevantLinesJavascript(D));
+
   // Add the name of the file as an <h1> tag.
   {
     std::string s;
@@ -450,6 +461,94 @@ void HTMLDiagnostics::FinalizeHTML(const
   html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
 }
 
+std::string
+HTMLDiagnostics::showRelevantLinesJavascript(const PathDiagnostic &D) {
+  std::string s;
+  llvm::raw_string_ostream os(s);
+  os << "<script type='text/javascript'>\n";
+  os << serializeExecutedLines(D);
+  os << R"<<<(
+
+var filterCounterexample = function (hide) {
+  var tables = document.getElementsByClassName("code");
+  for (var t=0; t<tables.length; t++) {
+    var table = tables[t];
+    var file_id = table.getAttribute("data-fileid");
+    var lines_in_fid = relevant_lines[file_id];
+    if (!lines_in_fid) {
+      lines_in_fid = {};
+    }
+    var lines = table.getElementsByClassName("codeline");
+    for (var i=0; i<lines.length; i++) {
+        var el = lines[i];
+        var lineNo = el.getAttribute("data-linenumber");
+        if (!lines_in_fid[lineNo]) {
+          if (hide) {
+            el.setAttribute("hidden", "");
+          } else {
+            el.removeAttribute("hidden");
+          }
+        }
+    }
+  }
+}
+
+window.addEventListener("keydown", function (event) {
+  if (event.defaultPrevented) {
+    return;
+  }
+  if (event.key == "S") {
+    var checked = document.getElementsByName("showCounterexample")[0].checked;
+    filterCounterexample(!checked);
+    document.getElementsByName("showCounterexample")[0].checked = !checked;
+  } else {
+    return;
+  } 
+  event.preventDefault();
+}, true);
+
+document.addEventListener("DOMContentLoaded", function() {
+    document.querySelector('input[name="showCounterexample"]').onchange=
+        function (event) {
+      filterCounterexample(this.checked);
+    };
+});
+</script>
+
+<form>
+    <input type="checkbox" name="showCounterexample" />
+    <label for="showCounterexample">
+       Show only relevant lines
+    </label>
+</form>
+)<<<";
+
+  return os.str();
+}
+
+std::string HTMLDiagnostics::serializeExecutedLines(const PathDiagnostic &D) {
+  std::string s;
+  llvm::raw_string_ostream os(s);
+  os << "var relevant_lines = {";
+  for (auto I = D.executedLines_begin(),
+            E = D.executedLines_end(); I != E; ++I) {
+    if (I != D.executedLines_begin())
+      os << ", ";
+
+    os << "\"" << I->first << "\": {";
+    for (unsigned LineNo : I->second) {
+      if (LineNo != *(I->second.begin()))
+        os << ", ";
+
+      os << "\"" << LineNo << "\": 1";
+    }
+    os << "}";
+  }
+
+  os << "};";
+  return os.str();
+}
+
 void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr,
     const PathPieces& path, FileID FID) {
   // Process the path.

Modified: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp?rev=322612&r1=322611&r2=322612&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp Tue Jan 16 18:59:11 2018
@@ -98,20 +98,18 @@ void PathPieces::flattenTo(PathPieces &P
 
 PathDiagnostic::~PathDiagnostic() {}
 
-PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue,
-                               StringRef bugtype, StringRef verboseDesc,
-                               StringRef shortDesc, StringRef category,
-                               PathDiagnosticLocation LocationToUnique,
-                               const Decl *DeclToUnique)
-  : CheckName(CheckName),
-    DeclWithIssue(declWithIssue),
-    BugType(StripTrailingDots(bugtype)),
-    VerboseDesc(StripTrailingDots(verboseDesc)),
-    ShortDesc(StripTrailingDots(shortDesc)),
-    Category(StripTrailingDots(category)),
-    UniqueingLoc(LocationToUnique),
-    UniqueingDecl(DeclToUnique),
-    path(pathImpl) {}
+PathDiagnostic::PathDiagnostic(
+    StringRef CheckName, const Decl *declWithIssue, StringRef bugtype,
+    StringRef verboseDesc, StringRef shortDesc, StringRef category,
+    PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,
+    std::unique_ptr<FilesToLineNumsMap> ExecutedLines)
+    : CheckName(CheckName), DeclWithIssue(declWithIssue),
+      BugType(StripTrailingDots(bugtype)),
+      VerboseDesc(StripTrailingDots(verboseDesc)),
+      ShortDesc(StripTrailingDots(shortDesc)),
+      Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),
+      UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)),
+      path(pathImpl) {}
 
 static PathDiagnosticCallPiece *
 getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/header.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/header.h?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/header.h (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/header.h Tue Jan 16 18:59:11 2018
@@ -0,0 +1,12 @@
+#define deref(X) (*X)
+
+char helper(
+    char *out,
+    int doDereference) {
+  if (doDereference) {
+    return deref(out);
+  } else {
+    return 'x';
+  }
+  return 'c';
+}

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/macros_same_file.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/macros_same_file.c?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/macros_same_file.c (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/macros_same_file.c Tue Jan 16 18:59:11 2018
@@ -0,0 +1,15 @@
+#define deref(X) (*X)
+
+int f(int coin) {
+  if (coin) {
+    int *x = 0;
+    return deref(x);
+  } else {
+    return 0;
+  }
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s
+// RUN: cat %t.output/* | FileCheck %s --match-full-lines
+// CHECK: var relevant_lines = {"1": {"3": 1, "4": 1, "5": 1, "6": 1}};

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multifile.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multifile.c?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multifile.c (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multifile.c Tue Jan 16 18:59:11 2018
@@ -0,0 +1,14 @@
+#include "header.h"
+
+int f(int coin) {
+  char *p = 0;
+  if (coin) {
+    return helper(p, coin);
+  }
+  return 0;
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s
+// RUN: cat %t.output/* | FileCheck %s --match-full-lines
+// CHECK: var relevant_lines = {"1": {"3": 1, "4": 1, "5": 1, "6": 1}, "3": {"3": 1, "4": 1, "5": 1, "6": 1, "7": 1}};

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multiline_func_def.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multiline_func_def.c?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multiline_func_def.c (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/multiline_func_def.c Tue Jan 16 18:59:11 2018
@@ -0,0 +1,16 @@
+int f(
+    int coin,
+    int paramA,
+    int paramB) {
+  if (coin) {
+    int *x = 0;
+    return *x;
+  } else {
+    return 0;
+  }
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s
+// RUN: cat %t.output/* | FileCheck %s --match-full-lines
+// CHECK: var relevant_lines = {"1": {"1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1}};

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/objcmethods.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/objcmethods.m?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/objcmethods.m (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/objcmethods.m Tue Jan 16 18:59:11 2018
@@ -0,0 +1,19 @@
+ at interface I
+- (int)func;
+ at end
+
+ at implementation I
+- (int)func:(int *)param {
+  return *param;
+}
+ at end
+
+void foo(I *i) {
+  int *x = 0;
+  [i func:x];
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output -Wno-objc-root-class %s
+// RUN: cat %t.output/* | FileCheck %s
+// CHECK: var relevant_lines = {"1": {"6": 1, "7": 1, "11": 1, "12": 1, "13": 1}};

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/simple_conditional.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/simple_conditional.c?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/simple_conditional.c (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/simple_conditional.c Tue Jan 16 18:59:11 2018
@@ -0,0 +1,13 @@
+int f(int coin) {
+  if (coin) {
+    int *x = 0;
+    return *x;
+  } else {
+    return 0;
+  }
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s
+// RUN: cat %t.output/* | FileCheck %s --match-full-lines
+// CHECK: var relevant_lines = {"1": {"1": 1, "2": 1, "3": 1, "4": 1}};

Added: cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/unused_header.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/unused_header.c?rev=322612&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/unused_header.c (added)
+++ cfe/trunk/test/Analysis/html_diagnostics/relevant_lines/unused_header.c Tue Jan 16 18:59:11 2018
@@ -0,0 +1,19 @@
+#include "header.h"
+
+int f(int coin) {
+  if (coin) {
+    int *x = 0;
+    return *x;
+  } else {
+    return 0;
+  }
+}
+
+int v(int coin) {
+  return coin;
+}
+
+// RUN: rm -rf %t.output
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s
+// RUN: cat %t.output/* | FileCheck %s --match-full-lines
+// CHECK: var relevant_lines = {"1": {"3": 1, "4": 1, "5": 1, "6": 1}};




More information about the cfe-commits mailing list