]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
MFV r331407: 9213 zfs: sytem typo
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Core / HTMLDiagnostics.cpp
1 //===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines the HTMLDiagnostics object.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/AST/ASTContext.h"
15 #include "clang/AST/Decl.h"
16 #include "clang/Basic/FileManager.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Lex/Lexer.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "clang/Rewrite/Core/HTMLRewrite.h"
21 #include "clang/Rewrite/Core/Rewriter.h"
22 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
23 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24 #include "clang/StaticAnalyzer/Core/IssueHash.h"
25 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
26 #include "llvm/Support/Errc.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/raw_ostream.h"
31 #include <sstream>
32
33 using namespace clang;
34 using namespace ento;
35
36 //===----------------------------------------------------------------------===//
37 // Boilerplate.
38 //===----------------------------------------------------------------------===//
39
40 namespace {
41
42 class HTMLDiagnostics : public PathDiagnosticConsumer {
43   std::string Directory;
44   bool createdDir, noDir;
45   const Preprocessor &PP;
46   AnalyzerOptions &AnalyzerOpts;
47   const bool SupportsCrossFileDiagnostics;
48 public:
49   HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
50                   const std::string& prefix,
51                   const Preprocessor &pp,
52                   bool supportsMultipleFiles);
53
54   ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
55
56   void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
57                             FilesMade *filesMade) override;
58
59   StringRef getName() const override {
60     return "HTMLDiagnostics";
61   }
62
63   bool supportsCrossFileDiagnostics() const override {
64     return SupportsCrossFileDiagnostics;
65   }
66
67   unsigned ProcessMacroPiece(raw_ostream &os,
68                              const PathDiagnosticMacroPiece& P,
69                              unsigned num);
70
71   void HandlePiece(Rewriter& R, FileID BugFileID,
72                    const PathDiagnosticPiece& P, unsigned num, unsigned max);
73
74   void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
75                       const char *HighlightStart = "<span class=\"mrange\">",
76                       const char *HighlightEnd = "</span>");
77
78   void ReportDiag(const PathDiagnostic& D,
79                   FilesMade *filesMade);
80
81   // Generate the full HTML report
82   std::string GenerateHTML(const PathDiagnostic& D, Rewriter &R,
83                            const SourceManager& SMgr, const PathPieces& path,
84                            const char *declName);
85
86   // Add HTML header/footers to file specified by FID
87   void FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
88                     const SourceManager& SMgr, const PathPieces& path,
89                     FileID FID, const FileEntry *Entry, const char *declName);
90
91   // Rewrite the file specified by FID with HTML formatting.
92   void RewriteFile(Rewriter &R, const SourceManager& SMgr,
93                    const PathPieces& path, FileID FID);
94
95   /// \return Javascript for navigating the HTML report using j/k keys.
96   std::string generateKeyboardNavigationJavascript();
97 };
98
99 } // end anonymous namespace
100
101 HTMLDiagnostics::HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
102                                  const std::string& prefix,
103                                  const Preprocessor &pp,
104                                  bool supportsMultipleFiles)
105     : Directory(prefix),
106       createdDir(false),
107       noDir(false),
108       PP(pp),
109       AnalyzerOpts(AnalyzerOpts),
110       SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
111
112 void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
113                                         PathDiagnosticConsumers &C,
114                                         const std::string& prefix,
115                                         const Preprocessor &PP) {
116   C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true));
117 }
118
119 void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
120                                                   PathDiagnosticConsumers &C,
121                                                   const std::string& prefix,
122                                                   const Preprocessor &PP) {
123   C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false));
124 }
125
126 //===----------------------------------------------------------------------===//
127 // Report processing.
128 //===----------------------------------------------------------------------===//
129
130 void HTMLDiagnostics::FlushDiagnosticsImpl(
131   std::vector<const PathDiagnostic *> &Diags,
132   FilesMade *filesMade) {
133   for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
134        et = Diags.end(); it != et; ++it) {
135     ReportDiag(**it, filesMade);
136   }
137 }
138
139 void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
140                                  FilesMade *filesMade) {
141
142   // Create the HTML directory if it is missing.
143   if (!createdDir) {
144     createdDir = true;
145     if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
146       llvm::errs() << "warning: could not create directory '"
147                    << Directory << "': " << ec.message() << '\n';
148
149       noDir = true;
150
151       return;
152     }
153   }
154
155   if (noDir)
156     return;
157
158   // First flatten out the entire path to make it easier to use.
159   PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);
160
161   // The path as already been prechecked that the path is non-empty.
162   assert(!path.empty());
163   const SourceManager &SMgr = path.front()->getLocation().getManager();
164
165   // Create a new rewriter to generate HTML.
166   Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
167
168   // The file for the first path element is considered the main report file, it
169   // will usually be equivalent to SMgr.getMainFileID(); however, it might be a
170   // header when -analyzer-opt-analyze-headers is used.
171   FileID ReportFile = path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
172
173   // Get the function/method name
174   SmallString<128> declName("unknown");
175   int offsetDecl = 0;
176   if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
177       if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue))
178           declName = ND->getDeclName().getAsString();
179
180       if (const Stmt *Body = DeclWithIssue->getBody()) {
181           // Retrieve the relative position of the declaration which will be used
182           // for the file name
183           FullSourceLoc L(
184               SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),
185               SMgr);
186           FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr);
187           offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
188       }
189   }
190
191   std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
192   if (report.empty()) {
193     llvm::errs() << "warning: no diagnostics generated for main file.\n";
194     return;
195   }
196
197   // Create a path for the target HTML file.
198   int FD;
199   SmallString<128> Model, ResultPath;
200
201   if (!AnalyzerOpts.shouldWriteStableReportFilename()) {
202       llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
203       if (std::error_code EC =
204           llvm::sys::fs::make_absolute(Model)) {
205           llvm::errs() << "warning: could not make '" << Model
206                        << "' absolute: " << EC.message() << '\n';
207         return;
208       }
209       if (std::error_code EC =
210           llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
211           llvm::errs() << "warning: could not create file in '" << Directory
212                        << "': " << EC.message() << '\n';
213           return;
214       }
215
216   } else {
217       int i = 1;
218       std::error_code EC;
219       do {
220           // Find a filename which is not already used
221           const FileEntry* Entry = SMgr.getFileEntryForID(ReportFile);
222           std::stringstream filename;
223           Model = "";
224           filename << "report-"
225                    << llvm::sys::path::filename(Entry->getName()).str()
226                    << "-" << declName.c_str()
227                    << "-" << offsetDecl
228                    << "-" << i << ".html";
229           llvm::sys::path::append(Model, Directory,
230                                   filename.str());
231           EC = llvm::sys::fs::openFileForWrite(Model,
232                                                FD,
233                                                llvm::sys::fs::F_RW |
234                                                llvm::sys::fs::F_Excl);
235           if (EC && EC != llvm::errc::file_exists) {
236               llvm::errs() << "warning: could not create file '" << Model
237                            << "': " << EC.message() << '\n';
238               return;
239           }
240           i++;
241       } while (EC);
242   }
243
244   llvm::raw_fd_ostream os(FD, true);
245
246   if (filesMade)
247     filesMade->addDiagnostic(D, getName(),
248                              llvm::sys::path::filename(ResultPath));
249
250   // Emit the HTML to disk.
251   os << report;
252 }
253
254 std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
255     const SourceManager& SMgr, const PathPieces& path, const char *declName) {
256
257   // Rewrite source files as HTML for every new file the path crosses
258   std::vector<FileID> FileIDs;
259   for (auto I : path) {
260     FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
261     if (std::find(FileIDs.begin(), FileIDs.end(), FID) != FileIDs.end())
262       continue;
263
264     FileIDs.push_back(FID);
265     RewriteFile(R, SMgr, path, FID);
266   }
267
268   if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
269     // Prefix file names, anchor tags, and nav cursors to every file
270     for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {
271       std::string s;
272       llvm::raw_string_ostream os(s);
273
274       if (I != FileIDs.begin())
275         os << "<hr class=divider>\n";
276
277       os << "<div id=File" << I->getHashValue() << ">\n";
278
279       // Left nav arrow
280       if (I != FileIDs.begin())
281         os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
282            << "\">&#x2190;</a></div>";
283
284       os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName()
285          << "</h4>\n";
286
287       // Right nav arrow
288       if (I + 1 != E)
289         os << "<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
290            << "\">&#x2192;</a></div>";
291
292       os << "</div>\n";
293
294       R.InsertTextBefore(SMgr.getLocForStartOfFile(*I), os.str());
295     }
296
297     // Append files to the main report file in the order they appear in the path
298     for (auto I : llvm::make_range(FileIDs.begin() + 1, FileIDs.end())) {
299       std::string s;
300       llvm::raw_string_ostream os(s);
301
302       const RewriteBuffer *Buf = R.getRewriteBufferFor(I);
303       for (auto BI : *Buf)
304         os << BI;
305
306       R.InsertTextAfter(SMgr.getLocForEndOfFile(FileIDs[0]), os.str());
307     }
308   }
309
310   const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]);
311   if (!Buf)
312     return "";
313
314   // Add CSS, header, and footer.
315   const FileEntry* Entry = SMgr.getFileEntryForID(FileIDs[0]);
316   FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName);
317
318   std::string file;
319   llvm::raw_string_ostream os(file);
320   for (auto BI : *Buf)
321     os << BI;
322
323   return os.str();
324 }
325
326 void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
327     const SourceManager& SMgr, const PathPieces& path, FileID FID,
328     const FileEntry *Entry, const char *declName) {
329   // This is a cludge; basically we want to append either the full
330   // working directory if we have no directory information.  This is
331   // a work in progress.
332
333   llvm::SmallString<0> DirName;
334
335   if (llvm::sys::path::is_relative(Entry->getName())) {
336     llvm::sys::fs::current_path(DirName);
337     DirName += '/';
338   }
339
340   int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
341   int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
342
343   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
344                      generateKeyboardNavigationJavascript());
345
346   // Add the name of the file as an <h1> tag.
347   {
348     std::string s;
349     llvm::raw_string_ostream os(s);
350
351     os << "<!-- REPORTHEADER -->\n"
352        << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
353           "<tr><td class=\"rowname\">File:</td><td>"
354        << html::EscapeText(DirName)
355        << html::EscapeText(Entry->getName())
356        << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
357           "<a href=\"#EndPath\">line "
358        << LineNumber
359        << ", column "
360        << ColumnNumber
361        << "</a><br />"
362        << D.getVerboseDescription() << "</td></tr>\n";
363
364     // The navigation across the extra notes pieces.
365     unsigned NumExtraPieces = 0;
366     for (const auto &Piece : path) {
367       if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
368         int LineNumber =
369             P->getLocation().asLocation().getExpansionLineNumber();
370         int ColumnNumber =
371             P->getLocation().asLocation().getExpansionColumnNumber();
372         os << "<tr><td class=\"rowname\">Note:</td><td>"
373            << "<a href=\"#Note" << NumExtraPieces << "\">line "
374            << LineNumber << ", column " << ColumnNumber << "</a><br />"
375            << P->getString() << "</td></tr>";
376         ++NumExtraPieces;
377       }
378     }
379
380     // Output any other meta data.
381
382     for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end();
383          I!=E; ++I) {
384       os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
385     }
386
387     os << R"<<<(
388 </table>
389 <!-- REPORTSUMMARYEXTRA -->
390 <h3>Annotated Source Code</h3>
391 <p><span class='macro'>[?]
392   <span class='expansion'>Use j/k keys for keyboard navigation</span>
393 </span></p>
394 )<<<";
395
396     R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
397   }
398
399   // Embed meta-data tags.
400   {
401     std::string s;
402     llvm::raw_string_ostream os(s);
403
404     StringRef BugDesc = D.getVerboseDescription();
405     if (!BugDesc.empty())
406       os << "\n<!-- BUGDESC " << BugDesc << " -->\n";
407
408     StringRef BugType = D.getBugType();
409     if (!BugType.empty())
410       os << "\n<!-- BUGTYPE " << BugType << " -->\n";
411
412     PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
413     FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
414                                              ? UPDLoc.asLocation()
415                                              : D.getLocation().asLocation()),
416                     SMgr);
417     const Decl *DeclWithIssue = D.getDeclWithIssue();
418
419     StringRef BugCategory = D.getCategory();
420     if (!BugCategory.empty())
421       os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";
422
423     os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
424
425     os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n";
426
427     os  << "\n<!-- FUNCTIONNAME " <<  declName << " -->\n";
428
429     os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
430        << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue,
431                        PP.getLangOpts()) << " -->\n";
432
433     os << "\n<!-- BUGLINE "
434        << LineNumber
435        << " -->\n";
436
437     os << "\n<!-- BUGCOLUMN "
438       << ColumnNumber
439       << " -->\n";
440
441     os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";
442
443     // Mark the end of the tags.
444     os << "\n<!-- BUGMETAEND -->\n";
445
446     // Insert the text.
447     R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
448   }
449
450   html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
451 }
452
453 void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr,
454     const PathPieces& path, FileID FID) {
455   // Process the path.
456   // Maintain the counts of extra note pieces separately.
457   unsigned TotalPieces = path.size();
458   unsigned TotalNotePieces =
459       std::count_if(path.begin(), path.end(),
460                     [](const std::shared_ptr<PathDiagnosticPiece> &p) {
461                       return isa<PathDiagnosticNotePiece>(*p);
462                     });
463
464   unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
465   unsigned NumRegularPieces = TotalRegularPieces;
466   unsigned NumNotePieces = TotalNotePieces;
467
468   for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
469     if (isa<PathDiagnosticNotePiece>(I->get())) {
470       // This adds diagnostic bubbles, but not navigation.
471       // Navigation through note pieces would be added later,
472       // as a separate pass through the piece list.
473       HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces);
474       --NumNotePieces;
475     } else {
476       HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces);
477       --NumRegularPieces;
478     }
479   }
480
481   // Add line numbers, header, footer, etc.
482
483   html::EscapeText(R, FID);
484   html::AddLineNumbers(R, FID);
485
486   // If we have a preprocessor, relex the file and syntax highlight.
487   // We might not have a preprocessor if we come from a deserialized AST file,
488   // for example.
489
490   html::SyntaxHighlight(R, FID, PP);
491   html::HighlightMacros(R, FID, PP);
492 }
493
494 void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
495                                   const PathDiagnosticPiece& P,
496                                   unsigned num, unsigned max) {
497
498   // For now, just draw a box above the line in question, and emit the
499   // warning.
500   FullSourceLoc Pos = P.getLocation().asLocation();
501
502   if (!Pos.isValid())
503     return;
504
505   SourceManager &SM = R.getSourceMgr();
506   assert(&Pos.getManager() == &SM && "SourceManagers are different!");
507   std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);
508
509   if (LPosInfo.first != BugFileID)
510     return;
511
512   const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first);
513   const char* FileStart = Buf->getBufferStart();
514
515   // Compute the column number.  Rewind from the current position to the start
516   // of the line.
517   unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
518   const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData();
519   const char *LineStart = TokInstantiationPtr-ColNo;
520
521   // Compute LineEnd.
522   const char *LineEnd = TokInstantiationPtr;
523   const char* FileEnd = Buf->getBufferEnd();
524   while (*LineEnd != '\n' && LineEnd != FileEnd)
525     ++LineEnd;
526
527   // Compute the margin offset by counting tabs and non-tabs.
528   unsigned PosNo = 0;
529   for (const char* c = LineStart; c != TokInstantiationPtr; ++c)
530     PosNo += *c == '\t' ? 8 : 1;
531
532   // Create the html for the message.
533
534   const char *Kind = nullptr;
535   bool IsNote = false;
536   bool SuppressIndex = (max == 1);
537   switch (P.getKind()) {
538   case PathDiagnosticPiece::Call:
539       llvm_unreachable("Calls and extra notes should already be handled");
540   case PathDiagnosticPiece::Event:  Kind = "Event"; break;
541   case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
542     // Setting Kind to "Control" is intentional.
543   case PathDiagnosticPiece::Macro: Kind = "Control"; break;
544   case PathDiagnosticPiece::Note:
545     Kind = "Note";
546     IsNote = true;
547     SuppressIndex = true;
548     break;
549   }
550
551   std::string sbuf;
552   llvm::raw_string_ostream os(sbuf);
553
554   os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
555
556   if (IsNote)
557     os << "Note" << num;
558   else if (num == max)
559     os << "EndPath";
560   else
561     os << "Path" << num;
562
563   os << "\" class=\"msg";
564   if (Kind)
565     os << " msg" << Kind;
566   os << "\" style=\"margin-left:" << PosNo << "ex";
567
568   // Output a maximum size.
569   if (!isa<PathDiagnosticMacroPiece>(P)) {
570     // Get the string and determining its maximum substring.
571     const auto &Msg = P.getString();
572     unsigned max_token = 0;
573     unsigned cnt = 0;
574     unsigned len = Msg.size();
575
576     for (char C : Msg)
577       switch (C) {
578       default:
579         ++cnt;
580         continue;
581       case ' ':
582       case '\t':
583       case '\n':
584         if (cnt > max_token) max_token = cnt;
585         cnt = 0;
586       }
587
588     if (cnt > max_token)
589       max_token = cnt;
590
591     // Determine the approximate size of the message bubble in em.
592     unsigned em;
593     const unsigned max_line = 120;
594
595     if (max_token >= max_line)
596       em = max_token / 2;
597     else {
598       unsigned characters = max_line;
599       unsigned lines = len / max_line;
600
601       if (lines > 0) {
602         for (; characters > max_token; --characters)
603           if (len / characters > lines) {
604             ++characters;
605             break;
606           }
607       }
608
609       em = characters / 2;
610     }
611
612     if (em < max_line/2)
613       os << "; max-width:" << em << "em";
614   }
615   else
616     os << "; max-width:100em";
617
618   os << "\">";
619
620   if (!SuppressIndex) {
621     os << "<table class=\"msgT\"><tr><td valign=\"top\">";
622     os << "<div class=\"PathIndex";
623     if (Kind) os << " PathIndex" << Kind;
624     os << "\">" << num << "</div>";
625
626     if (num > 1) {
627       os << "</td><td><div class=\"PathNav\"><a href=\"#Path"
628          << (num - 1)
629          << "\" title=\"Previous event ("
630          << (num - 1)
631          << ")\">&#x2190;</a></div></td>";
632     }
633
634     os << "</td><td>";
635   }
636
637   if (const PathDiagnosticMacroPiece *MP =
638         dyn_cast<PathDiagnosticMacroPiece>(&P)) {
639
640     os << "Within the expansion of the macro '";
641
642     // Get the name of the macro by relexing it.
643     {
644       FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
645       assert(L.isFileID());
646       StringRef BufferInfo = L.getBufferData();
647       std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc();
648       const char* MacroName = LocInfo.second + BufferInfo.data();
649       Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
650                      BufferInfo.begin(), MacroName, BufferInfo.end());
651
652       Token TheTok;
653       rawLexer.LexFromRawLexer(TheTok);
654       for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
655         os << MacroName[i];
656     }
657
658     os << "':\n";
659
660     if (!SuppressIndex) {
661       os << "</td>";
662       if (num < max) {
663         os << "<td><div class=\"PathNav\"><a href=\"#";
664         if (num == max - 1)
665           os << "EndPath";
666         else
667           os << "Path" << (num + 1);
668         os << "\" title=\"Next event ("
669         << (num + 1)
670         << ")\">&#x2192;</a></div></td>";
671       }
672
673       os << "</tr></table>";
674     }
675
676     // Within a macro piece.  Write out each event.
677     ProcessMacroPiece(os, *MP, 0);
678   }
679   else {
680     os << html::EscapeText(P.getString());
681
682     if (!SuppressIndex) {
683       os << "</td>";
684       if (num < max) {
685         os << "<td><div class=\"PathNav\"><a href=\"#";
686         if (num == max - 1)
687           os << "EndPath";
688         else
689           os << "Path" << (num + 1);
690         os << "\" title=\"Next event ("
691            << (num + 1)
692            << ")\">&#x2192;</a></div></td>";
693       }
694
695       os << "</tr></table>";
696     }
697   }
698
699   os << "</div></td></tr>";
700
701   // Insert the new html.
702   unsigned DisplayPos = LineEnd - FileStart;
703   SourceLocation Loc =
704     SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
705
706   R.InsertTextBefore(Loc, os.str());
707
708   // Now highlight the ranges.
709   ArrayRef<SourceRange> Ranges = P.getRanges();
710   for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
711                                        E = Ranges.end(); I != E; ++I) {
712     HighlightRange(R, LPosInfo.first, *I);
713   }
714 }
715
716 static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
717   unsigned x = n % ('z' - 'a');
718   n /= 'z' - 'a';
719
720   if (n > 0)
721     EmitAlphaCounter(os, n);
722
723   os << char('a' + x);
724 }
725
726 unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
727                                             const PathDiagnosticMacroPiece& P,
728                                             unsigned num) {
729
730   for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
731         I!=E; ++I) {
732
733     if (const PathDiagnosticMacroPiece *MP =
734             dyn_cast<PathDiagnosticMacroPiece>(I->get())) {
735       num = ProcessMacroPiece(os, *MP, num);
736       continue;
737     }
738
739     if (PathDiagnosticEventPiece *EP =
740             dyn_cast<PathDiagnosticEventPiece>(I->get())) {
741       os << "<div class=\"msg msgEvent\" style=\"width:94%; "
742             "margin-left:5px\">"
743             "<table class=\"msgT\"><tr>"
744             "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
745       EmitAlphaCounter(os, num++);
746       os << "</div></td><td valign=\"top\">"
747          << html::EscapeText(EP->getString())
748          << "</td></tr></table></div>\n";
749     }
750   }
751
752   return num;
753 }
754
755 void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
756                                      SourceRange Range,
757                                      const char *HighlightStart,
758                                      const char *HighlightEnd) {
759   SourceManager &SM = R.getSourceMgr();
760   const LangOptions &LangOpts = R.getLangOpts();
761
762   SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin());
763   unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);
764
765   SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd());
766   unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);
767
768   if (EndLineNo < StartLineNo)
769     return;
770
771   if (SM.getFileID(InstantiationStart) != BugFileID ||
772       SM.getFileID(InstantiationEnd) != BugFileID)
773     return;
774
775   // Compute the column number of the end.
776   unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);
777   unsigned OldEndColNo = EndColNo;
778
779   if (EndColNo) {
780     // Add in the length of the token, so that we cover multi-char tokens.
781     EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1;
782   }
783
784   // Highlight the range.  Make the span tag the outermost tag for the
785   // selected range.
786
787   SourceLocation E =
788     InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo);
789
790   html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
791 }
792
793 std::string HTMLDiagnostics::generateKeyboardNavigationJavascript() {
794   return R"<<<(
795 <script type='text/javascript'>
796 var digitMatcher = new RegExp("[0-9]+");
797
798 document.addEventListener("DOMContentLoaded", function() {
799     document.querySelectorAll(".PathNav > a").forEach(
800         function(currentValue, currentIndex) {
801             var hrefValue = currentValue.getAttribute("href");
802             currentValue.onclick = function() {
803                 scrollTo(document.querySelector(hrefValue));
804                 return false;
805             };
806         });
807 });
808
809 var findNum = function() {
810     var s = document.querySelector(".selected");
811     if (!s || s.id == "EndPath") {
812         return 0;
813     }
814     var out = parseInt(digitMatcher.exec(s.id)[0]);
815     return out;
816 };
817
818 var scrollTo = function(el) {
819     document.querySelectorAll(".selected").forEach(function(s) {
820         s.classList.remove("selected");
821     });
822     el.classList.add("selected");
823     window.scrollBy(0, el.getBoundingClientRect().top -
824         (window.innerHeight / 2));
825 }
826
827 var move = function(num, up, numItems) {
828   if (num == 1 && up || num == numItems - 1 && !up) {
829     return 0;
830   } else if (num == 0 && up) {
831     return numItems - 1;
832   } else if (num == 0 && !up) {
833     return 1 % numItems;
834   }
835   return up ? num - 1 : num + 1;
836 }
837
838 var numToId = function(num) {
839   if (num == 0) {
840     return document.getElementById("EndPath")
841   }
842   return document.getElementById("Path" + num);
843 };
844
845 var navigateTo = function(up) {
846   var numItems = document.querySelectorAll(".line > .msg").length;
847   var currentSelected = findNum();
848   var newSelected = move(currentSelected, up, numItems);
849   var newEl = numToId(newSelected, numItems);
850
851   // Scroll element into center.
852   scrollTo(newEl);
853 };
854
855 window.addEventListener("keydown", function (event) {
856   if (event.defaultPrevented) {
857     return;
858   }
859   if (event.key == "j") {
860     navigateTo(/*up=*/false);
861   } else if (event.key == "k") {
862     navigateTo(/*up=*/true);
863   } else {
864     return;
865   } 
866   event.preventDefault();
867 }, true);
868 </script>
869   )<<<";
870 }