29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/Sequence.h"
32#include "llvm/ADT/SmallString.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/ADT/iterator_range.h"
35#include "llvm/Support/Casting.h"
36#include "llvm/Support/Errc.h"
37#include "llvm/Support/ErrorHandling.h"
38#include "llvm/Support/FileSystem.h"
39#include "llvm/Support/MemoryBuffer.h"
40#include "llvm/Support/Path.h"
41#include "llvm/Support/raw_ostream.h"
49#include <system_error>
66 std::string Directory;
67 bool createdDir =
false;
70 const bool SupportsCrossFileDiagnostics;
75 bool supportsMultipleFiles)
76 : DiagOpts(
std::move(DiagOpts)), Directory(OutputDir), PP(pp),
77 SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
82 FilesMade *filesMade)
override;
84 StringRef
getName()
const override {
return "HTMLDiagnostics"; }
87 return SupportsCrossFileDiagnostics;
98 const std::vector<SourceRange> &PopUpRanges,
unsigned num,
102 const char *HighlightStart =
"<span class=\"mrange\">",
103 const char *HighlightEnd =
"</span>");
110 const char *declName);
126 const ArrowMap &ArrowIndices);
129 StringRef showHelpJavascript();
132 StringRef generateKeyboardNavigationJavascript();
135 StringRef generateArrowDrawingJavascript();
143 llvm::raw_string_ostream &os);
147 return isa<PathDiagnosticControlFlowPiece>(
P) &&
P.getString().empty();
150unsigned getPathSizeWithoutArrows(
const PathPieces &Path) {
151 unsigned TotalPieces = Path.size();
152 unsigned TotalArrowPieces = llvm::count_if(
154 return TotalPieces - TotalArrowPieces;
157class ArrowMap :
public std::vector<unsigned> {
158 using Base = std::vector<unsigned>;
161 ArrowMap(
unsigned Size) :
Base(
Size, 0) {}
162 unsigned getTotalNumberOfArrows()
const {
return at(0); }
165llvm::raw_ostream &
operator<<(llvm::raw_ostream &OS,
const ArrowMap &Indices) {
167 llvm::interleave(Indices, OS,
",");
173void ento::createHTMLDiagnosticConsumer(
184 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
188 if (OutputDir.empty())
191 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
true));
194void ento::createHTMLSingleFileDiagnosticConsumer(
199 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
203 if (OutputDir.empty())
206 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
false));
209void ento::createPlistHTMLDiagnosticConsumer(
214 createHTMLDiagnosticConsumer(
215 DiagOpts,
C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,
217 createPlistMultiFileDiagnosticConsumer(DiagOpts,
C, prefix, PP, CTU,
219 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, prefix, PP,
220 CTU, MacroExpansions);
223void ento::createSarifHTMLDiagnosticConsumer(
228 createHTMLDiagnosticConsumer(
229 DiagOpts,
C, std::string(llvm::sys::path::parent_path(sarif_file)), PP,
230 CTU, MacroExpansions);
231 createSarifDiagnosticConsumer(DiagOpts,
C, sarif_file, PP, CTU,
233 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, sarif_file,
234 PP, CTU, MacroExpansions);
241void HTMLDiagnostics::FlushDiagnosticsImpl(
242 std::vector<const PathDiagnostic *> &Diags,
243 FilesMade *filesMade) {
244 for (
const auto Diag : Diags)
245 ReportDiag(*
Diag, filesMade);
261 FilesMade *filesMade) {
265 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
266 llvm::errs() <<
"warning: could not create directory '"
267 << Directory <<
"': " << ec.message() <<
'\n';
280 assert(!path.empty());
281 const SourceManager &SMgr = path.front()->getLocation().getManager();
290 if (
const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
291 declName = ND->getDeclName().getAsString();
293 if (
const Stmt *Body = DeclWithIssue->getBody()) {
300 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
304 std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
305 if (report.empty()) {
306 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
314 llvm::raw_svector_ostream
FileName(FileNameStr);
327 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
332 << declName.c_str() <<
"-" << offsetDecl <<
"-";
338 llvm::sys::path::append(ResultPath, Directory,
FileName.str());
339 if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
340 llvm::errs() <<
"warning: could not make '" << ResultPath
341 <<
"' absolute: " << EC.message() <<
'\n';
345 if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
346 ResultPath, FD, llvm::sys::fs::CD_CreateNew,
347 llvm::sys::fs::OF_Text)) {
353 if (EC != llvm::errc::file_exists) {
354 llvm::errs() <<
"warning: could not create file in '" << Directory
355 <<
"': " << EC.message() <<
'\n';
360 llvm::raw_fd_ostream os(FD,
true);
363 filesMade->addDiagnostic(D,
getName(),
364 llvm::sys::path::filename(ResultPath));
373 std::vector<FileID> FileIDs;
374 for (
auto I : path) {
375 FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
376 if (llvm::is_contained(FileIDs, FID))
379 FileIDs.push_back(FID);
380 RewriteFile(R, path, FID);
383 if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
385 for (
auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {
387 llvm::raw_string_ostream os(
s);
389 if (I != FileIDs.begin())
390 os <<
"<hr class=divider>\n";
392 os <<
"<div id=File" << I->getHashValue() <<
">\n";
395 if (I != FileIDs.begin())
396 os <<
"<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
397 <<
"\">←</a></div>";
404 os <<
"<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
405 <<
"\">→</a></div>";
413 for (
auto I : llvm::drop_begin(FileIDs)) {
415 llvm::raw_string_ostream os(
s);
431 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
433 FinalizeHTML(D, R, SMgr, path, FileIDs[0], *Entry, declName);
436 llvm::raw_string_ostream os(file);
443void HTMLDiagnostics::dumpCoverageData(
446 llvm::raw_string_ostream &os) {
450 os <<
"var relevant_lines = {";
451 for (
auto I = ExecutedLines.begin(),
452 E = ExecutedLines.end(); I != E; ++I) {
453 if (I != ExecutedLines.begin())
456 os <<
"\"" << I->first.getHashValue() <<
"\": {";
457 for (
unsigned LineNo : I->second) {
458 if (LineNo != *(I->second.begin()))
461 os <<
"\"" << LineNo <<
"\": 1";
469std::string HTMLDiagnostics::showRelevantLinesJavascript(
472 llvm::raw_string_ostream os(
s);
473 os <<
"<script type='text/javascript'>\n";
474 dumpCoverageData(D, path, os);
477var filterCounterexample = function (hide) {
478 var tables = document.getElementsByClassName("code");
479 for (var t=0; t<tables.length; t++) {
480 var table = tables[t];
481 var file_id = table.getAttribute("data-fileid");
482 var lines_in_fid = relevant_lines[file_id];
486 var lines = table.getElementsByClassName("codeline");
487 for (var i=0; i<lines.length; i++) {
489 var lineNo = el.getAttribute("data-linenumber");
490 if (!lines_in_fid[lineNo]) {
492 el.setAttribute("hidden", "");
494 el.removeAttribute("hidden");
501window.addEventListener("keydown", function (event) {
502 if (event.defaultPrevented) {
506 if (event.shiftKey && event.keyCode == 83) {
507 var checked = document.getElementsByName("showCounterexample")[0].checked;
508 filterCounterexample(!checked);
509 document.getElementsByName("showCounterexample")[0].click();
513 event.preventDefault();
516document.addEventListener("DOMContentLoaded", function() {
517 document.querySelector('input[name="showCounterexample"]').onchange=
519 filterCounterexample(this.checked);
525 <input type="checkbox" name="showCounterexample" id="showCounterexample" />
526 <label for="showCounterexample">
527 Show only relevant lines
529 <input type="checkbox" name="showArrows"
530 id="showArrows" style="margin-left: 10px" />
531 <label for="showArrows">
532 Show control flow arrows
550 if (llvm::sys::path::is_relative(Entry.
getName())) {
551 llvm::sys::fs::current_path(DirName);
555 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
556 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
561 generateKeyboardNavigationJavascript());
564 generateArrowDrawingJavascript());
568 showRelevantLinesJavascript(D, path));
573 llvm::raw_string_ostream os(
s);
575 os <<
"<!-- REPORTHEADER -->\n"
576 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
577 "<tr><td class=\"rowname\">File:</td><td>"
580 <<
"</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
581 "<a href=\"#EndPath\">line "
589 unsigned NumExtraPieces = 0;
590 for (
const auto &Piece : path) {
591 if (
const auto *
P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
593 P->getLocation().asLocation().getExpansionLineNumber();
595 P->getLocation().asLocation().getExpansionColumnNumber();
597 os <<
"<tr><td class=\"rowname\">Note:</td><td>"
598 <<
"<a href=\"#Note" << NumExtraPieces <<
"\">line "
599 << LineNumber <<
", column " << ColumnNumber <<
"</a><br />"
600 <<
P->getString() <<
"</td></tr>";
606 for (
const std::string &Metadata :
613<!-- REPORTSUMMARYEXTRA -->
614<h3>Annotated Source Code</h3>
615<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
616 to see keyboard shortcuts</p>
617<input type="checkbox" class="spoilerhider" id="showinvocation" />
618<label for="showinvocation" >Show analyzer invocation</label>
619<div class="spoiler">clang -cc1 )<<<";
623<div id='tooltiphint' hidden="true">
624 <p>Keyboard shortcuts: </p>
626 <li>Use 'j/k' keys for keyboard navigation</li>
627 <li>Use 'Shift+S' to show/hide relevant lines</li>
628 <li>Use '?' to toggle this window</li>
630 <a href="#" onclick="toggleHelp(); return false;">Close</a>
640 llvm::raw_string_ostream os(
s);
643 if (!BugDesc.empty())
644 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
648 os <<
"\n<!-- BUGTYPE " <<
BugType <<
" -->\n";
657 if (!BugCategory.empty())
658 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
660 os <<
"\n<!-- BUGFILE " << DirName << Entry.
getName() <<
" -->\n";
662 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry.
getName()) <<
" -->\n";
664 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
666 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " <<
getIssueHash(D, PP)
669 os <<
"\n<!-- BUGLINE "
673 os <<
"\n<!-- BUGCOLUMN "
677 os <<
"\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) <<
" -->\n";
680 os <<
"\n<!-- BUGMETAEND -->\n";
689StringRef HTMLDiagnostics::showHelpJavascript() {
691<script type='text/javascript'>
693var toggleHelp = function() {
694 var hint = document.querySelector("#tooltiphint");
695 var attributeName = "hidden";
696 if (hint.hasAttribute(attributeName)) {
697 hint.removeAttribute(attributeName);
699 hint.setAttribute("hidden", "true");
702window.addEventListener("keydown", function (event) {
703 if (event.defaultPrevented) {
706 if (event.key == "?") {
711 event.preventDefault();
718 return !(
Range.getBegin().isMacroID() ||
Range.getEnd().isMacroID());
723 const std::vector<SourceRange> &PopUpRanges) {
724 for (
const auto &
Range : PopUpRanges) {
729 "<table class='variable_popup'><tbody>",
736 std::vector<SourceRange> &PopUpRanges,
737 unsigned int LastReportedPieceIndex,
738 unsigned int PopUpPieceIndex) {
740 llvm::raw_svector_ostream Out(Buf);
747 Out <<
"<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
748 << LastReportedPieceIndex;
751 Out <<
'.' << PopUpPieceIndex;
753 Out <<
"</div></td><td>" << Piece.
getString() <<
"</td></tr>";
756 if (!llvm::is_contained(PopUpRanges,
Range)) {
758 PopUpRanges.push_back(
Range);
760 Out <<
"</tbody></table></span>";
762 "<span class='variable'>", Buf.c_str(),
776 unsigned TotalPieces = getPathSizeWithoutArrows(path);
777 unsigned TotalNotePieces =
779 return isa<PathDiagnosticNotePiece>(*p);
781 unsigned PopUpPieceCount =
783 return isa<PathDiagnosticPopUpPiece>(*p);
786 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
787 unsigned NumRegularPieces = TotalRegularPieces;
788 unsigned NumNotePieces = TotalNotePieces;
789 unsigned NumberOfArrows = 0;
791 std::map<int, int> IndexMap;
792 ArrowMap ArrowIndices(TotalRegularPieces + 1);
795 std::vector<SourceRange> PopUpRanges;
797 const auto &Piece = *I.get();
799 if (isa<PathDiagnosticPopUpPiece>(Piece)) {
800 ++IndexMap[NumRegularPieces];
801 }
else if (isa<PathDiagnosticNotePiece>(Piece)) {
805 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
808 }
else if (isArrowPiece(Piece)) {
809 NumberOfArrows = ProcessControlFlowPiece(
810 R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
811 ArrowIndices[NumRegularPieces] = NumberOfArrows;
814 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
817 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
820 ArrowIndices[0] = NumberOfArrows;
830 assert(ArrowIndices.back() == 0 &&
831 "No arrows should be after the last event");
833 assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
834 "Incorrect arrow indices map");
838 NumRegularPieces = TotalRegularPieces;
840 const auto &Piece = *I.get();
842 if (
const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
843 int PopUpPieceIndex = IndexMap[NumRegularPieces];
853 if (PopUpPieceIndex > 0)
854 --IndexMap[NumRegularPieces];
856 }
else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
868 addArrowSVGs(R, FID, ArrowIndices);
879 const std::vector<SourceRange> &PopUpRanges,
880 unsigned num,
unsigned max) {
889 assert(&Pos.
getManager() == &
SM &&
"SourceManagers are different!");
890 std::pair<FileID, unsigned> LPosInfo =
SM.getDecomposedExpansionLoc(Pos);
892 if (LPosInfo.first != BugFileID)
895 llvm::MemoryBufferRef Buf =
SM.getBufferOrFake(LPosInfo.first);
896 const char *FileStart = Buf.getBufferStart();
900 unsigned ColNo =
SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
902 const char *LineStart = TokInstantiationPtr-ColNo;
905 const char *LineEnd = TokInstantiationPtr;
906 const char *FileEnd = Buf.getBufferEnd();
907 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
912 for (
const char*
c = LineStart;
c != TokInstantiationPtr; ++
c)
913 PosNo += *
c ==
'\t' ? 8 : 1;
917 const char *
Kind =
nullptr;
919 bool SuppressIndex = (
max == 1);
920 switch (
P.getKind()) {
928 SuppressIndex =
true;
932 llvm_unreachable(
"Calls and extra notes should already be handled");
936 llvm::raw_string_ostream os(sbuf);
938 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
947 os <<
"\" class=\"msg";
949 os <<
" msg" <<
Kind;
950 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
953 if (!isa<PathDiagnosticMacroPiece>(
P)) {
955 const auto &Msg =
P.getString();
956 unsigned max_token = 0;
958 unsigned len = Msg.size();
968 if (cnt > max_token) max_token = cnt;
977 const unsigned max_line = 120;
979 if (max_token >= max_line)
982 unsigned characters = max_line;
983 unsigned lines = len / max_line;
986 for (; characters > max_token; --characters)
987 if (len / characters > lines) {
997 os <<
"; max-width:" << em <<
"em";
1000 os <<
"; max-width:100em";
1004 if (!SuppressIndex) {
1005 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
1006 os <<
"<div class=\"PathIndex";
1007 if (Kind) os <<
" PathIndex" <<
Kind;
1008 os <<
"\">" << num <<
"</div>";
1011 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
1013 <<
"\" title=\"Previous event ("
1015 <<
")\">←</a></div>";
1021 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&
P)) {
1022 os <<
"Within the expansion of the macro '";
1030 const char* MacroName = LocInfo.second + BufferInfo.data();
1032 BufferInfo.begin(), MacroName, BufferInfo.end());
1036 for (
unsigned i = 0, n = TheTok.
getLength(); i < n; ++i)
1042 if (!SuppressIndex) {
1045 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1049 os <<
"Path" << (num + 1);
1050 os <<
"\" title=\"Next event ("
1052 <<
")\">→</a></div></td>";
1055 os <<
"</tr></table>";
1059 ProcessMacroPiece(os, *MP, 0);
1064 if (!SuppressIndex) {
1067 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1071 os <<
"Path" << (num + 1);
1072 os <<
"\" title=\"Next event ("
1074 <<
")\">→</a></div></td>";
1077 os <<
"</tr></table>";
1081 os <<
"</div></td></tr>";
1084 unsigned DisplayPos = LineEnd - FileStart;
1086 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
1092 for (
const auto &
Range : Ranges) {
1094 if (llvm::is_contained(PopUpRanges,
Range))
1097 HighlightRange(R, LPosInfo.first,
Range);
1102 unsigned x = n % (
'z' -
'a');
1108 os << char(
'a' + x);
1111unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
1114 for (
const auto &subPiece :
P.subPieces) {
1115 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
1116 num = ProcessMacroPiece(os, *MP, num);
1120 if (
const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
1121 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
1122 "margin-left:5px\">"
1123 "<table class=\"msgT\"><tr>"
1124 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
1126 os <<
"</div></td><td valign=\"top\">"
1128 <<
"</td></tr></table></div>\n";
1136 const ArrowMap &ArrowIndices) {
1138 llvm::raw_string_ostream OS(S);
1141<style type="text/css">
1148 pointer-events: none;
1152 stroke-opacity: 0.2;
1154 marker-end: url(#arrowhead);
1158 stroke-opacity: 0.6;
1160 marker-end: url(#arrowheadSelected);
1170<svg xmlns="http://www.w3.org/2000/svg">
1172 <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
1173 viewBox="0 0 10 10" refX="3" refY="5"
1174 markerWidth="4" markerHeight="4">
1175 <path d="M 0 0 L 10 5 L 0 10 z" />
1177 <marker id="arrowhead" class="arrowhead" opacity="0.2"
1178 viewBox="0 0 10 10" refX="3" refY="5"
1179 markerWidth="4" markerHeight="4">
1180 <path d="M 0 0 L 10 5 L 0 10 z" />
1183 <g id="arrows" fill="none" stroke="blue" visibility="hidden">
1186 for (
unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
1187 OS <<
" <path class=\"arrow\" id=\"arrow" << Index <<
"\"/>\n";
1193<script type='text/javascript'>
1194const arrowIndices = )<<<";
1196 OS << ArrowIndices << "\n</script>\n";
1204 llvm::raw_string_ostream OS(Result);
1205 OS <<
"<span id=\"" << ClassName << Index <<
"\">";
1217unsigned HTMLDiagnostics::ProcessControlFlowPiece(
1224 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
1226 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
1233void HTMLDiagnostics::HighlightRange(
Rewriter& R,
FileID BugFileID,
1235 const char *HighlightStart,
1236 const char *HighlightEnd) {
1241 unsigned StartLineNo =
SM.getExpansionLineNumber(InstantiationStart);
1244 unsigned EndLineNo =
SM.getExpansionLineNumber(InstantiationEnd);
1246 if (EndLineNo < StartLineNo)
1249 if (
SM.getFileID(InstantiationStart) != BugFileID ||
1250 SM.getFileID(InstantiationEnd) != BugFileID)
1254 unsigned EndColNo =
SM.getExpansionColumnNumber(InstantiationEnd);
1255 unsigned OldEndColNo = EndColNo;
1271StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
1273<script type='text/javascript'>
1274var digitMatcher = new RegExp("[0-9]+");
1276var querySelectorAllArray = function(selector) {
1277 return Array.prototype.slice.call(
1278 document.querySelectorAll(selector));
1281document.addEventListener("DOMContentLoaded", function() {
1282 querySelectorAllArray(".PathNav > a").forEach(
1283 function(currentValue, currentIndex) {
1284 var hrefValue = currentValue.getAttribute("href");
1285 currentValue.onclick = function() {
1286 scrollTo(document.querySelector(hrefValue));
1292var findNum = function() {
1293 var s = document.querySelector(".msg.selected");
1294 if (!s || s.id == "EndPath") {
1297 var out = parseInt(digitMatcher.exec(s.id)[0]);
1301var classListAdd = function(el, theClass) {
1302 if(!el.className.baseVal)
1303 el.className += " " + theClass;
1305 el.className.baseVal += " " + theClass;
1308var classListRemove = function(el, theClass) {
1309 var className = (!el.className.baseVal) ?
1310 el.className : el.className.baseVal;
1311 className = className.replace(" " + theClass, "");
1312 if(!el.className.baseVal)
1313 el.className = className;
1315 el.className.baseVal = className;
1318var scrollTo = function(el) {
1319 querySelectorAllArray(".selected").forEach(function(s) {
1320 classListRemove(s, "selected");
1322 classListAdd(el, "selected");
1323 window.scrollBy(0, el.getBoundingClientRect().top -
1324 (window.innerHeight / 2));
1325 highlightArrowsForSelectedEvent();
1328var move = function(num, up, numItems) {
1329 if (num == 1 && up || num == numItems - 1 && !up) {
1331 } else if (num == 0 && up) {
1332 return numItems - 1;
1333 } else if (num == 0 && !up) {
1334 return 1 % numItems;
1336 return up ? num - 1 : num + 1;
1339var numToId = function(num) {
1341 return document.getElementById("EndPath")
1343 return document.getElementById("Path" + num);
1346var navigateTo = function(up) {
1347 var numItems = document.querySelectorAll(
1348 ".line > .msgEvent, .line > .msgControl").length;
1349 var currentSelected = findNum();
1350 var newSelected = move(currentSelected, up, numItems);
1351 var newEl = numToId(newSelected, numItems);
1353 // Scroll element into center.
1357window.addEventListener("keydown", function (event) {
1358 if (event.defaultPrevented) {
1362 if (event.keyCode == 74) {
1363 navigateTo(/*up=*/false);
1365 } else if (event.keyCode == 75) {
1366 navigateTo(/*up=*/true);
1370 event.preventDefault();
1376StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
1378<script type='text/javascript'>
1379// Return range of numbers from a range [lower, upper).
1380function range(lower, upper) {
1382 for (var i = lower; i <= upper; ++i) {
1388var getRelatedArrowIndices = function(pathId) {
1389 // HTML numeration of events is a bit different than it is in the path.
1390 // Everything is rotated one step to the right, so the last element
1391 // (error diagnostic) has index 0.
1393 // arrowIndices has at least 2 elements
1394 pathId = arrowIndices.length - 1;
1397 return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
1400var highlightArrowsForSelectedEvent = function() {
1401 const selectedNum = findNum();
1402 const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
1403 arrowIndicesToHighlight.forEach((index) => {
1404 var arrow = document.querySelector("#arrow" + index);
1406 classListAdd(arrow, "selected")
1411var getAbsoluteBoundingRect = function(element) {
1412 const relative = element.getBoundingClientRect();
1414 left: relative.left + window.pageXOffset,
1415 right: relative.right + window.pageXOffset,
1416 top: relative.top + window.pageYOffset,
1417 bottom: relative.bottom + window.pageYOffset,
1418 height: relative.height,
1419 width: relative.width
1423var drawArrow = function(index) {
1424 // This function is based on the great answer from SO:
1425 // https://stackoverflow.com/a/39575674/11582326
1426 var start = document.querySelector("#start" + index);
1427 var end = document.querySelector("#end" + index);
1428 var arrow = document.querySelector("#arrow" + index);
1430 var startRect = getAbsoluteBoundingRect(start);
1431 var endRect = getAbsoluteBoundingRect(end);
1433 // It is an arrow from a token to itself, no need to visualize it.
1434 if (startRect.top == endRect.top &&
1435 startRect.left == endRect.left)
1438 // Each arrow is a very simple Bézier curve, with two nodes and
1439 // two handles. So, we need to calculate four points in the window:
1441 var posStart = { x: 0, y: 0 };
1443 var posEnd = { x: 0, y: 0 };
1444 // * handle for the start node
1445 var startHandle = { x: 0, y: 0 };
1446 // * handle for the end node
1447 var endHandle = { x: 0, y: 0 };
1448 // One can visualize it as follows:
1464 // NOTE: (0, 0) is the top left corner of the window.
1466 // We have 3 similar, but still different scenarios to cover:
1468 // 1. Two tokens on different lines.
1473 // In this situation, we draw arrow on the left curving to the left.
1474 // 2. Two tokens on the same line, and the destination is on the right.
1479 // In this situation, we draw arrow above curving upwards.
1480 // 3. Two tokens on the same line, and the destination is on the left.
1484 // In this situation, we draw arrow below curving downwards.
1485 const onDifferentLines = startRect.top <= endRect.top - 5 ||
1486 startRect.top >= endRect.top + 5;
1487 const leftToRight = startRect.left < endRect.left;
1489 // NOTE: various magic constants are chosen empirically for
1490 // better positioning and look
1491 if (onDifferentLines) {
1493 const topToBottom = startRect.top < endRect.top;
1494 posStart.x = startRect.left - 1;
1495 // We don't want to start it at the top left corner of the token,
1496 // it doesn't feel like this is where the arrow comes from.
1497 // For this reason, we start it in the middle of the left side
1499 posStart.y = startRect.top + startRect.height / 2;
1501 // End node has arrow head and we give it a bit more space.
1502 posEnd.x = endRect.left - 4;
1503 posEnd.y = endRect.top;
1505 // Utility object with x and y offsets for handles.
1507 // We want bottom-to-top arrow to curve a bit more, so it doesn't
1508 // overlap much with top-to-bottom curves (much more frequent).
1509 x: topToBottom ? 15 : 25,
1510 y: Math.min((posEnd.y - posStart.y) / 3, 10)
1513 // When destination is on the different line, we can make a
1514 // curvier arrow because we have space for it.
1515 // So, instead of using
1517 // startHandle.x = posStart.x - curvature.x
1518 // endHandle.x = posEnd.x - curvature.x
1520 // We use the leftmost of these two values for both handles.
1521 startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
1522 endHandle.x = startHandle.x;
1524 // Curving downwards from the start node...
1525 startHandle.y = posStart.y + curvature.y;
1526 // ... and upwards from the end node.
1527 endHandle.y = posEnd.y - curvature.y;
1529 } else if (leftToRight) {
1531 // Starting from the top right corner...
1532 posStart.x = startRect.right - 1;
1533 posStart.y = startRect.top;
1535 // ...and ending at the top left corner of the end token.
1536 posEnd.x = endRect.left + 1;
1537 posEnd.y = endRect.top - 1;
1539 // Utility object with x and y offsets for handles.
1541 x: Math.min((posEnd.x - posStart.x) / 3, 15),
1545 // Curving to the right...
1546 startHandle.x = posStart.x + curvature.x;
1547 // ... and upwards from the start node.
1548 startHandle.y = posStart.y - curvature.y;
1550 // And to the left...
1551 endHandle.x = posEnd.x - curvature.x;
1552 // ... and upwards from the end node.
1553 endHandle.y = posEnd.y - curvature.y;
1557 // Starting from the bottom right corner...
1558 posStart.x = startRect.right;
1559 posStart.y = startRect.bottom;
1561 // ...and ending also at the bottom right corner, but of the end token.
1562 posEnd.x = endRect.right - 1;
1563 posEnd.y = endRect.bottom + 1;
1565 // Utility object with x and y offsets for handles.
1567 x: Math.min((posStart.x - posEnd.x) / 3, 15),
1571 // Curving to the left...
1572 startHandle.x = posStart.x - curvature.x;
1573 // ... and downwards from the start node.
1574 startHandle.y = posStart.y + curvature.y;
1576 // And to the right...
1577 endHandle.x = posEnd.x + curvature.x;
1578 // ... and downwards from the end node.
1579 endHandle.y = posEnd.y + curvature.y;
1582 // Put it all together into a path.
1583 // More information on the format:
1584 // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
1585 var pathStr = "M" + posStart.x + "," + posStart.y + " " +
1586 "C" + startHandle.x + "," + startHandle.y + " " +
1587 endHandle.x + "," + endHandle.y + " " +
1588 posEnd.x + "," + posEnd.y;
1590 arrow.setAttribute("d", pathStr);
1593var drawArrows = function() {
1594 const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
1595 for (var i = 0; i < numOfArrows; ++i) {
1600var toggleArrows = function(event) {
1601 const arrows = document.querySelector("#arrows");
1602 if (event.target.checked) {
1603 arrows.setAttribute("visibility", "visible");
1605 arrows.setAttribute("visibility", "hidden");
1609window.addEventListener("resize", drawArrows);
1610document.addEventListener("DOMContentLoaded", function() {
1611 // Whenever we show invocation, locations change, i.e. we
1612 // need to redraw arrows.
1614 .querySelector('input[id="showinvocation"]')
1615 .addEventListener("click", drawArrows);
1616 // Hiding irrelevant lines also should cause arrow rerender.
1618 .querySelector('input[name="showCounterexample"]')
1619 .addEventListener("change", drawArrows);
1621 .querySelector('input[name="showArrows"]')
1622 .addEventListener("change", toggleArrows);
1624 // Default highlighting for the last event.
1625 highlightArrowsForSelectedEvent();
Defines the clang::FileManager interface and associated types.
static bool shouldDisplayPopUpRange(const SourceRange &Range)
static void EmitAlphaCounter(raw_ostream &os, unsigned n)
std::string getSpanBeginForControlStart(unsigned Index)
static llvm::SmallString< 32 > getIssueHash(const PathDiagnostic &D, const Preprocessor &PP)
static void HandlePopUpPieceStartTag(Rewriter &R, const std::vector< SourceRange > &PopUpRanges)
std::string getSpanBeginForControlEnd(unsigned Index)
std::string getSpanBeginForControl(const char *ClassName, unsigned Index)
static void HandlePopUpPieceEndTag(Rewriter &R, const PathDiagnosticPopUpPiece &Piece, std::vector< SourceRange > &PopUpRanges, unsigned int LastReportedPieceIndex, unsigned int PopUpPieceIndex)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Defines the clang::Preprocessor interface.
static std::string getName(const CallEvent &Call)
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
__DEVICE__ int max(int __a, int __b)
__device__ __2f16 float __ockl_bool s
__device__ __2f16 float c
Decl - This represents one declaration (or definition), e.g.
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
StringRef getName() const
The name of this FileEntry.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
A SourceLocation and its associated SourceManager.
FullSourceLoc getExpansionLoc() const
const char * getCharacterData(bool *Invalid=nullptr) const
StringRef getBufferData(bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
std::pair< FileID, unsigned > getDecomposedLoc() const
Decompose the specified location into a raw FileID + Offset pair.
const SourceManager & getManager() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
SourceManager & getSourceManager() const
const LangOptions & getLangOpts() const
RewriteBuffer - As code is rewritten, SourceBuffer's from the original input with modifications get a...
Rewriter - This is the main interface to the rewrite buffers.
bool InsertTextBefore(SourceLocation Loc, StringRef Str)
InsertText - Insert the specified string at the specified location in the original buffer.
const RewriteBuffer * getRewriteBufferFor(FileID FID) const
getRewriteBufferFor - Return the rewrite buffer for the specified FileID.
SourceManager & getSourceMgr() const
const LangOptions & getLangOpts() const
bool InsertTextAfter(SourceLocation Loc, StringRef Str)
InsertTextAfter - Insert the specified string at the specified location in the original buffer.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
SourceLocation getLocForEndOfFile(FileID FID) const
Return the source location corresponding to the last byte of the specified file.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID.
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
Token - This structure provides full information about a lexed token.
unsigned getLength() const
This class is used for tools that requires cross translation unit capability.
@ Everything
Used for HTML, shows both "arrows" and control notes.
virtual void FlushDiagnosticsImpl(std::vector< const PathDiagnostic * > &Diags, FilesMade *filesMade)=0
virtual bool supportsCrossFileDiagnostics() const
Return true if the PathDiagnosticConsumer supports individual PathDiagnostics that span multiple file...
virtual StringRef getName() const =0
virtual PathGenerationScheme getGenerationScheme() const
void FlushDiagnostics(FilesMade *FilesMade)
PathDiagnosticRange asRange() const
FullSourceLoc asLocation() const
StringRef getString() const
PathDiagnosticLocation getLocation() const override
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
StringRef getCheckerName() const
meta_iterator meta_end() const
PathDiagnosticLocation getUniqueingLoc() const
Get the location on which the report should be uniqued.
StringRef getVerboseDescription() const
const Decl * getDeclWithIssue() const
Return the semantic context where an issue occurred.
const FilesToLineNumsMap & getExecutedLines() const
StringRef getBugType() const
StringRef getCategory() const
meta_iterator meta_begin() const
PathDiagnosticLocation getLocation() const
PathPieces flatten(bool ShouldFlattenMacros) const
A Range represents the closed range [from, to].
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
void AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, StringRef title)
void HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, const char *StartTag, const char *EndTag, bool IsTokenRange=true)
HighlightRange - Highlight a range in the source code with the specified start/end tags.
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP)
SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords,...
void AddLineNumbers(Rewriter &R, FileID FID)
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP)
HighlightMacros - This uses the macro table state from the end of the file, to reexpand macros and in...
void EscapeText(Rewriter &R, FileID FID, bool EscapeSpaces=false, bool ReplaceTabs=false)
EscapeText - HTMLize a specified file so that special characters are are translated so that they are ...
const StreamingDiagnostic & operator<<(const StreamingDiagnostic &DB, const ASTContext::SectionInfo &Section)
Insertion operator for diagnostics.
These options tweak the behavior of path diangostic consumers.
bool ShouldWriteVerboseReportFilename
If the consumer intends to produce multiple output files, should it use a pseudo-random file name or ...
std::string ToolInvocation
Run-line of the tool that produced the diagnostic.