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;
71 llvm::StringSet<> EmittedHashes;
78 bool supportsMultipleFiles)
79 : DiagOpts(
std::move(DiagOpts)), Directory(OutputDir), PP(pp),
80 SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
85 FilesMade *filesMade)
override;
87 StringRef
getName()
const override {
return "HTMLDiagnostics"; }
90 return SupportsCrossFileDiagnostics;
101 const std::vector<SourceRange> &PopUpRanges,
unsigned num,
105 const char *HighlightStart =
"<span class=\"mrange\">",
106 const char *HighlightEnd =
"</span>");
113 const char *declName);
129 const ArrowMap &ArrowIndices);
132 StringRef showHelpJavascript();
135 StringRef generateKeyboardNavigationJavascript();
138 StringRef generateArrowDrawingJavascript();
146 llvm::raw_string_ostream &os);
150 return isa<PathDiagnosticControlFlowPiece>(
P) &&
P.getString().empty();
154 unsigned TotalPieces =
Path.size();
155 unsigned TotalArrowPieces = llvm::count_if(
157 return TotalPieces - TotalArrowPieces;
160class ArrowMap :
public std::vector<unsigned> {
161 using Base = std::vector<unsigned>;
164 ArrowMap(
unsigned Size) :
Base(
Size, 0) {}
165 unsigned getTotalNumberOfArrows()
const {
return at(0); }
168llvm::raw_ostream &
operator<<(llvm::raw_ostream &OS,
const ArrowMap &Indices) {
170 llvm::interleave(Indices, OS,
",");
176void ento::createHTMLDiagnosticConsumer(
187 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
191 if (OutputDir.empty())
194 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
true));
197void ento::createHTMLSingleFileDiagnosticConsumer(
202 createTextMinimalPathDiagnosticConsumer(DiagOpts,
C, OutputDir, PP, CTU,
206 if (OutputDir.empty())
209 C.push_back(
new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP,
false));
212void ento::createPlistHTMLDiagnosticConsumer(
217 createHTMLDiagnosticConsumer(
218 DiagOpts,
C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,
220 createPlistMultiFileDiagnosticConsumer(DiagOpts,
C, prefix, PP, CTU,
222 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, prefix, PP,
223 CTU, MacroExpansions);
226void ento::createSarifHTMLDiagnosticConsumer(
231 createHTMLDiagnosticConsumer(
232 DiagOpts,
C, std::string(llvm::sys::path::parent_path(sarif_file)), PP,
233 CTU, MacroExpansions);
234 createSarifDiagnosticConsumer(DiagOpts,
C, sarif_file, PP, CTU,
236 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, sarif_file,
237 PP, CTU, MacroExpansions);
244void HTMLDiagnostics::FlushDiagnosticsImpl(
245 std::vector<const PathDiagnostic *> &Diags,
246 FilesMade *filesMade) {
247 for (
const auto Diag : Diags)
248 ReportDiag(*
Diag, filesMade);
264 FilesMade *filesMade) {
268 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
269 llvm::errs() <<
"warning: could not create directory '"
270 << Directory <<
"': " << ec.message() <<
'\n';
283 assert(!path.empty());
284 const SourceManager &SMgr = path.front()->getLocation().getManager();
292 if (
const Decl *DeclWithIssue =
D.getDeclWithIssue()) {
293 if (
const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
294 declName = ND->getDeclName().getAsString();
296 if (
const Stmt *Body = DeclWithIssue->getBody()) {
303 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
308 auto [It, IsNew] = EmittedHashes.insert(IssueHash);
314 std::string report = GenerateHTML(
D, R, SMgr, path, declName.c_str());
315 if (report.empty()) {
316 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
324 llvm::raw_svector_ostream
FileName(FileNameStr);
337 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
342 << declName.c_str() <<
"-" << offsetDecl <<
"-";
345 FileName << StringRef(IssueHash).substr(0, 6).str() <<
".html";
348 llvm::sys::path::append(ResultPath, Directory,
FileName.str());
349 if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
350 llvm::errs() <<
"warning: could not make '" << ResultPath
351 <<
"' absolute: " << EC.message() <<
'\n';
355 if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
356 ResultPath, FD, llvm::sys::fs::CD_CreateNew,
357 llvm::sys::fs::OF_Text)) {
363 if (EC != llvm::errc::file_exists) {
364 llvm::errs() <<
"warning: could not create file in '" << Directory
365 <<
"': " << EC.message() <<
'\n';
370 llvm::raw_fd_ostream os(FD,
true);
373 filesMade->addDiagnostic(
D,
getName(),
374 llvm::sys::path::filename(ResultPath));
383 std::vector<FileID> FileIDs;
384 for (
auto I : path) {
385 FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
386 if (llvm::is_contained(FileIDs, FID))
389 FileIDs.push_back(FID);
390 RewriteFile(R, path, FID);
393 if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
395 for (
auto I = FileIDs.begin(),
E = FileIDs.end(); I !=
E; I++) {
397 llvm::raw_string_ostream os(
s);
399 if (I != FileIDs.begin())
400 os <<
"<hr class=divider>\n";
402 os <<
"<div id=File" << I->getHashValue() <<
">\n";
405 if (I != FileIDs.begin())
406 os <<
"<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
407 <<
"\">←</a></div>";
414 os <<
"<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
415 <<
"\">→</a></div>";
423 for (
auto I : llvm::drop_begin(FileIDs)) {
425 llvm::raw_string_ostream os(
s);
441 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
443 FinalizeHTML(
D, R, SMgr, path, FileIDs[0], *Entry, declName);
446 llvm::raw_string_ostream os(file);
453void HTMLDiagnostics::dumpCoverageData(
456 llvm::raw_string_ostream &os) {
460 os <<
"var relevant_lines = {";
461 for (
auto I = ExecutedLines.begin(),
462 E = ExecutedLines.end(); I !=
E; ++I) {
463 if (I != ExecutedLines.begin())
466 os <<
"\"" << I->first.getHashValue() <<
"\": {";
467 for (
unsigned LineNo : I->second) {
468 if (LineNo != *(I->second.begin()))
471 os <<
"\"" << LineNo <<
"\": 1";
479std::string HTMLDiagnostics::showRelevantLinesJavascript(
482 llvm::raw_string_ostream os(
s);
483 os <<
"<script type='text/javascript'>\n";
484 dumpCoverageData(
D, path, os);
487var filterCounterexample = function (hide) {
488 var tables = document.getElementsByClassName("code");
489 for (var t=0; t<tables.length; t++) {
490 var table = tables[t];
491 var file_id = table.getAttribute("data-fileid");
492 var lines_in_fid = relevant_lines[file_id];
496 var lines = table.getElementsByClassName("codeline");
497 for (var i=0; i<lines.length; i++) {
499 var lineNo = el.getAttribute("data-linenumber");
500 if (!lines_in_fid[lineNo]) {
502 el.setAttribute("hidden", "");
504 el.removeAttribute("hidden");
511window.addEventListener("keydown", function (event) {
512 if (event.defaultPrevented) {
516 if (event.shiftKey && event.keyCode == 83) {
517 var checked = document.getElementsByName("showCounterexample")[0].checked;
518 filterCounterexample(!checked);
519 document.getElementsByName("showCounterexample")[0].click();
523 event.preventDefault();
526document.addEventListener("DOMContentLoaded", function() {
527 document.querySelector('input[name="showCounterexample"]').onchange=
529 filterCounterexample(this.checked);
535 <input type="checkbox" name="showCounterexample" id="showCounterexample" />
536 <label for="showCounterexample">
537 Show only relevant lines
539 <input type="checkbox" name="showArrows"
540 id="showArrows" style="margin-left: 10px" />
541 <label for="showArrows">
542 Show control flow arrows
560 if (llvm::sys::path::is_relative(Entry.
getName())) {
561 llvm::sys::fs::current_path(DirName);
565 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
566 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
571 generateKeyboardNavigationJavascript());
574 generateArrowDrawingJavascript());
578 showRelevantLinesJavascript(
D, path));
583 llvm::raw_string_ostream os(
s);
585 os <<
"<!-- REPORTHEADER -->\n"
586 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
587 "<tr><td class=\"rowname\">File:</td><td>"
590 <<
"</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
591 "<a href=\"#EndPath\">line "
596 <<
D.getVerboseDescription() <<
"</td></tr>\n";
599 unsigned NumExtraPieces = 0;
600 for (
const auto &Piece : path) {
601 if (
const auto *
P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
603 P->getLocation().asLocation().getExpansionLineNumber();
605 P->getLocation().asLocation().getExpansionColumnNumber();
607 os <<
"<tr><td class=\"rowname\">Note:</td><td>"
608 <<
"<a href=\"#Note" << NumExtraPieces <<
"\">line "
609 << LineNumber <<
", column " << ColumnNumber <<
"</a><br />"
610 <<
P->getString() <<
"</td></tr>";
616 for (
const std::string &Metadata :
617 llvm::make_range(
D.meta_begin(),
D.meta_end())) {
623<!-- REPORTSUMMARYEXTRA -->
624<h3>Annotated Source Code</h3>
625<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
626 to see keyboard shortcuts</p>
627<input type="checkbox" class="spoilerhider" id="showinvocation" />
628<label for="showinvocation" >Show analyzer invocation</label>
629<div class="spoiler">clang -cc1 )<<<";
633<div id='tooltiphint' hidden="true">
634 <p>Keyboard shortcuts: </p>
636 <li>Use 'j/k' keys for keyboard navigation</li>
637 <li>Use 'Shift+S' to show/hide relevant lines</li>
638 <li>Use '?' to toggle this window</li>
640 <a href="#" onclick="toggleHelp(); return false;">Close</a>
650 llvm::raw_string_ostream os(
s);
652 StringRef BugDesc =
D.getVerboseDescription();
653 if (!BugDesc.empty())
654 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
658 os <<
"\n<!-- BUGTYPE " <<
BugType <<
" -->\n";
666 StringRef BugCategory =
D.getCategory();
667 if (!BugCategory.empty())
668 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
670 os <<
"\n<!-- BUGFILE " << DirName << Entry.
getName() <<
" -->\n";
672 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry.
getName()) <<
" -->\n";
674 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
676 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " <<
getIssueHash(
D, PP)
679 os <<
"\n<!-- BUGLINE "
683 os <<
"\n<!-- BUGCOLUMN "
687 os <<
"\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) <<
" -->\n";
690 os <<
"\n<!-- BUGMETAEND -->\n";
699StringRef HTMLDiagnostics::showHelpJavascript() {
701<script type='text/javascript'>
703var toggleHelp = function() {
704 var hint = document.querySelector("#tooltiphint");
705 var attributeName = "hidden";
706 if (hint.hasAttribute(attributeName)) {
707 hint.removeAttribute(attributeName);
709 hint.setAttribute("hidden", "true");
712window.addEventListener("keydown", function (event) {
713 if (event.defaultPrevented) {
716 if (event.key == "?") {
721 event.preventDefault();
728 return !(
Range.getBegin().isMacroID() ||
Range.getEnd().isMacroID());
733 const std::vector<SourceRange> &PopUpRanges) {
734 for (
const auto &
Range : PopUpRanges) {
739 "<table class='variable_popup'><tbody>",
746 std::vector<SourceRange> &PopUpRanges,
747 unsigned int LastReportedPieceIndex,
748 unsigned int PopUpPieceIndex) {
750 llvm::raw_svector_ostream Out(Buf);
757 Out <<
"<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
758 << LastReportedPieceIndex;
761 Out <<
'.' << PopUpPieceIndex;
763 Out <<
"</div></td><td>" << Piece.
getString() <<
"</td></tr>";
766 if (!llvm::is_contained(PopUpRanges,
Range)) {
768 PopUpRanges.push_back(
Range);
770 Out <<
"</tbody></table></span>";
772 "<span class='variable'>", Buf.c_str(),
786 unsigned TotalPieces = getPathSizeWithoutArrows(path);
787 unsigned TotalNotePieces =
789 return isa<PathDiagnosticNotePiece>(*p);
791 unsigned PopUpPieceCount =
793 return isa<PathDiagnosticPopUpPiece>(*p);
796 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
797 unsigned NumRegularPieces = TotalRegularPieces;
798 unsigned NumNotePieces = TotalNotePieces;
799 unsigned NumberOfArrows = 0;
801 std::map<int, int> IndexMap;
802 ArrowMap ArrowIndices(TotalRegularPieces + 1);
805 std::vector<SourceRange> PopUpRanges;
807 const auto &Piece = *I.get();
809 if (isa<PathDiagnosticPopUpPiece>(Piece)) {
810 ++IndexMap[NumRegularPieces];
811 }
else if (isa<PathDiagnosticNotePiece>(Piece)) {
815 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
818 }
else if (isArrowPiece(Piece)) {
819 NumberOfArrows = ProcessControlFlowPiece(
820 R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
821 ArrowIndices[NumRegularPieces] = NumberOfArrows;
824 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
827 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
830 ArrowIndices[0] = NumberOfArrows;
840 assert(ArrowIndices.back() == 0 &&
841 "No arrows should be after the last event");
843 assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
844 "Incorrect arrow indices map");
848 NumRegularPieces = TotalRegularPieces;
850 const auto &Piece = *I.get();
852 if (
const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
853 int PopUpPieceIndex = IndexMap[NumRegularPieces];
863 if (PopUpPieceIndex > 0)
864 --IndexMap[NumRegularPieces];
866 }
else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
878 addArrowSVGs(R, FID, ArrowIndices);
889 const std::vector<SourceRange> &PopUpRanges,
890 unsigned num,
unsigned max) {
899 assert(&Pos.
getManager() == &
SM &&
"SourceManagers are different!");
900 std::pair<FileID, unsigned> LPosInfo =
SM.getDecomposedExpansionLoc(Pos);
902 if (LPosInfo.first != BugFileID)
905 llvm::MemoryBufferRef Buf =
SM.getBufferOrFake(LPosInfo.first);
906 const char *FileStart = Buf.getBufferStart();
910 unsigned ColNo =
SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
912 const char *LineStart = TokInstantiationPtr-ColNo;
915 const char *LineEnd = TokInstantiationPtr;
916 const char *FileEnd = Buf.getBufferEnd();
917 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
922 for (
const char*
c = LineStart;
c != TokInstantiationPtr; ++
c)
923 PosNo += *
c ==
'\t' ? 8 : 1;
927 const char *
Kind =
nullptr;
929 bool SuppressIndex = (
max == 1);
930 switch (
P.getKind()) {
938 SuppressIndex =
true;
942 llvm_unreachable(
"Calls and extra notes should already be handled");
946 llvm::raw_string_ostream os(sbuf);
948 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
957 os <<
"\" class=\"msg";
959 os <<
" msg" <<
Kind;
960 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
963 if (!isa<PathDiagnosticMacroPiece>(
P)) {
965 const auto &Msg =
P.getString();
966 unsigned max_token = 0;
968 unsigned len = Msg.size();
978 if (cnt > max_token) max_token = cnt;
987 const unsigned max_line = 120;
989 if (max_token >= max_line)
992 unsigned characters = max_line;
993 unsigned lines = len / max_line;
996 for (; characters > max_token; --characters)
997 if (len / characters > lines) {
1003 em = characters / 2;
1006 if (em < max_line/2)
1007 os <<
"; max-width:" << em <<
"em";
1010 os <<
"; max-width:100em";
1014 if (!SuppressIndex) {
1015 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
1016 os <<
"<div class=\"PathIndex";
1017 if (Kind) os <<
" PathIndex" <<
Kind;
1018 os <<
"\">" << num <<
"</div>";
1021 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
1023 <<
"\" title=\"Previous event ("
1025 <<
")\">←</a></div>";
1031 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&
P)) {
1032 os <<
"Within the expansion of the macro '";
1040 const char* MacroName = LocInfo.second + BufferInfo.data();
1042 BufferInfo.begin(), MacroName, BufferInfo.end());
1046 for (
unsigned i = 0, n = TheTok.
getLength(); i < n; ++i)
1052 if (!SuppressIndex) {
1055 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1059 os <<
"Path" << (num + 1);
1060 os <<
"\" title=\"Next event ("
1062 <<
")\">→</a></div></td>";
1065 os <<
"</tr></table>";
1069 ProcessMacroPiece(os, *MP, 0);
1074 if (!SuppressIndex) {
1077 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1081 os <<
"Path" << (num + 1);
1082 os <<
"\" title=\"Next event ("
1084 <<
")\">→</a></div></td>";
1087 os <<
"</tr></table>";
1091 os <<
"</div></td></tr>";
1094 unsigned DisplayPos = LineEnd - FileStart;
1096 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
1102 for (
const auto &
Range : Ranges) {
1104 if (llvm::is_contained(PopUpRanges,
Range))
1107 HighlightRange(R, LPosInfo.first,
Range);
1112 unsigned x = n % (
'z' -
'a');
1118 os << char(
'a' + x);
1121unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
1124 for (
const auto &subPiece :
P.subPieces) {
1125 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
1126 num = ProcessMacroPiece(os, *MP, num);
1130 if (
const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
1131 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
1132 "margin-left:5px\">"
1133 "<table class=\"msgT\"><tr>"
1134 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
1136 os <<
"</div></td><td valign=\"top\">"
1138 <<
"</td></tr></table></div>\n";
1146 const ArrowMap &ArrowIndices) {
1148 llvm::raw_string_ostream OS(S);
1151<style type="text/css">
1158 pointer-events: none;
1162 stroke-opacity: 0.2;
1164 marker-end: url(#arrowhead);
1168 stroke-opacity: 0.6;
1170 marker-end: url(#arrowheadSelected);
1180<svg xmlns="http://www.w3.org/2000/svg">
1182 <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
1183 viewBox="0 0 10 10" refX="3" refY="5"
1184 markerWidth="4" markerHeight="4">
1185 <path d="M 0 0 L 10 5 L 0 10 z" />
1187 <marker id="arrowhead" class="arrowhead" opacity="0.2"
1188 viewBox="0 0 10 10" refX="3" refY="5"
1189 markerWidth="4" markerHeight="4">
1190 <path d="M 0 0 L 10 5 L 0 10 z" />
1193 <g id="arrows" fill="none" stroke="blue" visibility="hidden">
1196 for (
unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
1197 OS <<
" <path class=\"arrow\" id=\"arrow" << Index <<
"\"/>\n";
1203<script type='text/javascript'>
1204const arrowIndices = )<<<";
1206 OS << ArrowIndices << "\n</script>\n";
1214 llvm::raw_string_ostream OS(Result);
1215 OS <<
"<span id=\"" << ClassName << Index <<
"\">";
1227unsigned HTMLDiagnostics::ProcessControlFlowPiece(
1234 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
1236 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
1243void HTMLDiagnostics::HighlightRange(
Rewriter& R,
FileID BugFileID,
1245 const char *HighlightStart,
1246 const char *HighlightEnd) {
1251 unsigned StartLineNo =
SM.getExpansionLineNumber(InstantiationStart);
1254 unsigned EndLineNo =
SM.getExpansionLineNumber(InstantiationEnd);
1256 if (EndLineNo < StartLineNo)
1259 if (
SM.getFileID(InstantiationStart) != BugFileID ||
1260 SM.getFileID(InstantiationEnd) != BugFileID)
1264 unsigned EndColNo =
SM.getExpansionColumnNumber(InstantiationEnd);
1265 unsigned OldEndColNo = EndColNo;
1281StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
1283<script type='text/javascript'>
1284var digitMatcher = new RegExp("[0-9]+");
1286var querySelectorAllArray = function(selector) {
1287 return Array.prototype.slice.call(
1288 document.querySelectorAll(selector));
1291document.addEventListener("DOMContentLoaded", function() {
1292 querySelectorAllArray(".PathNav > a").forEach(
1293 function(currentValue, currentIndex) {
1294 var hrefValue = currentValue.getAttribute("href");
1295 currentValue.onclick = function() {
1296 scrollTo(document.querySelector(hrefValue));
1302var findNum = function() {
1303 var s = document.querySelector(".msg.selected");
1304 if (!s || s.id == "EndPath") {
1307 var out = parseInt(digitMatcher.exec(s.id)[0]);
1311var classListAdd = function(el, theClass) {
1312 if(!el.className.baseVal)
1313 el.className += " " + theClass;
1315 el.className.baseVal += " " + theClass;
1318var classListRemove = function(el, theClass) {
1319 var className = (!el.className.baseVal) ?
1320 el.className : el.className.baseVal;
1321 className = className.replace(" " + theClass, "");
1322 if(!el.className.baseVal)
1323 el.className = className;
1325 el.className.baseVal = className;
1328var scrollTo = function(el) {
1329 querySelectorAllArray(".selected").forEach(function(s) {
1330 classListRemove(s, "selected");
1332 classListAdd(el, "selected");
1333 window.scrollBy(0, el.getBoundingClientRect().top -
1334 (window.innerHeight / 2));
1335 highlightArrowsForSelectedEvent();
1338var move = function(num, up, numItems) {
1339 if (num == 1 && up || num == numItems - 1 && !up) {
1341 } else if (num == 0 && up) {
1342 return numItems - 1;
1343 } else if (num == 0 && !up) {
1344 return 1 % numItems;
1346 return up ? num - 1 : num + 1;
1349var numToId = function(num) {
1351 return document.getElementById("EndPath")
1353 return document.getElementById("Path" + num);
1356var navigateTo = function(up) {
1357 var numItems = document.querySelectorAll(
1358 ".line > .msgEvent, .line > .msgControl").length;
1359 var currentSelected = findNum();
1360 var newSelected = move(currentSelected, up, numItems);
1361 var newEl = numToId(newSelected, numItems);
1363 // Scroll element into center.
1367window.addEventListener("keydown", function (event) {
1368 if (event.defaultPrevented) {
1372 if (event.keyCode == 74) {
1373 navigateTo(/*up=*/false);
1375 } else if (event.keyCode == 75) {
1376 navigateTo(/*up=*/true);
1380 event.preventDefault();
1386StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
1388<script type='text/javascript'>
1389// Return range of numbers from a range [lower, upper).
1390function range(lower, upper) {
1392 for (var i = lower; i <= upper; ++i) {
1398var getRelatedArrowIndices = function(pathId) {
1399 // HTML numeration of events is a bit different than it is in the path.
1400 // Everything is rotated one step to the right, so the last element
1401 // (error diagnostic) has index 0.
1403 // arrowIndices has at least 2 elements
1404 pathId = arrowIndices.length - 1;
1407 return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
1410var highlightArrowsForSelectedEvent = function() {
1411 const selectedNum = findNum();
1412 const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
1413 arrowIndicesToHighlight.forEach((index) => {
1414 var arrow = document.querySelector("#arrow" + index);
1416 classListAdd(arrow, "selected")
1421var getAbsoluteBoundingRect = function(element) {
1422 const relative = element.getBoundingClientRect();
1424 left: relative.left + window.pageXOffset,
1425 right: relative.right + window.pageXOffset,
1426 top: relative.top + window.pageYOffset,
1427 bottom: relative.bottom + window.pageYOffset,
1428 height: relative.height,
1429 width: relative.width
1433var drawArrow = function(index) {
1434 // This function is based on the great answer from SO:
1435 // https://stackoverflow.com/a/39575674/11582326
1436 var start = document.querySelector("#start" + index);
1437 var end = document.querySelector("#end" + index);
1438 var arrow = document.querySelector("#arrow" + index);
1440 var startRect = getAbsoluteBoundingRect(start);
1441 var endRect = getAbsoluteBoundingRect(end);
1443 // It is an arrow from a token to itself, no need to visualize it.
1444 if (startRect.top == endRect.top &&
1445 startRect.left == endRect.left)
1448 // Each arrow is a very simple Bézier curve, with two nodes and
1449 // two handles. So, we need to calculate four points in the window:
1451 var posStart = { x: 0, y: 0 };
1453 var posEnd = { x: 0, y: 0 };
1454 // * handle for the start node
1455 var startHandle = { x: 0, y: 0 };
1456 // * handle for the end node
1457 var endHandle = { x: 0, y: 0 };
1458 // One can visualize it as follows:
1474 // NOTE: (0, 0) is the top left corner of the window.
1476 // We have 3 similar, but still different scenarios to cover:
1478 // 1. Two tokens on different lines.
1483 // In this situation, we draw arrow on the left curving to the left.
1484 // 2. Two tokens on the same line, and the destination is on the right.
1489 // In this situation, we draw arrow above curving upwards.
1490 // 3. Two tokens on the same line, and the destination is on the left.
1494 // In this situation, we draw arrow below curving downwards.
1495 const onDifferentLines = startRect.top <= endRect.top - 5 ||
1496 startRect.top >= endRect.top + 5;
1497 const leftToRight = startRect.left < endRect.left;
1499 // NOTE: various magic constants are chosen empirically for
1500 // better positioning and look
1501 if (onDifferentLines) {
1503 const topToBottom = startRect.top < endRect.top;
1504 posStart.x = startRect.left - 1;
1505 // We don't want to start it at the top left corner of the token,
1506 // it doesn't feel like this is where the arrow comes from.
1507 // For this reason, we start it in the middle of the left side
1509 posStart.y = startRect.top + startRect.height / 2;
1511 // End node has arrow head and we give it a bit more space.
1512 posEnd.x = endRect.left - 4;
1513 posEnd.y = endRect.top;
1515 // Utility object with x and y offsets for handles.
1517 // We want bottom-to-top arrow to curve a bit more, so it doesn't
1518 // overlap much with top-to-bottom curves (much more frequent).
1519 x: topToBottom ? 15 : 25,
1520 y: Math.min((posEnd.y - posStart.y) / 3, 10)
1523 // When destination is on the different line, we can make a
1524 // curvier arrow because we have space for it.
1525 // So, instead of using
1527 // startHandle.x = posStart.x - curvature.x
1528 // endHandle.x = posEnd.x - curvature.x
1530 // We use the leftmost of these two values for both handles.
1531 startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
1532 endHandle.x = startHandle.x;
1534 // Curving downwards from the start node...
1535 startHandle.y = posStart.y + curvature.y;
1536 // ... and upwards from the end node.
1537 endHandle.y = posEnd.y - curvature.y;
1539 } else if (leftToRight) {
1541 // Starting from the top right corner...
1542 posStart.x = startRect.right - 1;
1543 posStart.y = startRect.top;
1545 // ...and ending at the top left corner of the end token.
1546 posEnd.x = endRect.left + 1;
1547 posEnd.y = endRect.top - 1;
1549 // Utility object with x and y offsets for handles.
1551 x: Math.min((posEnd.x - posStart.x) / 3, 15),
1555 // Curving to the right...
1556 startHandle.x = posStart.x + curvature.x;
1557 // ... and upwards from the start node.
1558 startHandle.y = posStart.y - curvature.y;
1560 // And to the left...
1561 endHandle.x = posEnd.x - curvature.x;
1562 // ... and upwards from the end node.
1563 endHandle.y = posEnd.y - curvature.y;
1567 // Starting from the bottom right corner...
1568 posStart.x = startRect.right;
1569 posStart.y = startRect.bottom;
1571 // ...and ending also at the bottom right corner, but of the end token.
1572 posEnd.x = endRect.right - 1;
1573 posEnd.y = endRect.bottom + 1;
1575 // Utility object with x and y offsets for handles.
1577 x: Math.min((posStart.x - posEnd.x) / 3, 15),
1581 // Curving to the left...
1582 startHandle.x = posStart.x - curvature.x;
1583 // ... and downwards from the start node.
1584 startHandle.y = posStart.y + curvature.y;
1586 // And to the right...
1587 endHandle.x = posEnd.x + curvature.x;
1588 // ... and downwards from the end node.
1589 endHandle.y = posEnd.y + curvature.y;
1592 // Put it all together into a path.
1593 // More information on the format:
1594 // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
1595 var pathStr = "M" + posStart.x + "," + posStart.y + " " +
1596 "C" + startHandle.x + "," + startHandle.y + " " +
1597 endHandle.x + "," + endHandle.y + " " +
1598 posEnd.x + "," + posEnd.y;
1600 arrow.setAttribute("d", pathStr);
1603var drawArrows = function() {
1604 const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
1605 for (var i = 0; i < numOfArrows; ++i) {
1610var toggleArrows = function(event) {
1611 const arrows = document.querySelector("#arrows");
1612 if (event.target.checked) {
1613 arrows.setAttribute("visibility", "visible");
1615 arrows.setAttribute("visibility", "hidden");
1619window.addEventListener("resize", drawArrows);
1620document.addEventListener("DOMContentLoaded", function() {
1621 // Whenever we show invocation, locations change, i.e. we
1622 // need to redraw arrows.
1624 .querySelector('input[id="showinvocation"]')
1625 .addEventListener("click", drawArrows);
1626 // Hiding irrelevant lines also should cause arrow rerender.
1628 .querySelector('input[name="showCounterexample"]')
1629 .addEventListener("change", drawArrows);
1631 .querySelector('input[name="showArrows"]')
1632 .addEventListener("change", toggleArrows);
1634 // Default highlighting for the last event.
1635 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
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.
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.