29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/RewriteBuffer.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/Sequence.h"
33#include "llvm/ADT/SmallString.h"
34#include "llvm/ADT/StringRef.h"
35#include "llvm/ADT/iterator_range.h"
36#include "llvm/Support/Casting.h"
37#include "llvm/Support/Errc.h"
38#include "llvm/Support/ErrorHandling.h"
39#include "llvm/Support/FileSystem.h"
40#include "llvm/Support/MemoryBuffer.h"
41#include "llvm/Support/Path.h"
42#include "llvm/Support/raw_ostream.h"
50#include <system_error>
56using llvm::RewriteBuffer;
68 std::string Directory;
69 bool createdDir =
false;
72 const bool SupportsCrossFileDiagnostics;
73 llvm::StringSet<> EmittedHashes;
80 bool supportsMultipleFiles)
81 : DiagOpts(
std::move(DiagOpts)), Directory(OutputDir), PP(pp),
82 SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
87 FilesMade *filesMade)
override;
89 StringRef
getName()
const override {
return "HTMLDiagnostics"; }
92 return SupportsCrossFileDiagnostics;
103 const std::vector<SourceRange> &PopUpRanges,
unsigned num,
107 const char *HighlightStart =
"<span class=\"mrange\">",
108 const char *HighlightEnd =
"</span>");
115 const char *declName);
131 const ArrowMap &ArrowIndices);
134 StringRef showHelpJavascript();
137 StringRef generateKeyboardNavigationJavascript();
140 StringRef generateArrowDrawingJavascript();
148 llvm::raw_string_ostream &os);
152 return isa<PathDiagnosticControlFlowPiece>(
P) &&
P.getString().empty();
156 unsigned TotalPieces =
Path.size();
157 unsigned TotalArrowPieces = llvm::count_if(
159 return TotalPieces - TotalArrowPieces;
162class ArrowMap :
public std::vector<unsigned> {
163 using Base = std::vector<unsigned>;
166 ArrowMap(
unsigned Size) :
Base(
Size, 0) {}
167 unsigned getTotalNumberOfArrows()
const {
return at(0); }
170llvm::raw_ostream &
operator<<(llvm::raw_ostream &OS,
const ArrowMap &Indices) {
172 llvm::interleave(Indices, OS,
",");
178void ento::createHTMLDiagnosticConsumer(
189 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
193 if (OutputDir.empty())
196 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
true));
199void ento::createHTMLSingleFileDiagnosticConsumer(
204 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
208 if (OutputDir.empty())
211 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
false));
214void ento::createPlistHTMLDiagnosticConsumer(
219 createHTMLDiagnosticConsumer(
220 DiagOpts,
C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,
222 createPlistMultiFileDiagnosticConsumer(DiagOpts,
C, prefix, PP, CTU,
224 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, prefix, PP,
225 CTU, MacroExpansions);
228void ento::createSarifHTMLDiagnosticConsumer(
233 createHTMLDiagnosticConsumer(
234 DiagOpts,
C, std::string(llvm::sys::path::parent_path(sarif_file)), PP,
235 CTU, MacroExpansions);
236 createSarifDiagnosticConsumer(DiagOpts,
C, sarif_file, PP, CTU,
238 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, sarif_file,
239 PP, CTU, MacroExpansions);
246void HTMLDiagnostics::FlushDiagnosticsImpl(
247 std::vector<const PathDiagnostic *> &Diags,
248 FilesMade *filesMade) {
249 for (
const auto Diag : Diags)
250 ReportDiag(*
Diag, filesMade);
266 FilesMade *filesMade) {
270 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
271 llvm::errs() <<
"warning: could not create directory '"
272 << Directory <<
"': " << ec.message() <<
'\n';
285 assert(!path.empty());
286 const SourceManager &SMgr = path.front()->getLocation().getManager();
294 if (
const Decl *DeclWithIssue =
D.getDeclWithIssue()) {
295 if (
const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
296 declName = ND->getDeclName().getAsString();
298 if (
const Stmt *Body = DeclWithIssue->getBody()) {
305 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
310 auto [It, IsNew] = EmittedHashes.insert(IssueHash);
316 std::string report = GenerateHTML(
D, R, SMgr, path, declName.c_str());
317 if (report.empty()) {
318 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
326 llvm::raw_svector_ostream
FileName(FileNameStr);
339 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
344 << declName.c_str() <<
"-" << offsetDecl <<
"-";
347 FileName << StringRef(IssueHash).substr(0, 6).str() <<
".html";
350 llvm::sys::path::append(ResultPath, Directory,
FileName.str());
351 if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
352 llvm::errs() <<
"warning: could not make '" << ResultPath
353 <<
"' absolute: " << EC.message() <<
'\n';
357 if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
358 ResultPath, FD, llvm::sys::fs::CD_CreateNew,
359 llvm::sys::fs::OF_Text)) {
365 if (EC != llvm::errc::file_exists) {
366 llvm::errs() <<
"warning: could not create file in '" << Directory
367 <<
"': " << EC.message() <<
'\n';
372 llvm::raw_fd_ostream os(FD,
true);
375 filesMade->addDiagnostic(
D,
getName(),
376 llvm::sys::path::filename(ResultPath));
385 std::vector<FileID> FileIDs;
386 for (
auto I : path) {
387 FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
388 if (llvm::is_contained(FileIDs, FID))
391 FileIDs.push_back(FID);
392 RewriteFile(R, path, FID);
395 if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
397 for (
auto I = FileIDs.begin(),
E = FileIDs.end(); I !=
E; I++) {
399 llvm::raw_string_ostream os(
s);
401 if (I != FileIDs.begin())
402 os <<
"<hr class=divider>\n";
404 os <<
"<div id=File" << I->getHashValue() <<
">\n";
407 if (I != FileIDs.begin())
408 os <<
"<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
409 <<
"\">←</a></div>";
416 os <<
"<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
417 <<
"\">→</a></div>";
425 for (
auto I : llvm::drop_begin(FileIDs)) {
427 llvm::raw_string_ostream os(
s);
443 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
445 FinalizeHTML(
D, R, SMgr, path, FileIDs[0], *Entry, declName);
448 llvm::raw_string_ostream os(file);
455void HTMLDiagnostics::dumpCoverageData(
458 llvm::raw_string_ostream &os) {
462 os <<
"var relevant_lines = {";
463 for (
auto I = ExecutedLines.begin(),
464 E = ExecutedLines.end(); I !=
E; ++I) {
465 if (I != ExecutedLines.begin())
468 os <<
"\"" << I->first.getHashValue() <<
"\": {";
469 for (
unsigned LineNo : I->second) {
470 if (LineNo != *(I->second.begin()))
473 os <<
"\"" << LineNo <<
"\": 1";
481std::string HTMLDiagnostics::showRelevantLinesJavascript(
484 llvm::raw_string_ostream os(
s);
485 os <<
"<script type='text/javascript'>\n";
486 dumpCoverageData(
D, path, os);
489var filterCounterexample = function (hide) {
490 var tables = document.getElementsByClassName("code");
491 for (var t=0; t<tables.length; t++) {
492 var table = tables[t];
493 var file_id = table.getAttribute("data-fileid");
494 var lines_in_fid = relevant_lines[file_id];
498 var lines = table.getElementsByClassName("codeline");
499 for (var i=0; i<lines.length; i++) {
501 var lineNo = el.getAttribute("data-linenumber");
502 if (!lines_in_fid[lineNo]) {
504 el.setAttribute("hidden", "");
506 el.removeAttribute("hidden");
513window.addEventListener("keydown", function (event) {
514 if (event.defaultPrevented) {
518 if (event.shiftKey && event.keyCode == 83) {
519 var checked = document.getElementsByName("showCounterexample")[0].checked;
520 filterCounterexample(!checked);
521 document.getElementsByName("showCounterexample")[0].click();
525 event.preventDefault();
528document.addEventListener("DOMContentLoaded", function() {
529 document.querySelector('input[name="showCounterexample"]').onchange=
531 filterCounterexample(this.checked);
537 <input type="checkbox" name="showCounterexample" id="showCounterexample" />
538 <label for="showCounterexample">
539 Show only relevant lines
541 <input type="checkbox" name="showArrows"
542 id="showArrows" style="margin-left: 10px" />
543 <label for="showArrows">
544 Show control flow arrows
562 if (llvm::sys::path::is_relative(Entry.
getName())) {
563 llvm::sys::fs::current_path(DirName);
567 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
568 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
573 generateKeyboardNavigationJavascript());
576 generateArrowDrawingJavascript());
580 showRelevantLinesJavascript(
D, path));
585 llvm::raw_string_ostream os(
s);
587 os <<
"<!-- REPORTHEADER -->\n"
588 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
589 "<tr><td class=\"rowname\">File:</td><td>"
592 <<
"</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
593 "<a href=\"#EndPath\">line "
598 <<
D.getVerboseDescription() <<
"</td></tr>\n";
601 unsigned NumExtraPieces = 0;
602 for (
const auto &Piece : path) {
603 if (
const auto *
P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
605 P->getLocation().asLocation().getExpansionLineNumber();
607 P->getLocation().asLocation().getExpansionColumnNumber();
609 os <<
"<tr><td class=\"rowname\">Note:</td><td>"
610 <<
"<a href=\"#Note" << NumExtraPieces <<
"\">line "
611 << LineNumber <<
", column " << ColumnNumber <<
"</a><br />"
612 <<
P->getString() <<
"</td></tr>";
618 for (
const std::string &Metadata :
619 llvm::make_range(
D.meta_begin(),
D.meta_end())) {
625<!-- REPORTSUMMARYEXTRA -->
626<h3>Annotated Source Code</h3>
627<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
628 to see keyboard shortcuts</p>
629<input type="checkbox" class="spoilerhider" id="showinvocation" />
630<label for="showinvocation" >Show analyzer invocation</label>
631<div class="spoiler">clang -cc1 )<<<";
635<div id='tooltiphint' hidden="true">
636 <p>Keyboard shortcuts: </p>
638 <li>Use 'j/k' keys for keyboard navigation</li>
639 <li>Use 'Shift+S' to show/hide relevant lines</li>
640 <li>Use '?' to toggle this window</li>
642 <a href="#" onclick="toggleHelp(); return false;">Close</a>
652 llvm::raw_string_ostream os(
s);
654 StringRef BugDesc =
D.getVerboseDescription();
655 if (!BugDesc.empty())
656 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
660 os <<
"\n<!-- BUGTYPE " <<
BugType <<
" -->\n";
668 StringRef BugCategory =
D.getCategory();
669 if (!BugCategory.empty())
670 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
672 os <<
"\n<!-- BUGFILE " << DirName << Entry.
getName() <<
" -->\n";
674 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry.
getName()) <<
" -->\n";
676 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
678 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " <<
getIssueHash(
D, PP)
681 os <<
"\n<!-- BUGLINE "
685 os <<
"\n<!-- BUGCOLUMN "
689 os <<
"\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) <<
" -->\n";
692 os <<
"\n<!-- BUGMETAEND -->\n";
701StringRef HTMLDiagnostics::showHelpJavascript() {
703<script type='text/javascript'>
705var toggleHelp = function() {
706 var hint = document.querySelector("#tooltiphint");
707 var attributeName = "hidden";
708 if (hint.hasAttribute(attributeName)) {
709 hint.removeAttribute(attributeName);
711 hint.setAttribute("hidden", "true");
714window.addEventListener("keydown", function (event) {
715 if (event.defaultPrevented) {
718 if (event.key == "?") {
723 event.preventDefault();
730 return !(
Range.getBegin().isMacroID() ||
Range.getEnd().isMacroID());
735 const std::vector<SourceRange> &PopUpRanges) {
736 for (
const auto &
Range : PopUpRanges) {
741 "<table class='variable_popup'><tbody>",
748 std::vector<SourceRange> &PopUpRanges,
749 unsigned int LastReportedPieceIndex,
750 unsigned int PopUpPieceIndex) {
752 llvm::raw_svector_ostream Out(Buf);
759 Out <<
"<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
760 << LastReportedPieceIndex;
763 Out <<
'.' << PopUpPieceIndex;
765 Out <<
"</div></td><td>" << Piece.
getString() <<
"</td></tr>";
768 if (!llvm::is_contained(PopUpRanges,
Range)) {
770 PopUpRanges.push_back(
Range);
772 Out <<
"</tbody></table></span>";
774 "<span class='variable'>", Buf.c_str(),
788 unsigned TotalPieces = getPathSizeWithoutArrows(path);
789 unsigned TotalNotePieces =
791 return isa<PathDiagnosticNotePiece>(*p);
793 unsigned PopUpPieceCount =
795 return isa<PathDiagnosticPopUpPiece>(*p);
798 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
799 unsigned NumRegularPieces = TotalRegularPieces;
800 unsigned NumNotePieces = TotalNotePieces;
801 unsigned NumberOfArrows = 0;
803 std::map<int, int> IndexMap;
804 ArrowMap ArrowIndices(TotalRegularPieces + 1);
807 std::vector<SourceRange> PopUpRanges;
809 const auto &Piece = *I.get();
811 if (isa<PathDiagnosticPopUpPiece>(Piece)) {
812 ++IndexMap[NumRegularPieces];
813 }
else if (isa<PathDiagnosticNotePiece>(Piece)) {
817 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
820 }
else if (isArrowPiece(Piece)) {
821 NumberOfArrows = ProcessControlFlowPiece(
822 R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
823 ArrowIndices[NumRegularPieces] = NumberOfArrows;
826 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
829 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
832 ArrowIndices[0] = NumberOfArrows;
842 assert(ArrowIndices.back() == 0 &&
843 "No arrows should be after the last event");
845 assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
846 "Incorrect arrow indices map");
850 NumRegularPieces = TotalRegularPieces;
852 const auto &Piece = *I.get();
854 if (
const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
855 int PopUpPieceIndex = IndexMap[NumRegularPieces];
865 if (PopUpPieceIndex > 0)
866 --IndexMap[NumRegularPieces];
868 }
else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
880 addArrowSVGs(R, FID, ArrowIndices);
891 const std::vector<SourceRange> &PopUpRanges,
892 unsigned num,
unsigned max) {
901 assert(&Pos.
getManager() == &
SM &&
"SourceManagers are different!");
902 std::pair<FileID, unsigned> LPosInfo =
SM.getDecomposedExpansionLoc(Pos);
904 if (LPosInfo.first != BugFileID)
907 llvm::MemoryBufferRef Buf =
SM.getBufferOrFake(LPosInfo.first);
908 const char *FileStart = Buf.getBufferStart();
912 unsigned ColNo =
SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
914 const char *LineStart = TokInstantiationPtr-ColNo;
917 const char *LineEnd = TokInstantiationPtr;
918 const char *FileEnd = Buf.getBufferEnd();
919 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
924 for (
const char*
c = LineStart;
c != TokInstantiationPtr; ++
c)
925 PosNo += *
c ==
'\t' ? 8 : 1;
929 const char *
Kind =
nullptr;
931 bool SuppressIndex = (
max == 1);
932 switch (
P.getKind()) {
940 SuppressIndex =
true;
944 llvm_unreachable(
"Calls and extra notes should already be handled");
948 llvm::raw_string_ostream os(sbuf);
950 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
959 os <<
"\" class=\"msg";
961 os <<
" msg" <<
Kind;
962 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
965 if (!isa<PathDiagnosticMacroPiece>(
P)) {
967 const auto &Msg =
P.getString();
968 unsigned max_token = 0;
970 unsigned len = Msg.size();
980 if (cnt > max_token) max_token = cnt;
989 const unsigned max_line = 120;
991 if (max_token >= max_line)
994 unsigned characters = max_line;
995 unsigned lines = len / max_line;
998 for (; characters > max_token; --characters)
999 if (len / characters > lines) {
1005 em = characters / 2;
1008 if (em < max_line/2)
1009 os <<
"; max-width:" << em <<
"em";
1012 os <<
"; max-width:100em";
1016 if (!SuppressIndex) {
1017 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
1018 os <<
"<div class=\"PathIndex";
1019 if (Kind) os <<
" PathIndex" <<
Kind;
1020 os <<
"\">" << num <<
"</div>";
1023 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
1025 <<
"\" title=\"Previous event ("
1027 <<
")\">←</a></div>";
1033 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&
P)) {
1034 os <<
"Within the expansion of the macro '";
1042 const char* MacroName = LocInfo.second + BufferInfo.data();
1044 BufferInfo.begin(), MacroName, BufferInfo.end());
1048 for (
unsigned i = 0, n = TheTok.
getLength(); i < n; ++i)
1054 if (!SuppressIndex) {
1057 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1061 os <<
"Path" << (num + 1);
1062 os <<
"\" title=\"Next event ("
1064 <<
")\">→</a></div></td>";
1067 os <<
"</tr></table>";
1071 ProcessMacroPiece(os, *MP, 0);
1076 if (!SuppressIndex) {
1079 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1083 os <<
"Path" << (num + 1);
1084 os <<
"\" title=\"Next event ("
1086 <<
")\">→</a></div></td>";
1089 os <<
"</tr></table>";
1093 os <<
"</div></td></tr>";
1096 unsigned DisplayPos = LineEnd - FileStart;
1098 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
1104 for (
const auto &
Range : Ranges) {
1106 if (llvm::is_contained(PopUpRanges,
Range))
1109 HighlightRange(R, LPosInfo.first,
Range);
1114 unsigned x = n % (
'z' -
'a');
1120 os << char(
'a' + x);
1123unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
1126 for (
const auto &subPiece :
P.subPieces) {
1127 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
1128 num = ProcessMacroPiece(os, *MP, num);
1132 if (
const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
1133 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
1134 "margin-left:5px\">"
1135 "<table class=\"msgT\"><tr>"
1136 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
1138 os <<
"</div></td><td valign=\"top\">"
1140 <<
"</td></tr></table></div>\n";
1148 const ArrowMap &ArrowIndices) {
1150 llvm::raw_string_ostream OS(S);
1153<style type="text/css">
1160 pointer-events: none;
1164 stroke-opacity: 0.2;
1166 marker-end: url(#arrowhead);
1170 stroke-opacity: 0.6;
1172 marker-end: url(#arrowheadSelected);
1182<svg xmlns="http://www.w3.org/2000/svg">
1184 <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
1185 viewBox="0 0 10 10" refX="3" refY="5"
1186 markerWidth="4" markerHeight="4">
1187 <path d="M 0 0 L 10 5 L 0 10 z" />
1189 <marker id="arrowhead" class="arrowhead" opacity="0.2"
1190 viewBox="0 0 10 10" refX="3" refY="5"
1191 markerWidth="4" markerHeight="4">
1192 <path d="M 0 0 L 10 5 L 0 10 z" />
1195 <g id="arrows" fill="none" stroke="blue" visibility="hidden">
1198 for (
unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
1199 OS <<
" <path class=\"arrow\" id=\"arrow" << Index <<
"\"/>\n";
1205<script type='text/javascript'>
1206const arrowIndices = )<<<";
1208 OS << ArrowIndices << "\n</script>\n";
1216 llvm::raw_string_ostream OS(Result);
1217 OS <<
"<span id=\"" << ClassName << Index <<
"\">";
1229unsigned HTMLDiagnostics::ProcessControlFlowPiece(
1236 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
1238 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
1245void HTMLDiagnostics::HighlightRange(
Rewriter& R,
FileID BugFileID,
1247 const char *HighlightStart,
1248 const char *HighlightEnd) {
1253 unsigned StartLineNo =
SM.getExpansionLineNumber(InstantiationStart);
1256 unsigned EndLineNo =
SM.getExpansionLineNumber(InstantiationEnd);
1258 if (EndLineNo < StartLineNo)
1261 if (
SM.getFileID(InstantiationStart) != BugFileID ||
1262 SM.getFileID(InstantiationEnd) != BugFileID)
1266 unsigned EndColNo =
SM.getExpansionColumnNumber(InstantiationEnd);
1267 unsigned OldEndColNo = EndColNo;
1283StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
1285<script type='text/javascript'>
1286var digitMatcher = new RegExp("[0-9]+");
1288var querySelectorAllArray = function(selector) {
1289 return Array.prototype.slice.call(
1290 document.querySelectorAll(selector));
1293document.addEventListener("DOMContentLoaded", function() {
1294 querySelectorAllArray(".PathNav > a").forEach(
1295 function(currentValue, currentIndex) {
1296 var hrefValue = currentValue.getAttribute("href");
1297 currentValue.onclick = function() {
1298 scrollTo(document.querySelector(hrefValue));
1304var findNum = function() {
1305 var s = document.querySelector(".msg.selected");
1306 if (!s || s.id == "EndPath") {
1309 var out = parseInt(digitMatcher.exec(s.id)[0]);
1313var classListAdd = function(el, theClass) {
1314 if(!el.className.baseVal)
1315 el.className += " " + theClass;
1317 el.className.baseVal += " " + theClass;
1320var classListRemove = function(el, theClass) {
1321 var className = (!el.className.baseVal) ?
1322 el.className : el.className.baseVal;
1323 className = className.replace(" " + theClass, "");
1324 if(!el.className.baseVal)
1325 el.className = className;
1327 el.className.baseVal = className;
1330var scrollTo = function(el) {
1331 querySelectorAllArray(".selected").forEach(function(s) {
1332 classListRemove(s, "selected");
1334 classListAdd(el, "selected");
1335 window.scrollBy(0, el.getBoundingClientRect().top -
1336 (window.innerHeight / 2));
1337 highlightArrowsForSelectedEvent();
1340var move = function(num, up, numItems) {
1341 if (num == 1 && up || num == numItems - 1 && !up) {
1343 } else if (num == 0 && up) {
1344 return numItems - 1;
1345 } else if (num == 0 && !up) {
1346 return 1 % numItems;
1348 return up ? num - 1 : num + 1;
1351var numToId = function(num) {
1353 return document.getElementById("EndPath")
1355 return document.getElementById("Path" + num);
1358var navigateTo = function(up) {
1359 var numItems = document.querySelectorAll(
1360 ".line > .msgEvent, .line > .msgControl").length;
1361 var currentSelected = findNum();
1362 var newSelected = move(currentSelected, up, numItems);
1363 var newEl = numToId(newSelected, numItems);
1365 // Scroll element into center.
1369window.addEventListener("keydown", function (event) {
1370 if (event.defaultPrevented) {
1374 if (event.keyCode == 74) {
1375 navigateTo(/*up=*/false);
1377 } else if (event.keyCode == 75) {
1378 navigateTo(/*up=*/true);
1382 event.preventDefault();
1388StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
1390<script type='text/javascript'>
1391// Return range of numbers from a range [lower, upper).
1392function range(lower, upper) {
1394 for (var i = lower; i <= upper; ++i) {
1400var getRelatedArrowIndices = function(pathId) {
1401 // HTML numeration of events is a bit different than it is in the path.
1402 // Everything is rotated one step to the right, so the last element
1403 // (error diagnostic) has index 0.
1405 // arrowIndices has at least 2 elements
1406 pathId = arrowIndices.length - 1;
1409 return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
1412var highlightArrowsForSelectedEvent = function() {
1413 const selectedNum = findNum();
1414 const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
1415 arrowIndicesToHighlight.forEach((index) => {
1416 var arrow = document.querySelector("#arrow" + index);
1418 classListAdd(arrow, "selected")
1423var getAbsoluteBoundingRect = function(element) {
1424 const relative = element.getBoundingClientRect();
1426 left: relative.left + window.pageXOffset,
1427 right: relative.right + window.pageXOffset,
1428 top: relative.top + window.pageYOffset,
1429 bottom: relative.bottom + window.pageYOffset,
1430 height: relative.height,
1431 width: relative.width
1435var drawArrow = function(index) {
1436 // This function is based on the great answer from SO:
1437 // https://stackoverflow.com/a/39575674/11582326
1438 var start = document.querySelector("#start" + index);
1439 var end = document.querySelector("#end" + index);
1440 var arrow = document.querySelector("#arrow" + index);
1442 var startRect = getAbsoluteBoundingRect(start);
1443 var endRect = getAbsoluteBoundingRect(end);
1445 // It is an arrow from a token to itself, no need to visualize it.
1446 if (startRect.top == endRect.top &&
1447 startRect.left == endRect.left)
1450 // Each arrow is a very simple Bézier curve, with two nodes and
1451 // two handles. So, we need to calculate four points in the window:
1453 var posStart = { x: 0, y: 0 };
1455 var posEnd = { x: 0, y: 0 };
1456 // * handle for the start node
1457 var startHandle = { x: 0, y: 0 };
1458 // * handle for the end node
1459 var endHandle = { x: 0, y: 0 };
1460 // One can visualize it as follows:
1476 // NOTE: (0, 0) is the top left corner of the window.
1478 // We have 3 similar, but still different scenarios to cover:
1480 // 1. Two tokens on different lines.
1485 // In this situation, we draw arrow on the left curving to the left.
1486 // 2. Two tokens on the same line, and the destination is on the right.
1491 // In this situation, we draw arrow above curving upwards.
1492 // 3. Two tokens on the same line, and the destination is on the left.
1496 // In this situation, we draw arrow below curving downwards.
1497 const onDifferentLines = startRect.top <= endRect.top - 5 ||
1498 startRect.top >= endRect.top + 5;
1499 const leftToRight = startRect.left < endRect.left;
1501 // NOTE: various magic constants are chosen empirically for
1502 // better positioning and look
1503 if (onDifferentLines) {
1505 const topToBottom = startRect.top < endRect.top;
1506 posStart.x = startRect.left - 1;
1507 // We don't want to start it at the top left corner of the token,
1508 // it doesn't feel like this is where the arrow comes from.
1509 // For this reason, we start it in the middle of the left side
1511 posStart.y = startRect.top + startRect.height / 2;
1513 // End node has arrow head and we give it a bit more space.
1514 posEnd.x = endRect.left - 4;
1515 posEnd.y = endRect.top;
1517 // Utility object with x and y offsets for handles.
1519 // We want bottom-to-top arrow to curve a bit more, so it doesn't
1520 // overlap much with top-to-bottom curves (much more frequent).
1521 x: topToBottom ? 15 : 25,
1522 y: Math.min((posEnd.y - posStart.y) / 3, 10)
1525 // When destination is on the different line, we can make a
1526 // curvier arrow because we have space for it.
1527 // So, instead of using
1529 // startHandle.x = posStart.x - curvature.x
1530 // endHandle.x = posEnd.x - curvature.x
1532 // We use the leftmost of these two values for both handles.
1533 startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
1534 endHandle.x = startHandle.x;
1536 // Curving downwards from the start node...
1537 startHandle.y = posStart.y + curvature.y;
1538 // ... and upwards from the end node.
1539 endHandle.y = posEnd.y - curvature.y;
1541 } else if (leftToRight) {
1543 // Starting from the top right corner...
1544 posStart.x = startRect.right - 1;
1545 posStart.y = startRect.top;
1547 // ...and ending at the top left corner of the end token.
1548 posEnd.x = endRect.left + 1;
1549 posEnd.y = endRect.top - 1;
1551 // Utility object with x and y offsets for handles.
1553 x: Math.min((posEnd.x - posStart.x) / 3, 15),
1557 // Curving to the right...
1558 startHandle.x = posStart.x + curvature.x;
1559 // ... and upwards from the start node.
1560 startHandle.y = posStart.y - curvature.y;
1562 // And to the left...
1563 endHandle.x = posEnd.x - curvature.x;
1564 // ... and upwards from the end node.
1565 endHandle.y = posEnd.y - curvature.y;
1569 // Starting from the bottom right corner...
1570 posStart.x = startRect.right;
1571 posStart.y = startRect.bottom;
1573 // ...and ending also at the bottom right corner, but of the end token.
1574 posEnd.x = endRect.right - 1;
1575 posEnd.y = endRect.bottom + 1;
1577 // Utility object with x and y offsets for handles.
1579 x: Math.min((posStart.x - posEnd.x) / 3, 15),
1583 // Curving to the left...
1584 startHandle.x = posStart.x - curvature.x;
1585 // ... and downwards from the start node.
1586 startHandle.y = posStart.y + curvature.y;
1588 // And to the right...
1589 endHandle.x = posEnd.x + curvature.x;
1590 // ... and downwards from the end node.
1591 endHandle.y = posEnd.y + curvature.y;
1594 // Put it all together into a path.
1595 // More information on the format:
1596 // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
1597 var pathStr = "M" + posStart.x + "," + posStart.y + " " +
1598 "C" + startHandle.x + "," + startHandle.y + " " +
1599 endHandle.x + "," + endHandle.y + " " +
1600 posEnd.x + "," + posEnd.y;
1602 arrow.setAttribute("d", pathStr);
1605var drawArrows = function() {
1606 const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
1607 for (var i = 0; i < numOfArrows; ++i) {
1612var toggleArrows = function(event) {
1613 const arrows = document.querySelector("#arrows");
1614 if (event.target.checked) {
1615 arrows.setAttribute("visibility", "visible");
1617 arrows.setAttribute("visibility", "hidden");
1621window.addEventListener("resize", drawArrows);
1622document.addEventListener("DOMContentLoaded", function() {
1623 // Whenever we show invocation, locations change, i.e. we
1624 // need to redraw arrows.
1626 .querySelector('input[id="showinvocation"]')
1627 .addEventListener("click", drawArrows);
1628 // Hiding irrelevant lines also should cause arrow rerender.
1630 .querySelector('input[name="showCounterexample"]')
1631 .addEventListener("change", drawArrows);
1633 .querySelector('input[name="showArrows"]')
1634 .addEventListener("change", toggleArrows);
1636 // Default highlighting for the last event.
1637 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.
SourceLocation getLocation() const
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
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.
SourceManager & getSourceMgr() const
const LangOptions & getLangOpts() const
const llvm::RewriteBuffer * getRewriteBufferFor(FileID FID) const
getRewriteBufferFor - Return the rewrite buffer for the specified FileID.
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.
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.
RelexRewriteCacheRef instantiateRelexRewriteCache()
If you need to rewrite the same file multiple times, you can instantiate a RelexRewriteCache and refe...
void AddLineNumbers(Rewriter &R, FileID FID)
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)
SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords,...
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)
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 ...
std::shared_ptr< RelexRewriteCache > RelexRewriteCacheRef
The JSON file list parser is used to communicate input to InstallAPI.
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.