clang: lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
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"
43#include
44#include
45#include
46#include
47#include
48#include
49#include
50#include <system_error>
51#include
52#include
53
54using namespace clang;
55using namespace ento;
56using llvm::RewriteBuffer;
57
58
59
60
61
62namespace {
63
64class ArrowMap;
65
68 std::string Directory;
69 bool createdDir = false;
70 bool noDir = false;
72 const bool SupportsCrossFileDiagnostics;
73 llvm::StringSet<> EmittedHashes;
76
77public:
79 const std::string &OutputDir, const Preprocessor &pp,
80 bool supportsMultipleFiles)
81 : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp),
82 SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
83
85
87 FilesMade *filesMade) override;
88
89 StringRef getName() const override { return "HTMLDiagnostics"; }
90
92 return SupportsCrossFileDiagnostics;
93 }
94
96 unsigned num);
97
98 unsigned ProcessControlFlowPiece(Rewriter &R, FileID BugFileID,
100 unsigned Number);
101
103 const std::vector &PopUpRanges, unsigned num,
104 unsigned max);
105
107 const char *HighlightStart = "<span class=\"mrange\">",
108 const char *HighlightEnd = "");
109
110 void ReportDiag(const PathDiagnostic &D, FilesMade *filesMade);
111
112
115 const char *declName);
116
117
121
122
124
127 }
128
129private:
131 const ArrowMap &ArrowIndices);
132
133
134 StringRef showHelpJavascript();
135
136
137 StringRef generateKeyboardNavigationJavascript();
138
139
140 StringRef generateArrowDrawingJavascript();
141
142
143 std::string showRelevantLinesJavascript(const PathDiagnostic &D,
145
146
148 llvm::raw_string_ostream &os);
149};
150
152 return isa(P) && P.getString().empty();
153}
154
155unsigned getPathSizeWithoutArrows(const PathPieces &Path) {
156 unsigned TotalPieces = Path.size();
157 unsigned TotalArrowPieces = llvm::count_if(
159 return TotalPieces - TotalArrowPieces;
160}
161
162class ArrowMap : public std::vector {
163 using Base = std::vector;
164
165public:
166 ArrowMap(unsigned Size) : Base(Size, 0) {}
167 unsigned getTotalNumberOfArrows() const { return at(0); }
168};
169
170llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) {
171 OS << "[ ";
172 llvm::interleave(Indices, OS, ",");
173 return OS << " ]";
174}
175
176}
177
178void ento::createHTMLDiagnosticConsumer(
180 const std::string &OutputDir, const Preprocessor &PP,
183
184
185
186
187
188
189 createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,
190 MacroExpansions);
191
192
193 if (OutputDir.empty())
194 return;
195
196 C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true));
197}
198
199void ento::createHTMLSingleFileDiagnosticConsumer(
201 const std::string &OutputDir, const Preprocessor &PP,
204 createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,
205 MacroExpansions);
206
207
208 if (OutputDir.empty())
209 return;
210
211 C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false));
212}
213
214void ento::createPlistHTMLDiagnosticConsumer(
216 const std::string &prefix, const Preprocessor &PP,
219 createHTMLDiagnosticConsumer(
220 DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,
221 MacroExpansions);
222 createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU,
223 MacroExpansions);
224 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP,
225 CTU, MacroExpansions);
226}
227
228void ento::createSarifHTMLDiagnosticConsumer(
230 const std::string &sarif_file, const Preprocessor &PP,
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,
237 MacroExpansions);
238 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, sarif_file,
239 PP, CTU, MacroExpansions);
240}
241
242
243
244
245
246void HTMLDiagnostics::FlushDiagnosticsImpl(
247 std::vector<const PathDiagnostic *> &Diags,
248 FilesMade *filesMade) {
249 for (const auto Diag : Diags)
250 ReportDiag(*Diag, filesMade);
251}
252
260 SMgr);
261 return getIssueHash(L, D.getCheckerName(), D.getBugType(),
263}
264
266 FilesMade *filesMade) {
267
268 if (!createdDir) {
269 createdDir = true;
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';
273 noDir = true;
274 return;
275 }
276 }
277
278 if (noDir)
279 return;
280
281
282 PathPieces path = D.path.flatten(false);
283
284
285 assert(!path.empty());
286 const SourceManager &SMgr = path.front()->getLocation().getManager();
287
288
290
291
293 int offsetDecl = 0;
294 if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
295 if (const auto *ND = dyn_cast(DeclWithIssue))
296 declName = ND->getDeclName().getAsString();
297
298 if (const Stmt *Body = DeclWithIssue->getBody()) {
299
300
302 SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),
303 SMgr);
305 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
306 }
307 }
308
310 auto [It, IsNew] = EmittedHashes.insert(IssueHash);
311 if (!IsNew) {
312
313 return;
314 }
315
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";
319 return;
320 }
321
322
323 int FD;
324
326 llvm::raw_svector_ostream FileName(FileNameStr);
328
329
330
331
332
333
334
336
337
339 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
340
342
343 FileName << llvm::sys::path::filename(Entry->getName()).str() << "-"
344 << declName.c_str() << "-" << offsetDecl << "-";
345 }
346
347 FileName << StringRef(IssueHash).substr(0, 6).str() << ".html";
348
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';
354 return;
355 }
356
357 if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
358 ResultPath, FD, llvm::sys::fs::CD_CreateNew,
359 llvm::sys::fs::OF_Text)) {
360
361
362
363
364
365 if (EC != llvm::errc::file_exists) {
366 llvm::errs() << "warning: could not create file in '" << Directory
367 << "': " << EC.message() << '\n';
368 }
369 return;
370 }
371
372 llvm::raw_fd_ostream os(FD, true);
373
374 if (filesMade)
375 filesMade->addDiagnostic(D, getName(),
376 llvm::sys::path::filename(ResultPath));
377
378
379 os << report;
380}
381
384
385 std::vector FileIDs;
386 for (auto I : path) {
387 FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
388 if (llvm::is_contained(FileIDs, FID))
389 continue;
390
391 FileIDs.push_back(FID);
392 RewriteFile(R, path, FID);
393 }
394
395 if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
396
397 for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {
398 std::string s;
399 llvm::raw_string_ostream os(s);
400
401 if (I != FileIDs.begin())
402 os << "
\n";
403
404 os << "<div id=File" << I->getHashValue() << ">\n";
405
406
407 if (I != FileIDs.begin())
408 os << "
409 << "\">←
410
412 << "\n";
413
414
415 if (I + 1 != E)
416 os << "
417 << "\">→
418
419 os << "\n";
420
422 }
423
424
425 for (auto I : llvm::drop_begin(FileIDs)) {
426 std::string s;
427 llvm::raw_string_ostream os(s);
428
430 for (auto BI : *Buf)
431 os << BI;
432
434 }
435 }
436
438 if (!Buf)
439 return {};
440
441
443 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
445 FinalizeHTML(D, R, SMgr, path, FileIDs[0], *Entry, declName);
446
447 std::string file;
448 llvm::raw_string_ostream os(file);
449 for (auto BI : *Buf)
450 os << BI;
451
452 return file;
453}
454
455void HTMLDiagnostics::dumpCoverageData(
458 llvm::raw_string_ostream &os) {
459
461
462 os << "var relevant_lines = {";
463 for (auto I = ExecutedLines.begin(),
464 E = ExecutedLines.end(); I != E; ++I) {
465 if (I != ExecutedLines.begin())
466 os << ", ";
467
468 os << "\"" << I->first.getHashValue() << "\": {";
469 for (unsigned LineNo : I->second) {
470 if (LineNo != *(I->second.begin()))
471 os << ", ";
472
473 os << "\"" << LineNo << "\": 1";
474 }
475 os << "}";
476 }
477
478 os << "};";
479}
480
481std::string HTMLDiagnostics::showRelevantLinesJavascript(
483 std::string s;
484 llvm::raw_string_ostream os(s);
485 os << "
535
536
537
538
539 Show only relevant lines
540
541 <input type="checkbox" name="showArrows"
542 id="showArrows" style="margin-left: 10px" />
543
544 Show control flow arrows
545
546
547)<<<";
548
549 return s;
550}
551
556
557
558
559
561
562 if (llvm::sys::path::is_relative(Entry.getName())) {
563 llvm::sys::fs::current_path(DirName);
564 DirName += '/';
565 }
566
567 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
568 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
569
571
573 generateKeyboardNavigationJavascript());
574
576 generateArrowDrawingJavascript());
577
578
580 showRelevantLinesJavascript(D, path));
581
582
583 {
584 std::string s;
585 llvm::raw_string_ostream os(s);
586
587 os << "\n"
588 << "
Bug Summary
\n<table class=\"simpletable\">\n"589 "
592 << "\n
593 "<a href=\"#EndPath\">line "
594 << LineNumber
595 << ", column "
596 << ColumnNumber
597 << "
"
598 << D.getVerboseDescription() << "\n";
599
600
601 unsigned NumExtraPieces = 0;
602 for (const auto &Piece : path) {
603 if (const auto *P = dyn_cast(Piece.get())) {
604 int LineNumber =
605 P->getLocation().asLocation().getExpansionLineNumber();
606 int ColumnNumber =
607 P->getLocation().asLocation().getExpansionColumnNumber();
608 ++NumExtraPieces;
609 os << "
610 << "<a href=\"#Note" << NumExtraPieces << "\">line "
611 << LineNumber << ", column " << ColumnNumber << "
"
612 << P->getString() << "";
613 }
614 }
615
616
617
618 for (const std::string &Metadata :
619 llvm::make_range(D.meta_begin(), D.meta_end())) {
620 os << "
621 }
622
623 os << R"<<<(
624
625
626
Annotated Source Code
627
Press '?'
628 to see keyboard shortcuts
629
630Show analyzer invocation
631
633 os << R"<<<(
634
635
636
Keyboard shortcuts:
637
- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window
638
639
640
641
642 Close
643
644)<<<";
645
647 }
648
649
651 std::string s;
652 llvm::raw_string_ostream os(s);
653
654 StringRef BugDesc = D.getVerboseDescription();
655 if (!BugDesc.empty())
656 os << "\n\n";
657
658 StringRef BugType = D.getBugType();
660 os << "\n\n";
661
666 SMgr);
668 StringRef BugCategory = D.getCategory();
669 if (!BugCategory.empty())
670 os << "\n\n";
671
672 os << "\n\n";
673
674 os << "\n\n";
675
676 os << "\n\n";
677
678 os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)
679 << " -->\n";
680
681 os << "\n<!-- BUGLINE "
682 << LineNumber
683 << " -->\n";
684
685 os << "\n<!-- BUGCOLUMN "
686 << ColumnNumber
687 << " -->\n";
688
689 os << "\n\n";
690
691
692 os << "\n\n";
693
694
696 }
697
699}
700
701StringRef HTMLDiagnostics::showHelpJavascript() {
702 return R"<<<(
703
726)<<<";
727}
728
730 return !(Range.getBegin().isMacroID() || Range.getEnd().isMacroID());
731}
732
733static void
735 const std::vector &PopUpRanges) {
736 for (const auto &Range : PopUpRanges) {
738 continue;
739
741 "
" | " << Piece.getString() << " | ";
774 "", Buf.c_str(),
775 true);
776 } else {
777
779 true);
780 }
781}
782
783void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,
785
786
787
788 unsigned TotalPieces = getPathSizeWithoutArrows(path);
789 unsigned TotalNotePieces =
791 return isa(*p);
792 });
793 unsigned PopUpPieceCount =
795 return isa(*p);
796 });
797
798 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
799 unsigned NumRegularPieces = TotalRegularPieces;
800 unsigned NumNotePieces = TotalNotePieces;
801 unsigned NumberOfArrows = 0;
802
803 std::map<int, int> IndexMap;
804 ArrowMap ArrowIndices(TotalRegularPieces + 1);
805
806
807 std::vector PopUpRanges;
809 const auto &Piece = *I.get();
810
811 if (isa(Piece)) {
812 ++IndexMap[NumRegularPieces];
813 } else if (isa(Piece)) {
814
815
816
817 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
818 --NumNotePieces;
819
820 } else if (isArrowPiece(Piece)) {
821 NumberOfArrows = ProcessControlFlowPiece(
822 R, FID, cast(Piece), NumberOfArrows);
823 ArrowIndices[NumRegularPieces] = NumberOfArrows;
824
825 } else {
826 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
827 TotalRegularPieces);
828 --NumRegularPieces;
829 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
830 }
831 }
832 ArrowIndices[0] = NumberOfArrows;
833
834
835
836
837
838
839
840
841
842 assert(ArrowIndices.back() == 0 &&
843 "No arrows should be after the last event");
844
845 assert(llvm::is_sorted(ArrowIndices, std::greater()) &&
846 "Incorrect arrow indices map");
847
848
849
850 NumRegularPieces = TotalRegularPieces;
852 const auto &Piece = *I.get();
853
854 if (const auto *PopUpP = dyn_cast(&Piece)) {
855 int PopUpPieceIndex = IndexMap[NumRegularPieces];
856
857
858
859
860
861
863 PopUpPieceIndex);
864
865 if (PopUpPieceIndex > 0)
866 --IndexMap[NumRegularPieces];
867
868 } else if (!isa(Piece) && !isArrowPiece(Piece)) {
869 --NumRegularPieces;
870 }
871 }
872
873
875
876
879
880 addArrowSVGs(R, FID, ArrowIndices);
881
882
883
884
887}
888
889void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
891 const std::vector &PopUpRanges,
892 unsigned num, unsigned max) {
893
894
896
898 return;
899
901 assert(&Pos.getManager() == &SM && "SourceManagers are different!");
902 std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);
903
904 if (LPosInfo.first != BugFileID)
905 return;
906
907 llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first);
908 const char *FileStart = Buf.getBufferStart();
909
910
911
912 unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
914 const char *LineStart = TokInstantiationPtr-ColNo;
915
916
917 const char *LineEnd = TokInstantiationPtr;
918 const char *FileEnd = Buf.getBufferEnd();
919 while (*LineEnd != '\n' && LineEnd != FileEnd)
920 ++LineEnd;
921
922
923 unsigned PosNo = 0;
924 for (const char* c = LineStart; c != TokInstantiationPtr; ++c)
925 PosNo += *c == '\t' ? 8 : 1;
926
927
928
929 const char *Kind = nullptr;
930 bool IsNote = false;
931 bool SuppressIndex = (max == 1);
932 switch (P.getKind()) {
935
938 Kind = "Note";
939 IsNote = true;
940 SuppressIndex = true;
941 break;
944 llvm_unreachable("Calls and extra notes should already be handled");
945 }
946
947 std::string sbuf;
948 llvm::raw_string_ostream os(sbuf);
949
950 os << "\n
951
952 if (IsNote)
953 os << "Note" << num;
954 else if (num == max)
955 os << "EndPath";
956 else
957 os << "Path" << num;
958
959 os << "\" class=\"msg";
960 if (Kind)
961 os << " msg" << Kind;
962 os << "\" style=\"margin-left:" << PosNo << "ex";
963
964
965 if (!isa(P)) {
966
967 const auto &Msg = P.getString();
968 unsigned max_token = 0;
969 unsigned cnt = 0;
970 unsigned len = Msg.size();
971
972 for (char C : Msg)
973 switch (C) {
974 default:
975 ++cnt;
976 continue;
977 case ' ':
978 case '\t':
979 case '\n':
980 if (cnt > max_token) max_token = cnt;
981 cnt = 0;
982 }
983
984 if (cnt > max_token)
985 max_token = cnt;
986
987
988 unsigned em;
989 const unsigned max_line = 120;
990
991 if (max_token >= max_line)
992 em = max_token / 2;
993 else {
994 unsigned characters = max_line;
995 unsigned lines = len / max_line;
996
997 if (lines > 0) {
998 for (; characters > max_token; --characters)
999 if (len / characters > lines) {
1000 ++characters;
1001 break;
1002 }
1003 }
1004
1005 em = characters / 2;
1006 }
1007
1008 if (em < max_line/2)
1009 os << "; max-width:" << em << "em";
1010 }
1011 else
1012 os << "; max-width:100em";
1013
1014 os << "\">";
1015
1016 if (!SuppressIndex) {
1017 os << "<table class=\"msgT\">
1018 os << "<div class=\"PathIndex";
1019 if (Kind) os << " PathIndex" << Kind;
1020 os << "\">" << num << "";
1021
1022 if (num > 1) {
1023 os << "
1024 << (num - 1)
1025 << "\" title=\"Previous event ("
1026 << (num - 1)
1027 << ")\">←";
1028 }
1029
1030 os << "
1031 }
1032
1033 if (const auto *MP = dyn_cast(&P)) {
1034 os << "Within the expansion of the macro '";
1035
1036
1037 {
1042 const char* MacroName = LocInfo.second + BufferInfo.data();
1043 Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
1044 BufferInfo.begin(), MacroName, BufferInfo.end());
1045
1048 for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
1049 os << MacroName[i];
1050 }
1051
1052 os << "':\n";
1053
1054 if (!SuppressIndex) {
1055 os << "";
1056 if (num < max) {
1057 os << "
1058 if (num == max - 1)
1059 os << "EndPath";
1060 else
1061 os << "Path" << (num + 1);
1062 os << "\" title=\"Next event ("
1063 << (num + 1)
1064 << ")\">→";
1065 }
1066
1067 os << "";
1068 }
1069
1070
1071 ProcessMacroPiece(os, *MP, 0);
1072 }
1073 else {
1075
1076 if (!SuppressIndex) {
1077 os << "";
1078 if (num < max) {
1079 os << "
1080 if (num == max - 1)
1081 os << "EndPath";
1082 else
1083 os << "Path" << (num + 1);
1084 os << "\" title=\"Next event ("
1085 << (num + 1)
1086 << ")\">→";
1088
1089 os << "";
1090 }
1091 }
1092
1093 os << "";
1094
1096 unsigned DisplayPos = LineEnd - FileStart;
1098 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
1101
1102
1104 for (const auto &Range : Ranges) {
1105
1106 if (llvm::is_contained(PopUpRanges, Range))
1107 continue;
1108
1109 HighlightRange(R, LPosInfo.first, Range);
1110 }
1111}
1112
1114 unsigned x = n % ('z' - 'a');
1115 n /= 'z' - 'a';
1116
1117 if (n > 0)
1119
1120 os << char('a' + x);
1121}
1122
1123unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
1125 unsigned num) {
1126 for (const auto &subPiece : P.subPieces) {
1127 if (const auto *MP = dyn_cast(subPiece.get())) {
1128 num = ProcessMacroPiece(os, *MP, num);
1129 continue;
1130 }
1131
1132 if (const auto *EP = dyn_cast(subPiece.get())) {
1133 os << "<div class=\"msg msgEvent\" style=\"width:94%; "
1134 "margin-left:5px\">"
1135 "<table class=\"msgT\">
1136 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
1138 os << "<td valign=\"top\">"
1140 << "\n";
1141 }
1142 }
1143
1144 return num;
1145}
1146
1147void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,
1148 const ArrowMap &ArrowIndices) {
1149 std::string S;
1150 llvm::raw_string_ostream OS(S);
1151
1152 OS << R"<<<(
1153
1182
1183
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
1188
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
1193
1194
1195
1196)<<<";
1197
1198 for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
1199 OS << " <path class=\"arrow\" id=\"arrow" << Index << "\"/>\n";
1200 }
1201
1202 OS << R"<<<(
1203
1204
1205\n";
1209
1211 OS.str());
1212}
1213
1215 unsigned Index) {
1216 std::string Result;
1217 llvm::raw_string_ostream OS(Result);
1218 OS << "<span id=\"" << ClassName << Index << "\">";
1219 return Result;
1220}
1221
1224}
1225
1228}
1229
1230unsigned HTMLDiagnostics::ProcessControlFlowPiece(
1232 unsigned Number) {
1236
1237 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
1238 Start.c_str());
1239 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
1240 End.c_str());
1241 }
1242
1243 return Number;
1244}
1245
1246void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
1248 const char *HighlightStart,
1249 const char *HighlightEnd) {
1252
1254 unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);
1255
1257 unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);
1258
1259 if (EndLineNo < StartLineNo)
1260 return;
1261
1262 if (SM.getFileID(InstantiationStart) != BugFileID ||
1263 SM.getFileID(InstantiationEnd) != BugFileID)
1264 return;
1265
1266
1267 unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);
1268 unsigned OldEndColNo = EndColNo;
1269
1270 if (EndColNo) {
1271
1273 }
1274
1275
1276
1277
1280
1282}
1283
1284StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
1285 return R"<<<(
1286
1386 )<<<";
1387}
1388
1389StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
1390 return R"<<<(
1391
1641 )<<<";
1642}
Defines the clang::FileManager interface and associated types.
static bool shouldDisplayPopUpRange(const SourceRange &Range)
static void EmitAlphaCounter(raw_ostream &os, unsigned n)
static std::string getSpanBeginForControl(const char *ClassName, unsigned Index)
static llvm::SmallString< 32 > getIssueHash(const PathDiagnostic &D, const Preprocessor &PP)
static void HandlePopUpPieceStartTag(Rewriter &R, const std::vector< SourceRange > &PopUpRanges)
static std::string getSpanBeginForControlEnd(unsigned Index)
static void HandlePopUpPieceEndTag(Rewriter &R, const PathDiagnosticPopUpPiece &Piece, std::vector< SourceRange > &PopUpRanges, unsigned int LastReportedPieceIndex, unsigned int PopUpPieceIndex)
static std::string getSpanBeginForControlStart(unsigned Index)
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.