Skip to content

Commit

Permalink
[analyzer] Mark lines as relevant even if they weren't executed but h…
Browse files Browse the repository at this point in the history
…ave a label attached

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

llvm-svn: 323251
  • Loading branch information
George Karpenkov committed Jan 23, 2018
1 parent d5e8a26 commit 6d0dd76
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 92 deletions.
205 changes: 113 additions & 92 deletions clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
Expand Up @@ -28,6 +28,8 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <set>
#include <sstream>

using namespace clang;
Expand Down Expand Up @@ -96,11 +98,6 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
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);

/// \return Javascript for displaying shortcuts help;
std::string showHelpJavascript();
Expand Down Expand Up @@ -333,6 +330,115 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
return os.str();
}

/// Write executed lines from \p D in JSON format into \p os.
static void serializeExecutedLines(
const PathDiagnostic &D,
const PathPieces &path,
llvm::raw_string_ostream &os) {

// Copy executed lines from path diagnostics.
std::map<unsigned, std::set<unsigned>> ExecutedLines;
for (auto I = D.executedLines_begin(),
E = D.executedLines_end(); I != E; ++I) {
std::set<unsigned> &LinesInFile = ExecutedLines[I->first];
for (unsigned LineNo : I->second) {
LinesInFile.insert(LineNo);
}
}

// We need to include all lines for which any kind of diagnostics appears.
for (const auto &P : path) {
FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc();
FileID FID = Loc.getFileID();
unsigned LineNo = Loc.getLineNumber();
ExecutedLines[FID.getHashValue()].insert(LineNo);
}

os << "var relevant_lines = {";
for (auto I = ExecutedLines.begin(),
E = ExecutedLines.end(); I != E; ++I) {
if (I != ExecutedLines.begin())
os << ", ";

os << "\"" << I->first << "\": {";
for (unsigned LineNo : I->second) {
if (LineNo != *(I->second.begin()))
os << ", ";

os << "\"" << LineNo << "\": 1";
}
os << "}";
}

os << "};";
}

/// \return JavaScript for an option to only show relevant lines.
static std::string showRelevantLinesJavascript(
const PathDiagnostic &D, const PathPieces &path) {
std::string s;
llvm::raw_string_ostream os(s);
os << "<script type='text/javascript'>\n";
serializeExecutedLines(D, path, os);
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" id="showCounterexample" />
<label for="showCounterexample">
Show only relevant lines
</label>
</form>
)<<<";

return os.str();
}

void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
const SourceManager& SMgr, const PathPieces& path, FileID FID,
const FileEntry *Entry, const char *declName) {
Expand All @@ -357,7 +463,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,

// Checkbox and javascript for filtering the output to the counterexample.
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
showRelevantLinesJavascript(D));
showRelevantLinesJavascript(D, path));

// Add the name of the file as an <h1> tag.
{
Expand Down Expand Up @@ -507,93 +613,8 @@ window.addEventListener("keydown", function (event) {
)<<<";
}

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) {
Expand Down Expand Up @@ -1007,7 +1028,7 @@ window.addEventListener("keydown", function (event) {
navigateTo(/*up=*/true);
} else {
return;
}
}
event.preventDefault();
}, true);
</script>
Expand Down
@@ -0,0 +1,12 @@
int f() {
int zzz = 200;
zzz += 100;
return 0;
}

// Show line with the warning even if it wasn't executed (e.g. warning given
// by path-insensitive analysis).
// RUN: rm -rf %t.output
// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,deadcode -analyzer-output html -o %t.output %s
// RUN: cat %t.output/* | FileCheck %s --match-full-lines
// CHECK: var relevant_lines = {"1": {"3": 1}};

0 comments on commit 6d0dd76

Please sign in to comment.