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);
315 FileName <<
"report-";
327 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
331 FileName << llvm::sys::path::filename(Entry->
getName()).str() <<
"-"
332 << declName.c_str() <<
"-" << offsetDecl <<
"-";
335 FileName << StringRef(
getIssueHash(D, PP)).substr(0, 6).str() <<
".html";
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
542 const FileEntry *Entry,
const char *declName) {
549 if (llvm::sys::path::is_relative(Entry->
getName())) {
550 llvm::sys::fs::current_path(DirName);
554 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
555 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
560 generateKeyboardNavigationJavascript());
563 generateArrowDrawingJavascript());
567 showRelevantLinesJavascript(D, path));
572 llvm::raw_string_ostream os(
s);
574 os <<
"<!-- REPORTHEADER -->\n"
575 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
576 "<tr><td class=\"rowname\">File:</td><td>"
579 <<
"</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
580 "<a href=\"#EndPath\">line "
588 unsigned NumExtraPieces = 0;
589 for (
const auto &Piece : path) {
590 if (
const auto *
P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
592 P->getLocation().asLocation().getExpansionLineNumber();
594 P->getLocation().asLocation().getExpansionColumnNumber();
595 os <<
"<tr><td class=\"rowname\">Note:</td><td>"
596 <<
"<a href=\"#Note" << NumExtraPieces <<
"\">line "
597 << LineNumber <<
", column " << ColumnNumber <<
"</a><br />"
598 <<
P->getString() <<
"</td></tr>";
612<!-- REPORTSUMMARYEXTRA -->
613<h3>Annotated Source Code</h3>
614<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
615 to see keyboard shortcuts</p>
616<input type="checkbox" class="spoilerhider" id="showinvocation" />
617<label for="showinvocation" >Show analyzer invocation</label>
618<div class="spoiler">clang -cc1 )<<<";
622<div id='tooltiphint' hidden="true">
623 <p>Keyboard shortcuts: </p>
625 <li>Use 'j/k' keys for keyboard navigation</li>
626 <li>Use 'Shift+S' to show/hide relevant lines</li>
627 <li>Use '?' to toggle this window</li>
629 <a href="#" onclick="toggleHelp(); return false;">Close</a>
639 llvm::raw_string_ostream os(
s);
642 if (!BugDesc.empty())
643 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
647 os <<
"\n<!-- BUGTYPE " <<
BugType <<
" -->\n";
656 if (!BugCategory.empty())
657 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
659 os <<
"\n<!-- BUGFILE " << DirName << Entry->
getName() <<
" -->\n";
661 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry->
getName()) <<
" -->\n";
663 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
665 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " <<
getIssueHash(D, PP)
668 os <<
"\n<!-- BUGLINE "
672 os <<
"\n<!-- BUGCOLUMN "
676 os <<
"\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) <<
" -->\n";
679 os <<
"\n<!-- BUGMETAEND -->\n";
688StringRef HTMLDiagnostics::showHelpJavascript() {
690<script type='text/javascript'>
692var toggleHelp = function() {
693 var hint = document.querySelector("#tooltiphint");
694 var attributeName = "hidden";
695 if (hint.hasAttribute(attributeName)) {
696 hint.removeAttribute(attributeName);
698 hint.setAttribute("hidden", "true");
701window.addEventListener("keydown", function (event) {
702 if (event.defaultPrevented) {
705 if (event.key == "?") {
710 event.preventDefault();
717 return !(
Range.getBegin().isMacroID() ||
Range.getEnd().isMacroID());
722 const std::vector<SourceRange> &PopUpRanges) {
723 for (
const auto &
Range : PopUpRanges) {
728 "<table class='variable_popup'><tbody>",
735 std::vector<SourceRange> &PopUpRanges,
736 unsigned int LastReportedPieceIndex,
737 unsigned int PopUpPieceIndex) {
739 llvm::raw_svector_ostream Out(Buf);
746 Out <<
"<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
747 << LastReportedPieceIndex;
750 Out <<
'.' << PopUpPieceIndex;
752 Out <<
"</div></td><td>" << Piece.
getString() <<
"</td></tr>";
755 if (!llvm::is_contained(PopUpRanges,
Range)) {
757 PopUpRanges.push_back(
Range);
759 Out <<
"</tbody></table></span>";
761 "<span class='variable'>", Buf.c_str(),
775 unsigned TotalPieces = getPathSizeWithoutArrows(path);
776 unsigned TotalNotePieces =
778 return isa<PathDiagnosticNotePiece>(*p);
780 unsigned PopUpPieceCount =
782 return isa<PathDiagnosticPopUpPiece>(*p);
785 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
786 unsigned NumRegularPieces = TotalRegularPieces;
787 unsigned NumNotePieces = TotalNotePieces;
788 unsigned NumberOfArrows = 0;
790 std::map<int, int> IndexMap;
791 ArrowMap ArrowIndices(TotalRegularPieces + 1);
794 std::vector<SourceRange> PopUpRanges;
796 const auto &Piece = *I.get();
798 if (isa<PathDiagnosticPopUpPiece>(Piece)) {
799 ++IndexMap[NumRegularPieces];
800 }
else if (isa<PathDiagnosticNotePiece>(Piece)) {
804 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
807 }
else if (isArrowPiece(Piece)) {
808 NumberOfArrows = ProcessControlFlowPiece(
809 R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
810 ArrowIndices[NumRegularPieces] = NumberOfArrows;
813 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
816 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
819 ArrowIndices[0] = NumberOfArrows;
829 assert(ArrowIndices.back() == 0 &&
830 "No arrows should be after the last event");
832 assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
833 "Incorrect arrow indices map");
837 NumRegularPieces = TotalRegularPieces;
839 const auto &Piece = *I.get();
841 if (
const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
842 int PopUpPieceIndex = IndexMap[NumRegularPieces];
852 if (PopUpPieceIndex > 0)
853 --IndexMap[NumRegularPieces];
855 }
else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
867 addArrowSVGs(R, FID, ArrowIndices);
878 const std::vector<SourceRange> &PopUpRanges,
879 unsigned num,
unsigned max) {
888 assert(&Pos.
getManager() == &
SM &&
"SourceManagers are different!");
889 std::pair<FileID, unsigned> LPosInfo =
SM.getDecomposedExpansionLoc(Pos);
891 if (LPosInfo.first != BugFileID)
894 llvm::MemoryBufferRef Buf =
SM.getBufferOrFake(LPosInfo.first);
895 const char *FileStart = Buf.getBufferStart();
899 unsigned ColNo =
SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
901 const char *LineStart = TokInstantiationPtr-ColNo;
904 const char *LineEnd = TokInstantiationPtr;
905 const char *FileEnd = Buf.getBufferEnd();
906 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
911 for (
const char*
c = LineStart;
c != TokInstantiationPtr; ++
c)
912 PosNo += *
c ==
'\t' ? 8 : 1;
916 const char *
Kind =
nullptr;
918 bool SuppressIndex = (
max == 1);
919 switch (
P.getKind()) {
927 SuppressIndex =
true;
931 llvm_unreachable(
"Calls and extra notes should already be handled");
935 llvm::raw_string_ostream os(sbuf);
937 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
946 os <<
"\" class=\"msg";
948 os <<
" msg" <<
Kind;
949 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
952 if (!isa<PathDiagnosticMacroPiece>(
P)) {
954 const auto &Msg =
P.getString();
955 unsigned max_token = 0;
957 unsigned len = Msg.size();
967 if (cnt > max_token) max_token = cnt;
976 const unsigned max_line = 120;
978 if (max_token >= max_line)
981 unsigned characters = max_line;
982 unsigned lines = len / max_line;
985 for (; characters > max_token; --characters)
986 if (len / characters > lines) {
996 os <<
"; max-width:" << em <<
"em";
999 os <<
"; max-width:100em";
1003 if (!SuppressIndex) {
1004 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
1005 os <<
"<div class=\"PathIndex";
1006 if (Kind) os <<
" PathIndex" <<
Kind;
1007 os <<
"\">" << num <<
"</div>";
1010 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
1012 <<
"\" title=\"Previous event ("
1014 <<
")\">←</a></div>";
1020 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&
P)) {
1021 os <<
"Within the expansion of the macro '";
1029 const char* MacroName = LocInfo.second + BufferInfo.data();
1031 BufferInfo.begin(), MacroName, BufferInfo.end());
1035 for (
unsigned i = 0, n = TheTok.
getLength(); i < n; ++i)
1041 if (!SuppressIndex) {
1044 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1048 os <<
"Path" << (num + 1);
1049 os <<
"\" title=\"Next event ("
1051 <<
")\">→</a></div></td>";
1054 os <<
"</tr></table>";
1058 ProcessMacroPiece(os, *MP, 0);
1063 if (!SuppressIndex) {
1066 os <<
"<td><div class=\"PathNav\"><a href=\"#";
1070 os <<
"Path" << (num + 1);
1071 os <<
"\" title=\"Next event ("
1073 <<
")\">→</a></div></td>";
1076 os <<
"</tr></table>";
1080 os <<
"</div></td></tr>";
1083 unsigned DisplayPos = LineEnd - FileStart;
1085 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
1091 for (
const auto &
Range : Ranges) {
1093 if (llvm::is_contained(PopUpRanges,
Range))
1096 HighlightRange(R, LPosInfo.first,
Range);
1101 unsigned x = n % (
'z' -
'a');
1107 os << char(
'a' + x);
1110unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
1113 for (
const auto &subPiece :
P.subPieces) {
1114 if (
const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
1115 num = ProcessMacroPiece(os, *MP, num);
1119 if (
const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
1120 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
1121 "margin-left:5px\">"
1122 "<table class=\"msgT\"><tr>"
1123 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
1125 os <<
"</div></td><td valign=\"top\">"
1127 <<
"</td></tr></table></div>\n";
1135 const ArrowMap &ArrowIndices) {
1137 llvm::raw_string_ostream
OS(S);
1140<style type="text/css">
1147 pointer-events: none;
1151 stroke-opacity: 0.2;
1153 marker-end: url(#arrowhead);
1157 stroke-opacity: 0.6;
1159 marker-end: url(#arrowheadSelected);
1169<svg xmlns="http://www.w3.org/2000/svg">
1171 <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
1172 viewBox="0 0 10 10" refX="3" refY="5"
1173 markerWidth="4" markerHeight="4">
1174 <path d="M 0 0 L 10 5 L 0 10 z" />
1176 <marker id="arrowhead" class="arrowhead" opacity="0.2"
1177 viewBox="0 0 10 10" refX="3" refY="5"
1178 markerWidth="4" markerHeight="4">
1179 <path d="M 0 0 L 10 5 L 0 10 z" />
1182 <g id="arrows" fill="none" stroke="blue" visibility="hidden">
1185 for (
unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
1186 OS <<
" <path class=\"arrow\" id=\"arrow" << Index <<
"\"/>\n";
1192<script type='text/javascript'>
1193const arrowIndices = )<<<";
1195 OS << ArrowIndices << "\n</script>\n";
1203 llvm::raw_string_ostream
OS(Result);
1204 OS <<
"<span id=\"" << ClassName << Index <<
"\">";
1216unsigned HTMLDiagnostics::ProcessControlFlowPiece(
1223 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
1225 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
1232void HTMLDiagnostics::HighlightRange(
Rewriter& R,
FileID BugFileID,
1234 const char *HighlightStart,
1235 const char *HighlightEnd) {
1240 unsigned StartLineNo =
SM.getExpansionLineNumber(InstantiationStart);
1243 unsigned EndLineNo =
SM.getExpansionLineNumber(InstantiationEnd);
1245 if (EndLineNo < StartLineNo)
1248 if (
SM.getFileID(InstantiationStart) != BugFileID ||
1249 SM.getFileID(InstantiationEnd) != BugFileID)
1253 unsigned EndColNo =
SM.getExpansionColumnNumber(InstantiationEnd);
1254 unsigned OldEndColNo = EndColNo;
1270StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
1272<script type='text/javascript'>
1273var digitMatcher = new RegExp("[0-9]+");
1275var querySelectorAllArray = function(selector) {
1276 return Array.prototype.slice.call(
1277 document.querySelectorAll(selector));
1280document.addEventListener("DOMContentLoaded", function() {
1281 querySelectorAllArray(".PathNav > a").forEach(
1282 function(currentValue, currentIndex) {
1283 var hrefValue = currentValue.getAttribute("href");
1284 currentValue.onclick = function() {
1285 scrollTo(document.querySelector(hrefValue));
1291var findNum = function() {
1292 var s = document.querySelector(".msg.selected");
1293 if (!s || s.id == "EndPath") {
1296 var out = parseInt(digitMatcher.exec(s.id)[0]);
1300var classListAdd = function(el, theClass) {
1301 if(!el.className.baseVal)
1302 el.className += " " + theClass;
1304 el.className.baseVal += " " + theClass;
1307var classListRemove = function(el, theClass) {
1308 var className = (!el.className.baseVal) ?
1309 el.className : el.className.baseVal;
1310 className = className.replace(" " + theClass, "");
1311 if(!el.className.baseVal)
1312 el.className = className;
1314 el.className.baseVal = className;
1317var scrollTo = function(el) {
1318 querySelectorAllArray(".selected").forEach(function(s) {
1319 classListRemove(s, "selected");
1321 classListAdd(el, "selected");
1322 window.scrollBy(0, el.getBoundingClientRect().top -
1323 (window.innerHeight / 2));
1324 highlightArrowsForSelectedEvent();
1327var move = function(num, up, numItems) {
1328 if (num == 1 && up || num == numItems - 1 && !up) {
1330 } else if (num == 0 && up) {
1331 return numItems - 1;
1332 } else if (num == 0 && !up) {
1333 return 1 % numItems;
1335 return up ? num - 1 : num + 1;
1338var numToId = function(num) {
1340 return document.getElementById("EndPath")
1342 return document.getElementById("Path" + num);
1345var navigateTo = function(up) {
1346 var numItems = document.querySelectorAll(
1347 ".line > .msgEvent, .line > .msgControl").length;
1348 var currentSelected = findNum();
1349 var newSelected = move(currentSelected, up, numItems);
1350 var newEl = numToId(newSelected, numItems);
1352 // Scroll element into center.
1356window.addEventListener("keydown", function (event) {
1357 if (event.defaultPrevented) {
1361 if (event.keyCode == 74) {
1362 navigateTo(/*up=*/false);
1364 } else if (event.keyCode == 75) {
1365 navigateTo(/*up=*/true);
1369 event.preventDefault();
1375StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
1377<script type='text/javascript'>
1378// Return range of numbers from a range [lower, upper).
1379function range(lower, upper) {
1381 for (var i = lower; i <= upper; ++i) {
1387var getRelatedArrowIndices = function(pathId) {
1388 // HTML numeration of events is a bit different than it is in the path.
1389 // Everything is rotated one step to the right, so the last element
1390 // (error diagnostic) has index 0.
1392 // arrowIndices has at least 2 elements
1393 pathId = arrowIndices.length - 1;
1396 return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
1399var highlightArrowsForSelectedEvent = function() {
1400 const selectedNum = findNum();
1401 const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
1402 arrowIndicesToHighlight.forEach((index) => {
1403 var arrow = document.querySelector("#arrow" + index);
1405 classListAdd(arrow, "selected")
1410var getAbsoluteBoundingRect = function(element) {
1411 const relative = element.getBoundingClientRect();
1413 left: relative.left + window.pageXOffset,
1414 right: relative.right + window.pageXOffset,
1415 top: relative.top + window.pageYOffset,
1416 bottom: relative.bottom + window.pageYOffset,
1417 height: relative.height,
1418 width: relative.width
1422var drawArrow = function(index) {
1423 // This function is based on the great answer from SO:
1424 // https://stackoverflow.com/a/39575674/11582326
1425 var start = document.querySelector("#start" + index);
1426 var end = document.querySelector("#end" + index);
1427 var arrow = document.querySelector("#arrow" + index);
1429 var startRect = getAbsoluteBoundingRect(start);
1430 var endRect = getAbsoluteBoundingRect(end);
1432 // It is an arrow from a token to itself, no need to visualize it.
1433 if (startRect.top == endRect.top &&
1434 startRect.left == endRect.left)
1437 // Each arrow is a very simple Bézier curve, with two nodes and
1438 // two handles. So, we need to calculate four points in the window:
1440 var posStart = { x: 0, y: 0 };
1442 var posEnd = { x: 0, y: 0 };
1443 // * handle for the start node
1444 var startHandle = { x: 0, y: 0 };
1445 // * handle for the end node
1446 var endHandle = { x: 0, y: 0 };
1447 // One can visualize it as follows:
1463 // NOTE: (0, 0) is the top left corner of the window.
1465 // We have 3 similar, but still different scenarios to cover:
1467 // 1. Two tokens on different lines.
1472 // In this situation, we draw arrow on the left curving to the left.
1473 // 2. Two tokens on the same line, and the destination is on the right.
1478 // In this situation, we draw arrow above curving upwards.
1479 // 3. Two tokens on the same line, and the destination is on the left.
1483 // In this situation, we draw arrow below curving downwards.
1484 const onDifferentLines = startRect.top <= endRect.top - 5 ||
1485 startRect.top >= endRect.top + 5;
1486 const leftToRight = startRect.left < endRect.left;
1488 // NOTE: various magic constants are chosen empirically for
1489 // better positioning and look
1490 if (onDifferentLines) {
1492 const topToBottom = startRect.top < endRect.top;
1493 posStart.x = startRect.left - 1;
1494 // We don't want to start it at the top left corner of the token,
1495 // it doesn't feel like this is where the arrow comes from.
1496 // For this reason, we start it in the middle of the left side
1498 posStart.y = startRect.top + startRect.height / 2;
1500 // End node has arrow head and we give it a bit more space.
1501 posEnd.x = endRect.left - 4;
1502 posEnd.y = endRect.top;
1504 // Utility object with x and y offsets for handles.
1506 // We want bottom-to-top arrow to curve a bit more, so it doesn't
1507 // overlap much with top-to-bottom curves (much more frequent).
1508 x: topToBottom ? 15 : 25,
1509 y: Math.min((posEnd.y - posStart.y) / 3, 10)
1512 // When destination is on the different line, we can make a
1513 // curvier arrow because we have space for it.
1514 // So, instead of using
1516 // startHandle.x = posStart.x - curvature.x
1517 // endHandle.x = posEnd.x - curvature.x
1519 // We use the leftmost of these two values for both handles.
1520 startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
1521 endHandle.x = startHandle.x;
1523 // Curving downwards from the start node...
1524 startHandle.y = posStart.y + curvature.y;
1525 // ... and upwards from the end node.
1526 endHandle.y = posEnd.y - curvature.y;
1528 } else if (leftToRight) {
1530 // Starting from the top right corner...
1531 posStart.x = startRect.right - 1;
1532 posStart.y = startRect.top;
1534 // ...and ending at the top left corner of the end token.
1535 posEnd.x = endRect.left + 1;
1536 posEnd.y = endRect.top - 1;
1538 // Utility object with x and y offsets for handles.
1540 x: Math.min((posEnd.x - posStart.x) / 3, 15),
1544 // Curving to the right...
1545 startHandle.x = posStart.x + curvature.x;
1546 // ... and upwards from the start node.
1547 startHandle.y = posStart.y - curvature.y;
1549 // And to the left...
1550 endHandle.x = posEnd.x - curvature.x;
1551 // ... and upwards from the end node.
1552 endHandle.y = posEnd.y - curvature.y;
1556 // Starting from the bottom right corner...
1557 posStart.x = startRect.right;
1558 posStart.y = startRect.bottom;
1560 // ...and ending also at the bottom right corner, but of the end token.
1561 posEnd.x = endRect.right - 1;
1562 posEnd.y = endRect.bottom + 1;
1564 // Utility object with x and y offsets for handles.
1566 x: Math.min((posStart.x - posEnd.x) / 3, 15),
1570 // Curving to the left...
1571 startHandle.x = posStart.x - curvature.x;
1572 // ... and downwards from the start node.
1573 startHandle.y = posStart.y + curvature.y;
1575 // And to the right...
1576 endHandle.x = posEnd.x + curvature.x;
1577 // ... and downwards from the end node.
1578 endHandle.y = posEnd.y + curvature.y;
1581 // Put it all together into a path.
1582 // More information on the format:
1583 // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
1584 var pathStr = "M" + posStart.x + "," + posStart.y + " " +
1585 "C" + startHandle.x + "," + startHandle.y + " " +
1586 endHandle.x + "," + endHandle.y + " " +
1587 posEnd.x + "," + posEnd.y;
1589 arrow.setAttribute("d", pathStr);
1592var drawArrows = function() {
1593 const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
1594 for (var i = 0; i < numOfArrows; ++i) {
1599var toggleArrows = function(event) {
1600 const arrows = document.querySelector("#arrows");
1601 if (event.target.checked) {
1602 arrows.setAttribute("visibility", "visible");
1604 arrows.setAttribute("visibility", "hidden");
1608window.addEventListener("resize", drawArrows);
1609document.addEventListener("DOMContentLoaded", function() {
1610 // Whenever we show invocation, locations change, i.e. we
1611 // need to redraw arrows.
1613 .querySelector('input[id="showinvocation"]')
1614 .addEventListener("click", drawArrows);
1615 // Hiding irrelevant lines also should cause arrow rerender.
1617 .querySelector('input[name="showCounterexample"]')
1618 .addEventListener("change", drawArrows);
1620 .querySelector('input[name="showArrows"]')
1621 .addEventListener("change", toggleArrows);
1623 // Default highlighting for the last event.
1624 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 c
__device__ __2f16 float bool s
Decl - This represents one declaration (or definition), e.g.
Cached information about one file (either on disk or in the virtual file system).
StringRef getName() const
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.
SourceLocation getLocForEndOfFile(FileID FID) const
Return the source location corresponding to the last byte of the specified file.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
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.
std::deque< std::string >::const_iterator meta_iterator
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.
@ C
Languages that the frontend can parse and compile.
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.