19c4b2225SAlexander Belyaev //===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===//
29c4b2225SAlexander Belyaev //
39c4b2225SAlexander Belyaev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49c4b2225SAlexander Belyaev // See https://llvm.org/LICENSE.txt for license information.
59c4b2225SAlexander Belyaev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69c4b2225SAlexander Belyaev //
79c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
89c4b2225SAlexander Belyaev //
99c4b2225SAlexander Belyaev //  This file defines the HTMLDiagnostics object.
109c4b2225SAlexander Belyaev //
119c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
129c4b2225SAlexander Belyaev 
139c4b2225SAlexander Belyaev #include "clang/AST/Decl.h"
149c4b2225SAlexander Belyaev #include "clang/AST/DeclBase.h"
159c4b2225SAlexander Belyaev #include "clang/AST/Stmt.h"
167c58fb6bSBalazs Benics #include "clang/Analysis/IssueHash.h"
177c58fb6bSBalazs Benics #include "clang/Analysis/MacroExpansionContext.h"
187c58fb6bSBalazs Benics #include "clang/Analysis/PathDiagnostic.h"
199c4b2225SAlexander Belyaev #include "clang/Basic/FileManager.h"
209c4b2225SAlexander Belyaev #include "clang/Basic/LLVM.h"
219c4b2225SAlexander Belyaev #include "clang/Basic/SourceLocation.h"
229c4b2225SAlexander Belyaev #include "clang/Basic/SourceManager.h"
239c4b2225SAlexander Belyaev #include "clang/Lex/Lexer.h"
249c4b2225SAlexander Belyaev #include "clang/Lex/Preprocessor.h"
259c4b2225SAlexander Belyaev #include "clang/Lex/Token.h"
269c4b2225SAlexander Belyaev #include "clang/Rewrite/Core/HTMLRewrite.h"
279c4b2225SAlexander Belyaev #include "clang/Rewrite/Core/Rewriter.h"
289c4b2225SAlexander Belyaev #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
299c4b2225SAlexander Belyaev #include "llvm/ADT/ArrayRef.h"
309e02f587SValeriy Savchenko #include "llvm/ADT/STLExtras.h"
3197bcafa2SValeriy Savchenko #include "llvm/ADT/Sequence.h"
329c4b2225SAlexander Belyaev #include "llvm/ADT/SmallString.h"
339c4b2225SAlexander Belyaev #include "llvm/ADT/StringRef.h"
349c4b2225SAlexander Belyaev #include "llvm/ADT/iterator_range.h"
359c4b2225SAlexander Belyaev #include "llvm/Support/Casting.h"
369c4b2225SAlexander Belyaev #include "llvm/Support/Errc.h"
379c4b2225SAlexander Belyaev #include "llvm/Support/ErrorHandling.h"
389c4b2225SAlexander Belyaev #include "llvm/Support/FileSystem.h"
399c4b2225SAlexander Belyaev #include "llvm/Support/MemoryBuffer.h"
409c4b2225SAlexander Belyaev #include "llvm/Support/Path.h"
419c4b2225SAlexander Belyaev #include "llvm/Support/raw_ostream.h"
429c4b2225SAlexander Belyaev #include <algorithm>
439c4b2225SAlexander Belyaev #include <cassert>
449c4b2225SAlexander Belyaev #include <map>
459c4b2225SAlexander Belyaev #include <memory>
469c4b2225SAlexander Belyaev #include <set>
479c4b2225SAlexander Belyaev #include <sstream>
489c4b2225SAlexander Belyaev #include <string>
499c4b2225SAlexander Belyaev #include <system_error>
509c4b2225SAlexander Belyaev #include <utility>
519c4b2225SAlexander Belyaev #include <vector>
529c4b2225SAlexander Belyaev 
539c4b2225SAlexander Belyaev using namespace clang;
549c4b2225SAlexander Belyaev using namespace ento;
559c4b2225SAlexander Belyaev 
569c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
579c4b2225SAlexander Belyaev // Boilerplate.
589c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
599c4b2225SAlexander Belyaev 
609c4b2225SAlexander Belyaev namespace {
619c4b2225SAlexander Belyaev 
629e02f587SValeriy Savchenko class ArrowMap;
639e02f587SValeriy Savchenko 
649c4b2225SAlexander Belyaev class HTMLDiagnostics : public PathDiagnosticConsumer {
659c4b2225SAlexander Belyaev   PathDiagnosticConsumerOptions DiagOpts;
669c4b2225SAlexander Belyaev   std::string Directory;
679c4b2225SAlexander Belyaev   bool createdDir = false;
689c4b2225SAlexander Belyaev   bool noDir = false;
699c4b2225SAlexander Belyaev   const Preprocessor &PP;
709c4b2225SAlexander Belyaev   const bool SupportsCrossFileDiagnostics;
719c4b2225SAlexander Belyaev 
729c4b2225SAlexander Belyaev public:
HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,const std::string & OutputDir,const Preprocessor & pp,bool supportsMultipleFiles)739c4b2225SAlexander Belyaev   HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
749c4b2225SAlexander Belyaev                   const std::string &OutputDir, const Preprocessor &pp,
759c4b2225SAlexander Belyaev                   bool supportsMultipleFiles)
769c4b2225SAlexander Belyaev       : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp),
779c4b2225SAlexander Belyaev         SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
789c4b2225SAlexander Belyaev 
~HTMLDiagnostics()799c4b2225SAlexander Belyaev   ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
809c4b2225SAlexander Belyaev 
819c4b2225SAlexander Belyaev   void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
829c4b2225SAlexander Belyaev                             FilesMade *filesMade) override;
839c4b2225SAlexander Belyaev 
getName() const8497bcafa2SValeriy Savchenko   StringRef getName() const override { return "HTMLDiagnostics"; }
859c4b2225SAlexander Belyaev 
supportsCrossFileDiagnostics() const869c4b2225SAlexander Belyaev   bool supportsCrossFileDiagnostics() const override {
879c4b2225SAlexander Belyaev     return SupportsCrossFileDiagnostics;
889c4b2225SAlexander Belyaev   }
899c4b2225SAlexander Belyaev 
9097bcafa2SValeriy Savchenko   unsigned ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece &P,
919c4b2225SAlexander Belyaev                              unsigned num);
929c4b2225SAlexander Belyaev 
9397bcafa2SValeriy Savchenko   unsigned ProcessControlFlowPiece(Rewriter &R, FileID BugFileID,
9497bcafa2SValeriy Savchenko                                    const PathDiagnosticControlFlowPiece &P,
9597bcafa2SValeriy Savchenko                                    unsigned Number);
9697bcafa2SValeriy Savchenko 
979c4b2225SAlexander Belyaev   void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P,
989c4b2225SAlexander Belyaev                    const std::vector<SourceRange> &PopUpRanges, unsigned num,
999c4b2225SAlexander Belyaev                    unsigned max);
1009c4b2225SAlexander Belyaev 
1019c4b2225SAlexander Belyaev   void HighlightRange(Rewriter &R, FileID BugFileID, SourceRange Range,
1029c4b2225SAlexander Belyaev                       const char *HighlightStart = "<span class=\"mrange\">",
1039c4b2225SAlexander Belyaev                       const char *HighlightEnd = "</span>");
1049c4b2225SAlexander Belyaev 
10597bcafa2SValeriy Savchenko   void ReportDiag(const PathDiagnostic &D, FilesMade *filesMade);
1069c4b2225SAlexander Belyaev 
1079c4b2225SAlexander Belyaev   // Generate the full HTML report
1089c4b2225SAlexander Belyaev   std::string GenerateHTML(const PathDiagnostic &D, Rewriter &R,
1099c4b2225SAlexander Belyaev                            const SourceManager &SMgr, const PathPieces &path,
1109c4b2225SAlexander Belyaev                            const char *declName);
1119c4b2225SAlexander Belyaev 
1129c4b2225SAlexander Belyaev   // Add HTML header/footers to file specified by FID
1139c4b2225SAlexander Belyaev   void FinalizeHTML(const PathDiagnostic &D, Rewriter &R,
1149c4b2225SAlexander Belyaev                     const SourceManager &SMgr, const PathPieces &path,
1159c4b2225SAlexander Belyaev                     FileID FID, const FileEntry *Entry, const char *declName);
1169c4b2225SAlexander Belyaev 
1179c4b2225SAlexander Belyaev   // Rewrite the file specified by FID with HTML formatting.
1189c4b2225SAlexander Belyaev   void RewriteFile(Rewriter &R, const PathPieces &path, FileID FID);
1199c4b2225SAlexander Belyaev 
getGenerationScheme() const12097bcafa2SValeriy Savchenko   PathGenerationScheme getGenerationScheme() const override {
12197bcafa2SValeriy Savchenko     return Everything;
12297bcafa2SValeriy Savchenko   }
1239c4b2225SAlexander Belyaev 
1249c4b2225SAlexander Belyaev private:
1259e02f587SValeriy Savchenko   void addArrowSVGs(Rewriter &R, FileID BugFileID,
1269e02f587SValeriy Savchenko                     const ArrowMap &ArrowIndices);
12797bcafa2SValeriy Savchenko 
1289c4b2225SAlexander Belyaev   /// \return Javascript for displaying shortcuts help;
1299c4b2225SAlexander Belyaev   StringRef showHelpJavascript();
1309c4b2225SAlexander Belyaev 
1319c4b2225SAlexander Belyaev   /// \return Javascript for navigating the HTML report using j/k keys.
1329c4b2225SAlexander Belyaev   StringRef generateKeyboardNavigationJavascript();
1339c4b2225SAlexander Belyaev 
13497bcafa2SValeriy Savchenko   /// \return Javascript for drawing control-flow arrows.
13597bcafa2SValeriy Savchenko   StringRef generateArrowDrawingJavascript();
13697bcafa2SValeriy Savchenko 
1379c4b2225SAlexander Belyaev   /// \return JavaScript for an option to only show relevant lines.
13897bcafa2SValeriy Savchenko   std::string showRelevantLinesJavascript(const PathDiagnostic &D,
13997bcafa2SValeriy Savchenko                                           const PathPieces &path);
1409c4b2225SAlexander Belyaev 
1419c4b2225SAlexander Belyaev   /// Write executed lines from \p D in JSON format into \p os.
14297bcafa2SValeriy Savchenko   void dumpCoverageData(const PathDiagnostic &D, const PathPieces &path,
1439c4b2225SAlexander Belyaev                         llvm::raw_string_ostream &os);
1449c4b2225SAlexander Belyaev };
1459c4b2225SAlexander Belyaev 
isArrowPiece(const PathDiagnosticPiece & P)14697bcafa2SValeriy Savchenko bool isArrowPiece(const PathDiagnosticPiece &P) {
14797bcafa2SValeriy Savchenko   return isa<PathDiagnosticControlFlowPiece>(P) && P.getString().empty();
14897bcafa2SValeriy Savchenko }
14997bcafa2SValeriy Savchenko 
getPathSizeWithoutArrows(const PathPieces & Path)15097bcafa2SValeriy Savchenko unsigned getPathSizeWithoutArrows(const PathPieces &Path) {
15197bcafa2SValeriy Savchenko   unsigned TotalPieces = Path.size();
15297bcafa2SValeriy Savchenko   unsigned TotalArrowPieces = llvm::count_if(
15397bcafa2SValeriy Savchenko       Path, [](const PathDiagnosticPieceRef &P) { return isArrowPiece(*P); });
15497bcafa2SValeriy Savchenko   return TotalPieces - TotalArrowPieces;
15597bcafa2SValeriy Savchenko }
15697bcafa2SValeriy Savchenko 
1579e02f587SValeriy Savchenko class ArrowMap : public std::vector<unsigned> {
1589e02f587SValeriy Savchenko   using Base = std::vector<unsigned>;
1599e02f587SValeriy Savchenko 
1609e02f587SValeriy Savchenko public:
ArrowMap(unsigned Size)1619e02f587SValeriy Savchenko   ArrowMap(unsigned Size) : Base(Size, 0) {}
getTotalNumberOfArrows() const1629e02f587SValeriy Savchenko   unsigned getTotalNumberOfArrows() const { return at(0); }
1639e02f587SValeriy Savchenko };
1649e02f587SValeriy Savchenko 
operator <<(llvm::raw_ostream & OS,const ArrowMap & Indices)1659e02f587SValeriy Savchenko llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) {
1669e02f587SValeriy Savchenko   OS << "[ ";
1679e02f587SValeriy Savchenko   llvm::interleave(Indices, OS, ",");
1689e02f587SValeriy Savchenko   return OS << " ]";
1699e02f587SValeriy Savchenko }
1709e02f587SValeriy Savchenko 
1719c4b2225SAlexander Belyaev } // namespace
1729c4b2225SAlexander Belyaev 
createHTMLDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & OutputDir,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)1739c4b2225SAlexander Belyaev void ento::createHTMLDiagnosticConsumer(
1749c4b2225SAlexander Belyaev     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1759c4b2225SAlexander Belyaev     const std::string &OutputDir, const Preprocessor &PP,
1767c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
1777c58fb6bSBalazs Benics     const MacroExpansionContext &MacroExpansions) {
1789c4b2225SAlexander Belyaev 
1799c4b2225SAlexander Belyaev   // FIXME: HTML is currently our default output type, but if the output
1809c4b2225SAlexander Belyaev   // directory isn't specified, it acts like if it was in the minimal text
1819c4b2225SAlexander Belyaev   // output mode. This doesn't make much sense, we should have the minimal text
1829c4b2225SAlexander Belyaev   // as our default. In the case of backward compatibility concerns, this could
1839c4b2225SAlexander Belyaev   // be preserved with -analyzer-config-compatibility-mode=true.
1847c58fb6bSBalazs Benics   createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,
1857c58fb6bSBalazs Benics                                           MacroExpansions);
1869c4b2225SAlexander Belyaev 
1879c4b2225SAlexander Belyaev   // TODO: Emit an error here.
1889c4b2225SAlexander Belyaev   if (OutputDir.empty())
1899c4b2225SAlexander Belyaev     return;
1909c4b2225SAlexander Belyaev 
1919c4b2225SAlexander Belyaev   C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true));
1929c4b2225SAlexander Belyaev }
1939c4b2225SAlexander Belyaev 
createHTMLSingleFileDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & OutputDir,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const clang::MacroExpansionContext & MacroExpansions)1949c4b2225SAlexander Belyaev void ento::createHTMLSingleFileDiagnosticConsumer(
1959c4b2225SAlexander Belyaev     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1969c4b2225SAlexander Belyaev     const std::string &OutputDir, const Preprocessor &PP,
1977c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
1987c58fb6bSBalazs Benics     const clang::MacroExpansionContext &MacroExpansions) {
1997c58fb6bSBalazs Benics   createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,
2007c58fb6bSBalazs Benics                                           MacroExpansions);
2019c4b2225SAlexander Belyaev 
2029c4b2225SAlexander Belyaev   // TODO: Emit an error here.
2039c4b2225SAlexander Belyaev   if (OutputDir.empty())
2049c4b2225SAlexander Belyaev     return;
2059c4b2225SAlexander Belyaev 
2069c4b2225SAlexander Belyaev   C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false));
2079c4b2225SAlexander Belyaev }
2089c4b2225SAlexander Belyaev 
createPlistHTMLDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & prefix,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)2099c4b2225SAlexander Belyaev void ento::createPlistHTMLDiagnosticConsumer(
2109c4b2225SAlexander Belyaev     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
2119c4b2225SAlexander Belyaev     const std::string &prefix, const Preprocessor &PP,
2127c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
2137c58fb6bSBalazs Benics     const MacroExpansionContext &MacroExpansions) {
2149c4b2225SAlexander Belyaev   createHTMLDiagnosticConsumer(
2157c58fb6bSBalazs Benics       DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,
2167c58fb6bSBalazs Benics       MacroExpansions);
2177c58fb6bSBalazs Benics   createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU,
2187c58fb6bSBalazs Benics                                          MacroExpansions);
2199c4b2225SAlexander Belyaev   createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP,
2207c58fb6bSBalazs Benics                                           CTU, MacroExpansions);
2219c4b2225SAlexander Belyaev }
2229c4b2225SAlexander Belyaev 
createSarifHTMLDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & sarif_file,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)2232407eb08SDaniel Hwang void ento::createSarifHTMLDiagnosticConsumer(
2242407eb08SDaniel Hwang     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
2252407eb08SDaniel Hwang     const std::string &sarif_file, const Preprocessor &PP,
2267c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
2277c58fb6bSBalazs Benics     const MacroExpansionContext &MacroExpansions) {
2287c58fb6bSBalazs Benics   createHTMLDiagnosticConsumer(
2297c58fb6bSBalazs Benics       DiagOpts, C, std::string(llvm::sys::path::parent_path(sarif_file)), PP,
2307c58fb6bSBalazs Benics       CTU, MacroExpansions);
2317c58fb6bSBalazs Benics   createSarifDiagnosticConsumer(DiagOpts, C, sarif_file, PP, CTU,
2327c58fb6bSBalazs Benics                                 MacroExpansions);
2337c58fb6bSBalazs Benics   createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, sarif_file,
2347c58fb6bSBalazs Benics                                           PP, CTU, MacroExpansions);
2352407eb08SDaniel Hwang }
2362407eb08SDaniel Hwang 
2379c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
2389c4b2225SAlexander Belyaev // Report processing.
2399c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
2409c4b2225SAlexander Belyaev 
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)2419c4b2225SAlexander Belyaev void HTMLDiagnostics::FlushDiagnosticsImpl(
2429c4b2225SAlexander Belyaev   std::vector<const PathDiagnostic *> &Diags,
2439c4b2225SAlexander Belyaev   FilesMade *filesMade) {
2449c4b2225SAlexander Belyaev   for (const auto Diag : Diags)
2459c4b2225SAlexander Belyaev     ReportDiag(*Diag, filesMade);
2469c4b2225SAlexander Belyaev }
2479c4b2225SAlexander Belyaev 
getIssueHash(const PathDiagnostic & D,const Preprocessor & PP)24873093599SArtem Dergachev static llvm::SmallString<32> getIssueHash(const PathDiagnostic &D,
24973093599SArtem Dergachev                                           const Preprocessor &PP) {
25073093599SArtem Dergachev   SourceManager &SMgr = PP.getSourceManager();
25173093599SArtem Dergachev   PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
25273093599SArtem Dergachev   FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
25373093599SArtem Dergachev                                            ? UPDLoc.asLocation()
25473093599SArtem Dergachev                                            : D.getLocation().asLocation()),
25573093599SArtem Dergachev                   SMgr);
25673093599SArtem Dergachev   return getIssueHash(L, D.getCheckerName(), D.getBugType(),
25773093599SArtem Dergachev                       D.getDeclWithIssue(), PP.getLangOpts());
25873093599SArtem Dergachev }
25973093599SArtem Dergachev 
ReportDiag(const PathDiagnostic & D,FilesMade * filesMade)2609c4b2225SAlexander Belyaev void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
2619c4b2225SAlexander Belyaev                                  FilesMade *filesMade) {
2629c4b2225SAlexander Belyaev   // Create the HTML directory if it is missing.
2639c4b2225SAlexander Belyaev   if (!createdDir) {
2649c4b2225SAlexander Belyaev     createdDir = true;
2659c4b2225SAlexander Belyaev     if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
2669c4b2225SAlexander Belyaev       llvm::errs() << "warning: could not create directory '"
2679c4b2225SAlexander Belyaev                    << Directory << "': " << ec.message() << '\n';
2689c4b2225SAlexander Belyaev       noDir = true;
2699c4b2225SAlexander Belyaev       return;
2709c4b2225SAlexander Belyaev     }
2719c4b2225SAlexander Belyaev   }
2729c4b2225SAlexander Belyaev 
2739c4b2225SAlexander Belyaev   if (noDir)
2749c4b2225SAlexander Belyaev     return;
2759c4b2225SAlexander Belyaev 
2769c4b2225SAlexander Belyaev   // First flatten out the entire path to make it easier to use.
2779c4b2225SAlexander Belyaev   PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);
2789c4b2225SAlexander Belyaev 
2799c4b2225SAlexander Belyaev   // The path as already been prechecked that the path is non-empty.
2809c4b2225SAlexander Belyaev   assert(!path.empty());
2819c4b2225SAlexander Belyaev   const SourceManager &SMgr = path.front()->getLocation().getManager();
2829c4b2225SAlexander Belyaev 
2839c4b2225SAlexander Belyaev   // Create a new rewriter to generate HTML.
2849c4b2225SAlexander Belyaev   Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
2859c4b2225SAlexander Belyaev 
2869c4b2225SAlexander Belyaev   // Get the function/method name
2879c4b2225SAlexander Belyaev   SmallString<128> declName("unknown");
2889c4b2225SAlexander Belyaev   int offsetDecl = 0;
2899c4b2225SAlexander Belyaev   if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
2909c4b2225SAlexander Belyaev       if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
2919c4b2225SAlexander Belyaev           declName = ND->getDeclName().getAsString();
2929c4b2225SAlexander Belyaev 
2939c4b2225SAlexander Belyaev       if (const Stmt *Body = DeclWithIssue->getBody()) {
2949c4b2225SAlexander Belyaev           // Retrieve the relative position of the declaration which will be used
2959c4b2225SAlexander Belyaev           // for the file name
2969c4b2225SAlexander Belyaev           FullSourceLoc L(
2979c4b2225SAlexander Belyaev               SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),
2989c4b2225SAlexander Belyaev               SMgr);
2999c4b2225SAlexander Belyaev           FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getBeginLoc()), SMgr);
3009c4b2225SAlexander Belyaev           offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
3019c4b2225SAlexander Belyaev       }
3029c4b2225SAlexander Belyaev   }
3039c4b2225SAlexander Belyaev 
3049c4b2225SAlexander Belyaev   std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
3059c4b2225SAlexander Belyaev   if (report.empty()) {
3069c4b2225SAlexander Belyaev     llvm::errs() << "warning: no diagnostics generated for main file.\n";
3079c4b2225SAlexander Belyaev     return;
3089c4b2225SAlexander Belyaev   }
3099c4b2225SAlexander Belyaev 
3109c4b2225SAlexander Belyaev   // Create a path for the target HTML file.
3119c4b2225SAlexander Belyaev   int FD;
3129c4b2225SAlexander Belyaev 
31373093599SArtem Dergachev   SmallString<128> FileNameStr;
31473093599SArtem Dergachev   llvm::raw_svector_ostream FileName(FileNameStr);
31573093599SArtem Dergachev   FileName << "report-";
31673093599SArtem Dergachev 
31773093599SArtem Dergachev   // Historically, neither the stable report filename nor the unstable report
31873093599SArtem Dergachev   // filename were actually stable. That said, the stable report filename
31973093599SArtem Dergachev   // was more stable because it was mostly composed of information
32073093599SArtem Dergachev   // about the bug report instead of being completely random.
32173093599SArtem Dergachev   // Now both stable and unstable report filenames are in fact stable
32273093599SArtem Dergachev   // but the stable report filename is still more verbose.
32373093599SArtem Dergachev   if (DiagOpts.ShouldWriteVerboseReportFilename) {
32473093599SArtem Dergachev     // FIXME: This code relies on knowing what constitutes the issue hash.
32573093599SArtem Dergachev     // Otherwise deduplication won't work correctly.
32673093599SArtem Dergachev     FileID ReportFile =
32773093599SArtem Dergachev         path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
32873093599SArtem Dergachev 
32973093599SArtem Dergachev     const FileEntry *Entry = SMgr.getFileEntryForID(ReportFile);
33073093599SArtem Dergachev 
33173093599SArtem Dergachev     FileName << llvm::sys::path::filename(Entry->getName()).str() << "-"
33273093599SArtem Dergachev              << declName.c_str() << "-" << offsetDecl << "-";
33373093599SArtem Dergachev   }
33473093599SArtem Dergachev 
33573093599SArtem Dergachev   FileName << StringRef(getIssueHash(D, PP)).substr(0, 6).str() << ".html";
33673093599SArtem Dergachev 
33773093599SArtem Dergachev   SmallString<128> ResultPath;
33873093599SArtem Dergachev   llvm::sys::path::append(ResultPath, Directory, FileName.str());
33973093599SArtem Dergachev   if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
34073093599SArtem Dergachev     llvm::errs() << "warning: could not make '" << ResultPath
3419c4b2225SAlexander Belyaev                  << "' absolute: " << EC.message() << '\n';
3429c4b2225SAlexander Belyaev     return;
3439c4b2225SAlexander Belyaev   }
34473093599SArtem Dergachev 
34573093599SArtem Dergachev   if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
34673093599SArtem Dergachev           ResultPath, FD, llvm::sys::fs::CD_CreateNew,
347ae206db2SFanbo Meng           llvm::sys::fs::OF_Text)) {
34873093599SArtem Dergachev     // Existence of the file corresponds to the situation where a different
34973093599SArtem Dergachev     // Clang instance has emitted a bug report with the same issue hash.
35073093599SArtem Dergachev     // This is an entirely normal situation that does not deserve a warning,
35173093599SArtem Dergachev     // as apart from hash collisions this can happen because the reports
35273093599SArtem Dergachev     // are in fact similar enough to be considered duplicates of each other.
35373093599SArtem Dergachev     if (EC != llvm::errc::file_exists) {
3549c4b2225SAlexander Belyaev       llvm::errs() << "warning: could not create file in '" << Directory
3559c4b2225SAlexander Belyaev                    << "': " << EC.message() << '\n';
3569c4b2225SAlexander Belyaev     }
3579c4b2225SAlexander Belyaev     return;
3589c4b2225SAlexander Belyaev   }
3599c4b2225SAlexander Belyaev 
3609c4b2225SAlexander Belyaev   llvm::raw_fd_ostream os(FD, true);
3619c4b2225SAlexander Belyaev 
3629c4b2225SAlexander Belyaev   if (filesMade)
3639c4b2225SAlexander Belyaev     filesMade->addDiagnostic(D, getName(),
3649c4b2225SAlexander Belyaev                              llvm::sys::path::filename(ResultPath));
3659c4b2225SAlexander Belyaev 
3669c4b2225SAlexander Belyaev   // Emit the HTML to disk.
3679c4b2225SAlexander Belyaev   os << report;
3689c4b2225SAlexander Belyaev }
3699c4b2225SAlexander Belyaev 
GenerateHTML(const PathDiagnostic & D,Rewriter & R,const SourceManager & SMgr,const PathPieces & path,const char * declName)3709c4b2225SAlexander Belyaev std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
3719c4b2225SAlexander Belyaev     const SourceManager& SMgr, const PathPieces& path, const char *declName) {
3729c4b2225SAlexander Belyaev   // Rewrite source files as HTML for every new file the path crosses
3739c4b2225SAlexander Belyaev   std::vector<FileID> FileIDs;
3749c4b2225SAlexander Belyaev   for (auto I : path) {
3759c4b2225SAlexander Belyaev     FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
3769c4b2225SAlexander Belyaev     if (llvm::is_contained(FileIDs, FID))
3779c4b2225SAlexander Belyaev       continue;
3789c4b2225SAlexander Belyaev 
3799c4b2225SAlexander Belyaev     FileIDs.push_back(FID);
3809c4b2225SAlexander Belyaev     RewriteFile(R, path, FID);
3819c4b2225SAlexander Belyaev   }
3829c4b2225SAlexander Belyaev 
3839c4b2225SAlexander Belyaev   if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
3849c4b2225SAlexander Belyaev     // Prefix file names, anchor tags, and nav cursors to every file
3859c4b2225SAlexander Belyaev     for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {
3869c4b2225SAlexander Belyaev       std::string s;
3879c4b2225SAlexander Belyaev       llvm::raw_string_ostream os(s);
3889c4b2225SAlexander Belyaev 
3899c4b2225SAlexander Belyaev       if (I != FileIDs.begin())
3909c4b2225SAlexander Belyaev         os << "<hr class=divider>\n";
3919c4b2225SAlexander Belyaev 
3929c4b2225SAlexander Belyaev       os << "<div id=File" << I->getHashValue() << ">\n";
3939c4b2225SAlexander Belyaev 
3949c4b2225SAlexander Belyaev       // Left nav arrow
3959c4b2225SAlexander Belyaev       if (I != FileIDs.begin())
3969c4b2225SAlexander Belyaev         os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
3979c4b2225SAlexander Belyaev            << "\">&#x2190;</a></div>";
3989c4b2225SAlexander Belyaev 
3999c4b2225SAlexander Belyaev       os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName()
4009c4b2225SAlexander Belyaev          << "</h4>\n";
4019c4b2225SAlexander Belyaev 
4029c4b2225SAlexander Belyaev       // Right nav arrow
4039c4b2225SAlexander Belyaev       if (I + 1 != E)
4049c4b2225SAlexander Belyaev         os << "<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
4059c4b2225SAlexander Belyaev            << "\">&#x2192;</a></div>";
4069c4b2225SAlexander Belyaev 
4079c4b2225SAlexander Belyaev       os << "</div>\n";
4089c4b2225SAlexander Belyaev 
4099c4b2225SAlexander Belyaev       R.InsertTextBefore(SMgr.getLocForStartOfFile(*I), os.str());
4109c4b2225SAlexander Belyaev     }
4119c4b2225SAlexander Belyaev 
4129c4b2225SAlexander Belyaev     // Append files to the main report file in the order they appear in the path
413*713ee230SKazu Hirata     for (auto I : llvm::drop_begin(FileIDs)) {
4149c4b2225SAlexander Belyaev       std::string s;
4159c4b2225SAlexander Belyaev       llvm::raw_string_ostream os(s);
4169c4b2225SAlexander Belyaev 
4179c4b2225SAlexander Belyaev       const RewriteBuffer *Buf = R.getRewriteBufferFor(I);
4189c4b2225SAlexander Belyaev       for (auto BI : *Buf)
4199c4b2225SAlexander Belyaev         os << BI;
4209c4b2225SAlexander Belyaev 
4219c4b2225SAlexander Belyaev       R.InsertTextAfter(SMgr.getLocForEndOfFile(FileIDs[0]), os.str());
4229c4b2225SAlexander Belyaev     }
4239c4b2225SAlexander Belyaev   }
4249c4b2225SAlexander Belyaev 
4259c4b2225SAlexander Belyaev   const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]);
4269c4b2225SAlexander Belyaev   if (!Buf)
4279c4b2225SAlexander Belyaev     return {};
4289c4b2225SAlexander Belyaev 
4299c4b2225SAlexander Belyaev   // Add CSS, header, and footer.
4309c4b2225SAlexander Belyaev   FileID FID =
4319c4b2225SAlexander Belyaev       path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
4329c4b2225SAlexander Belyaev   const FileEntry* Entry = SMgr.getFileEntryForID(FID);
4339c4b2225SAlexander Belyaev   FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName);
4349c4b2225SAlexander Belyaev 
4359c4b2225SAlexander Belyaev   std::string file;
4369c4b2225SAlexander Belyaev   llvm::raw_string_ostream os(file);
4379c4b2225SAlexander Belyaev   for (auto BI : *Buf)
4389c4b2225SAlexander Belyaev     os << BI;
4399c4b2225SAlexander Belyaev 
440715c72b4SLogan Smith   return file;
4419c4b2225SAlexander Belyaev }
4429c4b2225SAlexander Belyaev 
dumpCoverageData(const PathDiagnostic & D,const PathPieces & path,llvm::raw_string_ostream & os)4439c4b2225SAlexander Belyaev void HTMLDiagnostics::dumpCoverageData(
4449c4b2225SAlexander Belyaev     const PathDiagnostic &D,
4459c4b2225SAlexander Belyaev     const PathPieces &path,
4469c4b2225SAlexander Belyaev     llvm::raw_string_ostream &os) {
4479c4b2225SAlexander Belyaev 
4489c4b2225SAlexander Belyaev   const FilesToLineNumsMap &ExecutedLines = D.getExecutedLines();
4499c4b2225SAlexander Belyaev 
4509c4b2225SAlexander Belyaev   os << "var relevant_lines = {";
4519c4b2225SAlexander Belyaev   for (auto I = ExecutedLines.begin(),
4529c4b2225SAlexander Belyaev             E = ExecutedLines.end(); I != E; ++I) {
4539c4b2225SAlexander Belyaev     if (I != ExecutedLines.begin())
4549c4b2225SAlexander Belyaev       os << ", ";
4559c4b2225SAlexander Belyaev 
4569c4b2225SAlexander Belyaev     os << "\"" << I->first.getHashValue() << "\": {";
4579c4b2225SAlexander Belyaev     for (unsigned LineNo : I->second) {
4589c4b2225SAlexander Belyaev       if (LineNo != *(I->second.begin()))
4599c4b2225SAlexander Belyaev         os << ", ";
4609c4b2225SAlexander Belyaev 
4619c4b2225SAlexander Belyaev       os << "\"" << LineNo << "\": 1";
4629c4b2225SAlexander Belyaev     }
4639c4b2225SAlexander Belyaev     os << "}";
4649c4b2225SAlexander Belyaev   }
4659c4b2225SAlexander Belyaev 
4669c4b2225SAlexander Belyaev   os << "};";
4679c4b2225SAlexander Belyaev }
4689c4b2225SAlexander Belyaev 
showRelevantLinesJavascript(const PathDiagnostic & D,const PathPieces & path)4699c4b2225SAlexander Belyaev std::string HTMLDiagnostics::showRelevantLinesJavascript(
4709c4b2225SAlexander Belyaev       const PathDiagnostic &D, const PathPieces &path) {
4719c4b2225SAlexander Belyaev   std::string s;
4729c4b2225SAlexander Belyaev   llvm::raw_string_ostream os(s);
4739c4b2225SAlexander Belyaev   os << "<script type='text/javascript'>\n";
4749c4b2225SAlexander Belyaev   dumpCoverageData(D, path, os);
4759c4b2225SAlexander Belyaev   os << R"<<<(
4769c4b2225SAlexander Belyaev 
4779c4b2225SAlexander Belyaev var filterCounterexample = function (hide) {
4789c4b2225SAlexander Belyaev   var tables = document.getElementsByClassName("code");
4799c4b2225SAlexander Belyaev   for (var t=0; t<tables.length; t++) {
4809c4b2225SAlexander Belyaev     var table = tables[t];
4819c4b2225SAlexander Belyaev     var file_id = table.getAttribute("data-fileid");
4829c4b2225SAlexander Belyaev     var lines_in_fid = relevant_lines[file_id];
4839c4b2225SAlexander Belyaev     if (!lines_in_fid) {
4849c4b2225SAlexander Belyaev       lines_in_fid = {};
4859c4b2225SAlexander Belyaev     }
4869c4b2225SAlexander Belyaev     var lines = table.getElementsByClassName("codeline");
4879c4b2225SAlexander Belyaev     for (var i=0; i<lines.length; i++) {
4889c4b2225SAlexander Belyaev         var el = lines[i];
4899c4b2225SAlexander Belyaev         var lineNo = el.getAttribute("data-linenumber");
4909c4b2225SAlexander Belyaev         if (!lines_in_fid[lineNo]) {
4919c4b2225SAlexander Belyaev           if (hide) {
4929c4b2225SAlexander Belyaev             el.setAttribute("hidden", "");
4939c4b2225SAlexander Belyaev           } else {
4949c4b2225SAlexander Belyaev             el.removeAttribute("hidden");
4959c4b2225SAlexander Belyaev           }
4969c4b2225SAlexander Belyaev         }
4979c4b2225SAlexander Belyaev     }
4989c4b2225SAlexander Belyaev   }
4999c4b2225SAlexander Belyaev }
5009c4b2225SAlexander Belyaev 
5019c4b2225SAlexander Belyaev window.addEventListener("keydown", function (event) {
5029c4b2225SAlexander Belyaev   if (event.defaultPrevented) {
5039c4b2225SAlexander Belyaev     return;
5049c4b2225SAlexander Belyaev   }
50597bcafa2SValeriy Savchenko   // SHIFT + S
50697bcafa2SValeriy Savchenko   if (event.shiftKey && event.keyCode == 83) {
5079c4b2225SAlexander Belyaev     var checked = document.getElementsByName("showCounterexample")[0].checked;
5089c4b2225SAlexander Belyaev     filterCounterexample(!checked);
50997bcafa2SValeriy Savchenko     document.getElementsByName("showCounterexample")[0].click();
5109c4b2225SAlexander Belyaev   } else {
5119c4b2225SAlexander Belyaev     return;
5129c4b2225SAlexander Belyaev   }
5139c4b2225SAlexander Belyaev   event.preventDefault();
5149c4b2225SAlexander Belyaev }, true);
5159c4b2225SAlexander Belyaev 
5169c4b2225SAlexander Belyaev document.addEventListener("DOMContentLoaded", function() {
5179c4b2225SAlexander Belyaev     document.querySelector('input[name="showCounterexample"]').onchange=
5189c4b2225SAlexander Belyaev         function (event) {
5199c4b2225SAlexander Belyaev       filterCounterexample(this.checked);
5209c4b2225SAlexander Belyaev     };
5219c4b2225SAlexander Belyaev });
5229c4b2225SAlexander Belyaev </script>
5239c4b2225SAlexander Belyaev 
5249c4b2225SAlexander Belyaev <form>
5259c4b2225SAlexander Belyaev     <input type="checkbox" name="showCounterexample" id="showCounterexample" />
5269c4b2225SAlexander Belyaev     <label for="showCounterexample">
5279c4b2225SAlexander Belyaev        Show only relevant lines
5289c4b2225SAlexander Belyaev     </label>
52997bcafa2SValeriy Savchenko     <input type="checkbox" name="showArrows"
53097bcafa2SValeriy Savchenko            id="showArrows" style="margin-left: 10px" />
53197bcafa2SValeriy Savchenko     <label for="showArrows">
53297bcafa2SValeriy Savchenko        Show control flow arrows
53397bcafa2SValeriy Savchenko     </label>
5349c4b2225SAlexander Belyaev </form>
5359c4b2225SAlexander Belyaev )<<<";
5369c4b2225SAlexander Belyaev 
537715c72b4SLogan Smith   return s;
5389c4b2225SAlexander Belyaev }
5399c4b2225SAlexander Belyaev 
FinalizeHTML(const PathDiagnostic & D,Rewriter & R,const SourceManager & SMgr,const PathPieces & path,FileID FID,const FileEntry * Entry,const char * declName)5409c4b2225SAlexander Belyaev void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
5419c4b2225SAlexander Belyaev     const SourceManager& SMgr, const PathPieces& path, FileID FID,
5429c4b2225SAlexander Belyaev     const FileEntry *Entry, const char *declName) {
5439c4b2225SAlexander Belyaev   // This is a cludge; basically we want to append either the full
5449c4b2225SAlexander Belyaev   // working directory if we have no directory information.  This is
5459c4b2225SAlexander Belyaev   // a work in progress.
5469c4b2225SAlexander Belyaev 
5479c4b2225SAlexander Belyaev   llvm::SmallString<0> DirName;
5489c4b2225SAlexander Belyaev 
5499c4b2225SAlexander Belyaev   if (llvm::sys::path::is_relative(Entry->getName())) {
5509c4b2225SAlexander Belyaev     llvm::sys::fs::current_path(DirName);
5519c4b2225SAlexander Belyaev     DirName += '/';
5529c4b2225SAlexander Belyaev   }
5539c4b2225SAlexander Belyaev 
5549c4b2225SAlexander Belyaev   int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
5559c4b2225SAlexander Belyaev   int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
5569c4b2225SAlexander Belyaev 
5579c4b2225SAlexander Belyaev   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), showHelpJavascript());
5589c4b2225SAlexander Belyaev 
5599c4b2225SAlexander Belyaev   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
5609c4b2225SAlexander Belyaev                      generateKeyboardNavigationJavascript());
5619c4b2225SAlexander Belyaev 
56297bcafa2SValeriy Savchenko   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
56397bcafa2SValeriy Savchenko                      generateArrowDrawingJavascript());
56497bcafa2SValeriy Savchenko 
5659c4b2225SAlexander Belyaev   // Checkbox and javascript for filtering the output to the counterexample.
5669c4b2225SAlexander Belyaev   R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
5679c4b2225SAlexander Belyaev                      showRelevantLinesJavascript(D, path));
5689c4b2225SAlexander Belyaev 
5699c4b2225SAlexander Belyaev   // Add the name of the file as an <h1> tag.
5709c4b2225SAlexander Belyaev   {
5719c4b2225SAlexander Belyaev     std::string s;
5729c4b2225SAlexander Belyaev     llvm::raw_string_ostream os(s);
5739c4b2225SAlexander Belyaev 
5749c4b2225SAlexander Belyaev     os << "<!-- REPORTHEADER -->\n"
5759c4b2225SAlexander Belyaev        << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
5769c4b2225SAlexander Belyaev           "<tr><td class=\"rowname\">File:</td><td>"
5779c4b2225SAlexander Belyaev        << html::EscapeText(DirName)
5789c4b2225SAlexander Belyaev        << html::EscapeText(Entry->getName())
5799c4b2225SAlexander Belyaev        << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
5809c4b2225SAlexander Belyaev           "<a href=\"#EndPath\">line "
5819c4b2225SAlexander Belyaev        << LineNumber
5829c4b2225SAlexander Belyaev        << ", column "
5839c4b2225SAlexander Belyaev        << ColumnNumber
5849c4b2225SAlexander Belyaev        << "</a><br />"
5859c4b2225SAlexander Belyaev        << D.getVerboseDescription() << "</td></tr>\n";
5869c4b2225SAlexander Belyaev 
5879c4b2225SAlexander Belyaev     // The navigation across the extra notes pieces.
5889c4b2225SAlexander Belyaev     unsigned NumExtraPieces = 0;
5899c4b2225SAlexander Belyaev     for (const auto &Piece : path) {
5909c4b2225SAlexander Belyaev       if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
5919c4b2225SAlexander Belyaev         int LineNumber =
5929c4b2225SAlexander Belyaev             P->getLocation().asLocation().getExpansionLineNumber();
5939c4b2225SAlexander Belyaev         int ColumnNumber =
5949c4b2225SAlexander Belyaev             P->getLocation().asLocation().getExpansionColumnNumber();
5959c4b2225SAlexander Belyaev         os << "<tr><td class=\"rowname\">Note:</td><td>"
5969c4b2225SAlexander Belyaev            << "<a href=\"#Note" << NumExtraPieces << "\">line "
5979c4b2225SAlexander Belyaev            << LineNumber << ", column " << ColumnNumber << "</a><br />"
5989c4b2225SAlexander Belyaev            << P->getString() << "</td></tr>";
5999c4b2225SAlexander Belyaev         ++NumExtraPieces;
6009c4b2225SAlexander Belyaev       }
6019c4b2225SAlexander Belyaev     }
6029c4b2225SAlexander Belyaev 
6039c4b2225SAlexander Belyaev     // Output any other meta data.
6049c4b2225SAlexander Belyaev 
6059c4b2225SAlexander Belyaev     for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end();
6069c4b2225SAlexander Belyaev          I != E; ++I) {
6079c4b2225SAlexander Belyaev       os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
6089c4b2225SAlexander Belyaev     }
6099c4b2225SAlexander Belyaev 
6109c4b2225SAlexander Belyaev     os << R"<<<(
6119c4b2225SAlexander Belyaev </table>
6129c4b2225SAlexander Belyaev <!-- REPORTSUMMARYEXTRA -->
6139c4b2225SAlexander Belyaev <h3>Annotated Source Code</h3>
6149c4b2225SAlexander Belyaev <p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
6159c4b2225SAlexander Belyaev    to see keyboard shortcuts</p>
6169c4b2225SAlexander Belyaev <input type="checkbox" class="spoilerhider" id="showinvocation" />
6179c4b2225SAlexander Belyaev <label for="showinvocation" >Show analyzer invocation</label>
6189c4b2225SAlexander Belyaev <div class="spoiler">clang -cc1 )<<<";
6199c4b2225SAlexander Belyaev     os << html::EscapeText(DiagOpts.ToolInvocation);
6209c4b2225SAlexander Belyaev     os << R"<<<(
6219c4b2225SAlexander Belyaev </div>
6229c4b2225SAlexander Belyaev <div id='tooltiphint' hidden="true">
6239c4b2225SAlexander Belyaev   <p>Keyboard shortcuts: </p>
6249c4b2225SAlexander Belyaev   <ul>
6259c4b2225SAlexander Belyaev     <li>Use 'j/k' keys for keyboard navigation</li>
6269c4b2225SAlexander Belyaev     <li>Use 'Shift+S' to show/hide relevant lines</li>
6279c4b2225SAlexander Belyaev     <li>Use '?' to toggle this window</li>
6289c4b2225SAlexander Belyaev   </ul>
6299c4b2225SAlexander Belyaev   <a href="#" onclick="toggleHelp(); return false;">Close</a>
6309c4b2225SAlexander Belyaev </div>
6319c4b2225SAlexander Belyaev )<<<";
63297bcafa2SValeriy Savchenko 
6339c4b2225SAlexander Belyaev     R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
6349c4b2225SAlexander Belyaev   }
6359c4b2225SAlexander Belyaev 
6369c4b2225SAlexander Belyaev   // Embed meta-data tags.
6379c4b2225SAlexander Belyaev   {
6389c4b2225SAlexander Belyaev     std::string s;
6399c4b2225SAlexander Belyaev     llvm::raw_string_ostream os(s);
6409c4b2225SAlexander Belyaev 
6419c4b2225SAlexander Belyaev     StringRef BugDesc = D.getVerboseDescription();
6429c4b2225SAlexander Belyaev     if (!BugDesc.empty())
6439c4b2225SAlexander Belyaev       os << "\n<!-- BUGDESC " << BugDesc << " -->\n";
6449c4b2225SAlexander Belyaev 
6459c4b2225SAlexander Belyaev     StringRef BugType = D.getBugType();
6469c4b2225SAlexander Belyaev     if (!BugType.empty())
6479c4b2225SAlexander Belyaev       os << "\n<!-- BUGTYPE " << BugType << " -->\n";
6489c4b2225SAlexander Belyaev 
6499c4b2225SAlexander Belyaev     PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
6509c4b2225SAlexander Belyaev     FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
6519c4b2225SAlexander Belyaev                                              ? UPDLoc.asLocation()
6529c4b2225SAlexander Belyaev                                              : D.getLocation().asLocation()),
6539c4b2225SAlexander Belyaev                     SMgr);
6549c4b2225SAlexander Belyaev 
6559c4b2225SAlexander Belyaev     StringRef BugCategory = D.getCategory();
6569c4b2225SAlexander Belyaev     if (!BugCategory.empty())
6579c4b2225SAlexander Belyaev       os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";
6589c4b2225SAlexander Belyaev 
6599c4b2225SAlexander Belyaev     os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
6609c4b2225SAlexander Belyaev 
6619c4b2225SAlexander Belyaev     os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n";
6629c4b2225SAlexander Belyaev 
6639c4b2225SAlexander Belyaev     os  << "\n<!-- FUNCTIONNAME " <<  declName << " -->\n";
6649c4b2225SAlexander Belyaev 
66573093599SArtem Dergachev     os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)
6669c4b2225SAlexander Belyaev        << " -->\n";
6679c4b2225SAlexander Belyaev 
6689c4b2225SAlexander Belyaev     os << "\n<!-- BUGLINE "
6699c4b2225SAlexander Belyaev        << LineNumber
6709c4b2225SAlexander Belyaev        << " -->\n";
6719c4b2225SAlexander Belyaev 
6729c4b2225SAlexander Belyaev     os << "\n<!-- BUGCOLUMN "
6739c4b2225SAlexander Belyaev       << ColumnNumber
6749c4b2225SAlexander Belyaev       << " -->\n";
6759c4b2225SAlexander Belyaev 
67697bcafa2SValeriy Savchenko     os << "\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) << " -->\n";
6779c4b2225SAlexander Belyaev 
6789c4b2225SAlexander Belyaev     // Mark the end of the tags.
6799c4b2225SAlexander Belyaev     os << "\n<!-- BUGMETAEND -->\n";
6809c4b2225SAlexander Belyaev 
6819c4b2225SAlexander Belyaev     // Insert the text.
6829c4b2225SAlexander Belyaev     R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
6839c4b2225SAlexander Belyaev   }
6849c4b2225SAlexander Belyaev 
6859c4b2225SAlexander Belyaev   html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
6869c4b2225SAlexander Belyaev }
6879c4b2225SAlexander Belyaev 
showHelpJavascript()6889c4b2225SAlexander Belyaev StringRef HTMLDiagnostics::showHelpJavascript() {
6899c4b2225SAlexander Belyaev   return R"<<<(
6909c4b2225SAlexander Belyaev <script type='text/javascript'>
6919c4b2225SAlexander Belyaev 
6929c4b2225SAlexander Belyaev var toggleHelp = function() {
6939c4b2225SAlexander Belyaev     var hint = document.querySelector("#tooltiphint");
6949c4b2225SAlexander Belyaev     var attributeName = "hidden";
6959c4b2225SAlexander Belyaev     if (hint.hasAttribute(attributeName)) {
6969c4b2225SAlexander Belyaev       hint.removeAttribute(attributeName);
6979c4b2225SAlexander Belyaev     } else {
6989c4b2225SAlexander Belyaev       hint.setAttribute("hidden", "true");
6999c4b2225SAlexander Belyaev     }
7009c4b2225SAlexander Belyaev };
7019c4b2225SAlexander Belyaev window.addEventListener("keydown", function (event) {
7029c4b2225SAlexander Belyaev   if (event.defaultPrevented) {
7039c4b2225SAlexander Belyaev     return;
7049c4b2225SAlexander Belyaev   }
7059c4b2225SAlexander Belyaev   if (event.key == "?") {
7069c4b2225SAlexander Belyaev     toggleHelp();
7079c4b2225SAlexander Belyaev   } else {
7089c4b2225SAlexander Belyaev     return;
7099c4b2225SAlexander Belyaev   }
7109c4b2225SAlexander Belyaev   event.preventDefault();
7119c4b2225SAlexander Belyaev });
7129c4b2225SAlexander Belyaev </script>
7139c4b2225SAlexander Belyaev )<<<";
7149c4b2225SAlexander Belyaev }
7159c4b2225SAlexander Belyaev 
shouldDisplayPopUpRange(const SourceRange & Range)7169c4b2225SAlexander Belyaev static bool shouldDisplayPopUpRange(const SourceRange &Range) {
7179c4b2225SAlexander Belyaev   return !(Range.getBegin().isMacroID() || Range.getEnd().isMacroID());
7189c4b2225SAlexander Belyaev }
7199c4b2225SAlexander Belyaev 
7209c4b2225SAlexander Belyaev static void
HandlePopUpPieceStartTag(Rewriter & R,const std::vector<SourceRange> & PopUpRanges)7219c4b2225SAlexander Belyaev HandlePopUpPieceStartTag(Rewriter &R,
7229c4b2225SAlexander Belyaev                          const std::vector<SourceRange> &PopUpRanges) {
7239c4b2225SAlexander Belyaev   for (const auto &Range : PopUpRanges) {
7249c4b2225SAlexander Belyaev     if (!shouldDisplayPopUpRange(Range))
7259c4b2225SAlexander Belyaev       continue;
7269c4b2225SAlexander Belyaev 
7279c4b2225SAlexander Belyaev     html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "",
7289c4b2225SAlexander Belyaev                          "<table class='variable_popup'><tbody>",
7299c4b2225SAlexander Belyaev                          /*IsTokenRange=*/true);
7309c4b2225SAlexander Belyaev   }
7319c4b2225SAlexander Belyaev }
7329c4b2225SAlexander Belyaev 
HandlePopUpPieceEndTag(Rewriter & R,const PathDiagnosticPopUpPiece & Piece,std::vector<SourceRange> & PopUpRanges,unsigned int LastReportedPieceIndex,unsigned int PopUpPieceIndex)7339c4b2225SAlexander Belyaev static void HandlePopUpPieceEndTag(Rewriter &R,
7349c4b2225SAlexander Belyaev                                    const PathDiagnosticPopUpPiece &Piece,
7359c4b2225SAlexander Belyaev                                    std::vector<SourceRange> &PopUpRanges,
7369c4b2225SAlexander Belyaev                                    unsigned int LastReportedPieceIndex,
7379c4b2225SAlexander Belyaev                                    unsigned int PopUpPieceIndex) {
7389c4b2225SAlexander Belyaev   SmallString<256> Buf;
7399c4b2225SAlexander Belyaev   llvm::raw_svector_ostream Out(Buf);
7409c4b2225SAlexander Belyaev 
7419c4b2225SAlexander Belyaev   SourceRange Range(Piece.getLocation().asRange());
7429c4b2225SAlexander Belyaev   if (!shouldDisplayPopUpRange(Range))
7439c4b2225SAlexander Belyaev     return;
7449c4b2225SAlexander Belyaev 
7459c4b2225SAlexander Belyaev   // Write out the path indices with a right arrow and the message as a row.
7469c4b2225SAlexander Belyaev   Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
7479c4b2225SAlexander Belyaev       << LastReportedPieceIndex;
7489c4b2225SAlexander Belyaev 
7499c4b2225SAlexander Belyaev   // Also annotate the state transition with extra indices.
7509c4b2225SAlexander Belyaev   Out << '.' << PopUpPieceIndex;
7519c4b2225SAlexander Belyaev 
7529c4b2225SAlexander Belyaev   Out << "</div></td><td>" << Piece.getString() << "</td></tr>";
7539c4b2225SAlexander Belyaev 
7549c4b2225SAlexander Belyaev   // If no report made at this range mark the variable and add the end tags.
755e567f37dSKazu Hirata   if (!llvm::is_contained(PopUpRanges, Range)) {
7569c4b2225SAlexander Belyaev     // Store that we create a report at this range.
7579c4b2225SAlexander Belyaev     PopUpRanges.push_back(Range);
7589c4b2225SAlexander Belyaev 
7599c4b2225SAlexander Belyaev     Out << "</tbody></table></span>";
7609c4b2225SAlexander Belyaev     html::HighlightRange(R, Range.getBegin(), Range.getEnd(),
7619c4b2225SAlexander Belyaev                          "<span class='variable'>", Buf.c_str(),
7629c4b2225SAlexander Belyaev                          /*IsTokenRange=*/true);
7639c4b2225SAlexander Belyaev   } else {
7649c4b2225SAlexander Belyaev     // Otherwise inject just the new row at the end of the range.
7659c4b2225SAlexander Belyaev     html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(),
7669c4b2225SAlexander Belyaev                          /*IsTokenRange=*/true);
7679c4b2225SAlexander Belyaev   }
7689c4b2225SAlexander Belyaev }
7699c4b2225SAlexander Belyaev 
RewriteFile(Rewriter & R,const PathPieces & path,FileID FID)77097bcafa2SValeriy Savchenko void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,
77197bcafa2SValeriy Savchenko                                   FileID FID) {
77297bcafa2SValeriy Savchenko 
7739c4b2225SAlexander Belyaev   // Process the path.
7749c4b2225SAlexander Belyaev   // Maintain the counts of extra note pieces separately.
77597bcafa2SValeriy Savchenko   unsigned TotalPieces = getPathSizeWithoutArrows(path);
77697bcafa2SValeriy Savchenko   unsigned TotalNotePieces =
77797bcafa2SValeriy Savchenko       llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
7789c4b2225SAlexander Belyaev         return isa<PathDiagnosticNotePiece>(*p);
7799c4b2225SAlexander Belyaev       });
78097bcafa2SValeriy Savchenko   unsigned PopUpPieceCount =
78197bcafa2SValeriy Savchenko       llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
7829c4b2225SAlexander Belyaev         return isa<PathDiagnosticPopUpPiece>(*p);
7839c4b2225SAlexander Belyaev       });
7849c4b2225SAlexander Belyaev 
7859c4b2225SAlexander Belyaev   unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
7869c4b2225SAlexander Belyaev   unsigned NumRegularPieces = TotalRegularPieces;
7879c4b2225SAlexander Belyaev   unsigned NumNotePieces = TotalNotePieces;
78897bcafa2SValeriy Savchenko   unsigned NumberOfArrows = 0;
7899c4b2225SAlexander Belyaev   // Stores the count of the regular piece indices.
7909c4b2225SAlexander Belyaev   std::map<int, int> IndexMap;
7919e02f587SValeriy Savchenko   ArrowMap ArrowIndices(TotalRegularPieces + 1);
7929c4b2225SAlexander Belyaev 
7939c4b2225SAlexander Belyaev   // Stores the different ranges where we have reported something.
7949c4b2225SAlexander Belyaev   std::vector<SourceRange> PopUpRanges;
79574115602SKazu Hirata   for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
79674115602SKazu Hirata     const auto &Piece = *I.get();
7979c4b2225SAlexander Belyaev 
7989c4b2225SAlexander Belyaev     if (isa<PathDiagnosticPopUpPiece>(Piece)) {
7999c4b2225SAlexander Belyaev       ++IndexMap[NumRegularPieces];
8009c4b2225SAlexander Belyaev     } else if (isa<PathDiagnosticNotePiece>(Piece)) {
8019c4b2225SAlexander Belyaev       // This adds diagnostic bubbles, but not navigation.
8029c4b2225SAlexander Belyaev       // Navigation through note pieces would be added later,
8039c4b2225SAlexander Belyaev       // as a separate pass through the piece list.
8049c4b2225SAlexander Belyaev       HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
8059c4b2225SAlexander Belyaev       --NumNotePieces;
80697bcafa2SValeriy Savchenko 
80797bcafa2SValeriy Savchenko     } else if (isArrowPiece(Piece)) {
80897bcafa2SValeriy Savchenko       NumberOfArrows = ProcessControlFlowPiece(
80997bcafa2SValeriy Savchenko           R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
8109e02f587SValeriy Savchenko       ArrowIndices[NumRegularPieces] = NumberOfArrows;
81197bcafa2SValeriy Savchenko 
8129c4b2225SAlexander Belyaev     } else {
8139c4b2225SAlexander Belyaev       HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
8149c4b2225SAlexander Belyaev                   TotalRegularPieces);
8159c4b2225SAlexander Belyaev       --NumRegularPieces;
8169e02f587SValeriy Savchenko       ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
8179c4b2225SAlexander Belyaev     }
8189c4b2225SAlexander Belyaev   }
8199e02f587SValeriy Savchenko   ArrowIndices[0] = NumberOfArrows;
8209e02f587SValeriy Savchenko 
8219e02f587SValeriy Savchenko   // At this point ArrowIndices represent the following data structure:
8229e02f587SValeriy Savchenko   //   [a_0, a_1, ..., a_N]
8239e02f587SValeriy Savchenko   // where N is the number of events in the path.
8249e02f587SValeriy Savchenko   //
8259e02f587SValeriy Savchenko   // Then for every event with index i \in [0, N - 1], we can say that
8269e02f587SValeriy Savchenko   // arrows with indices \in [a_(i+1), a_i) correspond to that event.
8279e02f587SValeriy Savchenko   // We can say that because arrows with these indices appeared in the
8289e02f587SValeriy Savchenko   // path in between the i-th and the (i+1)-th events.
8299e02f587SValeriy Savchenko   assert(ArrowIndices.back() == 0 &&
8309e02f587SValeriy Savchenko          "No arrows should be after the last event");
8319e02f587SValeriy Savchenko   // This assertion also guarantees that all indices in are <= NumberOfArrows.
8329e02f587SValeriy Savchenko   assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
8339e02f587SValeriy Savchenko          "Incorrect arrow indices map");
8349c4b2225SAlexander Belyaev 
8359c4b2225SAlexander Belyaev   // Secondary indexing if we are having multiple pop-ups between two notes.
8369c4b2225SAlexander Belyaev   // (e.g. [(13) 'a' is 'true'];  [(13.1) 'b' is 'false'];  [(13.2) 'c' is...)
8379c4b2225SAlexander Belyaev   NumRegularPieces = TotalRegularPieces;
83874115602SKazu Hirata   for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
83974115602SKazu Hirata     const auto &Piece = *I.get();
8409c4b2225SAlexander Belyaev 
8419c4b2225SAlexander Belyaev     if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
8429c4b2225SAlexander Belyaev       int PopUpPieceIndex = IndexMap[NumRegularPieces];
8439c4b2225SAlexander Belyaev 
8449c4b2225SAlexander Belyaev       // Pop-up pieces needs the index of the last reported piece and its count
8459c4b2225SAlexander Belyaev       // how many times we report to handle multiple reports on the same range.
8469c4b2225SAlexander Belyaev       // This marks the variable, adds the </table> end tag and the message
8479c4b2225SAlexander Belyaev       // (list element) as a row. The <table> start tag will be added after the
8489c4b2225SAlexander Belyaev       // rows has been written out. Note: It stores every different range.
8499c4b2225SAlexander Belyaev       HandlePopUpPieceEndTag(R, *PopUpP, PopUpRanges, NumRegularPieces,
8509c4b2225SAlexander Belyaev                              PopUpPieceIndex);
8519c4b2225SAlexander Belyaev 
8529c4b2225SAlexander Belyaev       if (PopUpPieceIndex > 0)
8539c4b2225SAlexander Belyaev         --IndexMap[NumRegularPieces];
8549c4b2225SAlexander Belyaev 
85597bcafa2SValeriy Savchenko     } else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
8569c4b2225SAlexander Belyaev       --NumRegularPieces;
8579c4b2225SAlexander Belyaev     }
8589c4b2225SAlexander Belyaev   }
8599c4b2225SAlexander Belyaev 
8609c4b2225SAlexander Belyaev   // Add the <table> start tag of pop-up pieces based on the stored ranges.
8619c4b2225SAlexander Belyaev   HandlePopUpPieceStartTag(R, PopUpRanges);
8629c4b2225SAlexander Belyaev 
8639c4b2225SAlexander Belyaev   // Add line numbers, header, footer, etc.
8649c4b2225SAlexander Belyaev   html::EscapeText(R, FID);
8659c4b2225SAlexander Belyaev   html::AddLineNumbers(R, FID);
8669c4b2225SAlexander Belyaev 
8679e02f587SValeriy Savchenko   addArrowSVGs(R, FID, ArrowIndices);
86897bcafa2SValeriy Savchenko 
8699c4b2225SAlexander Belyaev   // If we have a preprocessor, relex the file and syntax highlight.
8709c4b2225SAlexander Belyaev   // We might not have a preprocessor if we come from a deserialized AST file,
8719c4b2225SAlexander Belyaev   // for example.
8729c4b2225SAlexander Belyaev   html::SyntaxHighlight(R, FID, PP);
8739c4b2225SAlexander Belyaev   html::HighlightMacros(R, FID, PP);
8749c4b2225SAlexander Belyaev }
8759c4b2225SAlexander Belyaev 
HandlePiece(Rewriter & R,FileID BugFileID,const PathDiagnosticPiece & P,const std::vector<SourceRange> & PopUpRanges,unsigned num,unsigned max)8769c4b2225SAlexander Belyaev void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
8779c4b2225SAlexander Belyaev                                   const PathDiagnosticPiece &P,
8789c4b2225SAlexander Belyaev                                   const std::vector<SourceRange> &PopUpRanges,
8799c4b2225SAlexander Belyaev                                   unsigned num, unsigned max) {
8809c4b2225SAlexander Belyaev   // For now, just draw a box above the line in question, and emit the
8819c4b2225SAlexander Belyaev   // warning.
8829c4b2225SAlexander Belyaev   FullSourceLoc Pos = P.getLocation().asLocation();
8839c4b2225SAlexander Belyaev 
8849c4b2225SAlexander Belyaev   if (!Pos.isValid())
8859c4b2225SAlexander Belyaev     return;
8869c4b2225SAlexander Belyaev 
8879c4b2225SAlexander Belyaev   SourceManager &SM = R.getSourceMgr();
8889c4b2225SAlexander Belyaev   assert(&Pos.getManager() == &SM && "SourceManagers are different!");
8899c4b2225SAlexander Belyaev   std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);
8909c4b2225SAlexander Belyaev 
8919c4b2225SAlexander Belyaev   if (LPosInfo.first != BugFileID)
8929c4b2225SAlexander Belyaev     return;
8939c4b2225SAlexander Belyaev 
8949c4b2225SAlexander Belyaev   llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first);
8959c4b2225SAlexander Belyaev   const char *FileStart = Buf.getBufferStart();
8969c4b2225SAlexander Belyaev 
8979c4b2225SAlexander Belyaev   // Compute the column number.  Rewind from the current position to the start
8989c4b2225SAlexander Belyaev   // of the line.
8999c4b2225SAlexander Belyaev   unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
9009c4b2225SAlexander Belyaev   const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData();
9019c4b2225SAlexander Belyaev   const char *LineStart = TokInstantiationPtr-ColNo;
9029c4b2225SAlexander Belyaev 
9039c4b2225SAlexander Belyaev   // Compute LineEnd.
9049c4b2225SAlexander Belyaev   const char *LineEnd = TokInstantiationPtr;
9059c4b2225SAlexander Belyaev   const char *FileEnd = Buf.getBufferEnd();
9069c4b2225SAlexander Belyaev   while (*LineEnd != '\n' && LineEnd != FileEnd)
9079c4b2225SAlexander Belyaev     ++LineEnd;
9089c4b2225SAlexander Belyaev 
9099c4b2225SAlexander Belyaev   // Compute the margin offset by counting tabs and non-tabs.
9109c4b2225SAlexander Belyaev   unsigned PosNo = 0;
9119c4b2225SAlexander Belyaev   for (const char* c = LineStart; c != TokInstantiationPtr; ++c)
9129c4b2225SAlexander Belyaev     PosNo += *c == '\t' ? 8 : 1;
9139c4b2225SAlexander Belyaev 
9149c4b2225SAlexander Belyaev   // Create the html for the message.
9159c4b2225SAlexander Belyaev 
9169c4b2225SAlexander Belyaev   const char *Kind = nullptr;
9179c4b2225SAlexander Belyaev   bool IsNote = false;
9189c4b2225SAlexander Belyaev   bool SuppressIndex = (max == 1);
9199c4b2225SAlexander Belyaev   switch (P.getKind()) {
9209c4b2225SAlexander Belyaev   case PathDiagnosticPiece::Event: Kind = "Event"; break;
9219c4b2225SAlexander Belyaev   case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
9229c4b2225SAlexander Belyaev     // Setting Kind to "Control" is intentional.
9239c4b2225SAlexander Belyaev   case PathDiagnosticPiece::Macro: Kind = "Control"; break;
9249c4b2225SAlexander Belyaev   case PathDiagnosticPiece::Note:
9259c4b2225SAlexander Belyaev     Kind = "Note";
9269c4b2225SAlexander Belyaev     IsNote = true;
9279c4b2225SAlexander Belyaev     SuppressIndex = true;
9289c4b2225SAlexander Belyaev     break;
9299c4b2225SAlexander Belyaev   case PathDiagnosticPiece::Call:
9309c4b2225SAlexander Belyaev   case PathDiagnosticPiece::PopUp:
9319c4b2225SAlexander Belyaev     llvm_unreachable("Calls and extra notes should already be handled");
9329c4b2225SAlexander Belyaev   }
9339c4b2225SAlexander Belyaev 
9349c4b2225SAlexander Belyaev   std::string sbuf;
9359c4b2225SAlexander Belyaev   llvm::raw_string_ostream os(sbuf);
9369c4b2225SAlexander Belyaev 
9379c4b2225SAlexander Belyaev   os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
9389c4b2225SAlexander Belyaev 
9399c4b2225SAlexander Belyaev   if (IsNote)
9409c4b2225SAlexander Belyaev     os << "Note" << num;
9419c4b2225SAlexander Belyaev   else if (num == max)
9429c4b2225SAlexander Belyaev     os << "EndPath";
9439c4b2225SAlexander Belyaev   else
9449c4b2225SAlexander Belyaev     os << "Path" << num;
9459c4b2225SAlexander Belyaev 
9469c4b2225SAlexander Belyaev   os << "\" class=\"msg";
9479c4b2225SAlexander Belyaev   if (Kind)
9489c4b2225SAlexander Belyaev     os << " msg" << Kind;
9499c4b2225SAlexander Belyaev   os << "\" style=\"margin-left:" << PosNo << "ex";
9509c4b2225SAlexander Belyaev 
9519c4b2225SAlexander Belyaev   // Output a maximum size.
9529c4b2225SAlexander Belyaev   if (!isa<PathDiagnosticMacroPiece>(P)) {
9539c4b2225SAlexander Belyaev     // Get the string and determining its maximum substring.
9549c4b2225SAlexander Belyaev     const auto &Msg = P.getString();
9559c4b2225SAlexander Belyaev     unsigned max_token = 0;
9569c4b2225SAlexander Belyaev     unsigned cnt = 0;
9579c4b2225SAlexander Belyaev     unsigned len = Msg.size();
9589c4b2225SAlexander Belyaev 
9599c4b2225SAlexander Belyaev     for (char C : Msg)
9609c4b2225SAlexander Belyaev       switch (C) {
9619c4b2225SAlexander Belyaev       default:
9629c4b2225SAlexander Belyaev         ++cnt;
9639c4b2225SAlexander Belyaev         continue;
9649c4b2225SAlexander Belyaev       case ' ':
9659c4b2225SAlexander Belyaev       case '\t':
9669c4b2225SAlexander Belyaev       case '\n':
9679c4b2225SAlexander Belyaev         if (cnt > max_token) max_token = cnt;
9689c4b2225SAlexander Belyaev         cnt = 0;
9699c4b2225SAlexander Belyaev       }
9709c4b2225SAlexander Belyaev 
9719c4b2225SAlexander Belyaev     if (cnt > max_token)
9729c4b2225SAlexander Belyaev       max_token = cnt;
9739c4b2225SAlexander Belyaev 
9749c4b2225SAlexander Belyaev     // Determine the approximate size of the message bubble in em.
9759c4b2225SAlexander Belyaev     unsigned em;
9769c4b2225SAlexander Belyaev     const unsigned max_line = 120;
9779c4b2225SAlexander Belyaev 
9789c4b2225SAlexander Belyaev     if (max_token >= max_line)
9799c4b2225SAlexander Belyaev       em = max_token / 2;
9809c4b2225SAlexander Belyaev     else {
9819c4b2225SAlexander Belyaev       unsigned characters = max_line;
9829c4b2225SAlexander Belyaev       unsigned lines = len / max_line;
9839c4b2225SAlexander Belyaev 
9849c4b2225SAlexander Belyaev       if (lines > 0) {
9859c4b2225SAlexander Belyaev         for (; characters > max_token; --characters)
9869c4b2225SAlexander Belyaev           if (len / characters > lines) {
9879c4b2225SAlexander Belyaev             ++characters;
9889c4b2225SAlexander Belyaev             break;
9899c4b2225SAlexander Belyaev           }
9909c4b2225SAlexander Belyaev       }
9919c4b2225SAlexander Belyaev 
9929c4b2225SAlexander Belyaev       em = characters / 2;
9939c4b2225SAlexander Belyaev     }
9949c4b2225SAlexander Belyaev 
9959c4b2225SAlexander Belyaev     if (em < max_line/2)
9969c4b2225SAlexander Belyaev       os << "; max-width:" << em << "em";
9979c4b2225SAlexander Belyaev   }
9989c4b2225SAlexander Belyaev   else
9999c4b2225SAlexander Belyaev     os << "; max-width:100em";
10009c4b2225SAlexander Belyaev 
10019c4b2225SAlexander Belyaev   os << "\">";
10029c4b2225SAlexander Belyaev 
10039c4b2225SAlexander Belyaev   if (!SuppressIndex) {
10049c4b2225SAlexander Belyaev     os << "<table class=\"msgT\"><tr><td valign=\"top\">";
10059c4b2225SAlexander Belyaev     os << "<div class=\"PathIndex";
10069c4b2225SAlexander Belyaev     if (Kind) os << " PathIndex" << Kind;
10079c4b2225SAlexander Belyaev     os << "\">" << num << "</div>";
10089c4b2225SAlexander Belyaev 
10099c4b2225SAlexander Belyaev     if (num > 1) {
10109c4b2225SAlexander Belyaev       os << "</td><td><div class=\"PathNav\"><a href=\"#Path"
10119c4b2225SAlexander Belyaev          << (num - 1)
10129c4b2225SAlexander Belyaev          << "\" title=\"Previous event ("
10139c4b2225SAlexander Belyaev          << (num - 1)
10149c4b2225SAlexander Belyaev          << ")\">&#x2190;</a></div>";
10159c4b2225SAlexander Belyaev     }
10169c4b2225SAlexander Belyaev 
10179c4b2225SAlexander Belyaev     os << "</td><td>";
10189c4b2225SAlexander Belyaev   }
10199c4b2225SAlexander Belyaev 
10209c4b2225SAlexander Belyaev   if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) {
10219c4b2225SAlexander Belyaev     os << "Within the expansion of the macro '";
10229c4b2225SAlexander Belyaev 
10239c4b2225SAlexander Belyaev     // Get the name of the macro by relexing it.
10249c4b2225SAlexander Belyaev     {
10259c4b2225SAlexander Belyaev       FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
10269c4b2225SAlexander Belyaev       assert(L.isFileID());
10279c4b2225SAlexander Belyaev       StringRef BufferInfo = L.getBufferData();
10289c4b2225SAlexander Belyaev       std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc();
10299c4b2225SAlexander Belyaev       const char* MacroName = LocInfo.second + BufferInfo.data();
10309c4b2225SAlexander Belyaev       Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
10319c4b2225SAlexander Belyaev                      BufferInfo.begin(), MacroName, BufferInfo.end());
10329c4b2225SAlexander Belyaev 
10339c4b2225SAlexander Belyaev       Token TheTok;
10349c4b2225SAlexander Belyaev       rawLexer.LexFromRawLexer(TheTok);
10359c4b2225SAlexander Belyaev       for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
10369c4b2225SAlexander Belyaev         os << MacroName[i];
10379c4b2225SAlexander Belyaev     }
10389c4b2225SAlexander Belyaev 
10399c4b2225SAlexander Belyaev     os << "':\n";
10409c4b2225SAlexander Belyaev 
10419c4b2225SAlexander Belyaev     if (!SuppressIndex) {
10429c4b2225SAlexander Belyaev       os << "</td>";
10439c4b2225SAlexander Belyaev       if (num < max) {
10449c4b2225SAlexander Belyaev         os << "<td><div class=\"PathNav\"><a href=\"#";
10459c4b2225SAlexander Belyaev         if (num == max - 1)
10469c4b2225SAlexander Belyaev           os << "EndPath";
10479c4b2225SAlexander Belyaev         else
10489c4b2225SAlexander Belyaev           os << "Path" << (num + 1);
10499c4b2225SAlexander Belyaev         os << "\" title=\"Next event ("
10509c4b2225SAlexander Belyaev         << (num + 1)
10519c4b2225SAlexander Belyaev         << ")\">&#x2192;</a></div></td>";
10529c4b2225SAlexander Belyaev       }
10539c4b2225SAlexander Belyaev 
10549c4b2225SAlexander Belyaev       os << "</tr></table>";
10559c4b2225SAlexander Belyaev     }
10569c4b2225SAlexander Belyaev 
10579c4b2225SAlexander Belyaev     // Within a macro piece.  Write out each event.
10589c4b2225SAlexander Belyaev     ProcessMacroPiece(os, *MP, 0);
10599c4b2225SAlexander Belyaev   }
10609c4b2225SAlexander Belyaev   else {
10619c4b2225SAlexander Belyaev     os << html::EscapeText(P.getString());
10629c4b2225SAlexander Belyaev 
10639c4b2225SAlexander Belyaev     if (!SuppressIndex) {
10649c4b2225SAlexander Belyaev       os << "</td>";
10659c4b2225SAlexander Belyaev       if (num < max) {
10669c4b2225SAlexander Belyaev         os << "<td><div class=\"PathNav\"><a href=\"#";
10679c4b2225SAlexander Belyaev         if (num == max - 1)
10689c4b2225SAlexander Belyaev           os << "EndPath";
10699c4b2225SAlexander Belyaev         else
10709c4b2225SAlexander Belyaev           os << "Path" << (num + 1);
10719c4b2225SAlexander Belyaev         os << "\" title=\"Next event ("
10729c4b2225SAlexander Belyaev            << (num + 1)
10739c4b2225SAlexander Belyaev            << ")\">&#x2192;</a></div></td>";
10749c4b2225SAlexander Belyaev       }
10759c4b2225SAlexander Belyaev 
10769c4b2225SAlexander Belyaev       os << "</tr></table>";
10779c4b2225SAlexander Belyaev     }
10789c4b2225SAlexander Belyaev   }
10799c4b2225SAlexander Belyaev 
10809c4b2225SAlexander Belyaev   os << "</div></td></tr>";
10819c4b2225SAlexander Belyaev 
10829c4b2225SAlexander Belyaev   // Insert the new html.
10839c4b2225SAlexander Belyaev   unsigned DisplayPos = LineEnd - FileStart;
10849c4b2225SAlexander Belyaev   SourceLocation Loc =
10859c4b2225SAlexander Belyaev     SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
10869c4b2225SAlexander Belyaev 
10879c4b2225SAlexander Belyaev   R.InsertTextBefore(Loc, os.str());
10889c4b2225SAlexander Belyaev 
10899c4b2225SAlexander Belyaev   // Now highlight the ranges.
10909c4b2225SAlexander Belyaev   ArrayRef<SourceRange> Ranges = P.getRanges();
10919c4b2225SAlexander Belyaev   for (const auto &Range : Ranges) {
10929c4b2225SAlexander Belyaev     // If we have already highlighted the range as a pop-up there is no work.
1093e567f37dSKazu Hirata     if (llvm::is_contained(PopUpRanges, Range))
10949c4b2225SAlexander Belyaev       continue;
10959c4b2225SAlexander Belyaev 
10969c4b2225SAlexander Belyaev     HighlightRange(R, LPosInfo.first, Range);
10979c4b2225SAlexander Belyaev   }
10989c4b2225SAlexander Belyaev }
10999c4b2225SAlexander Belyaev 
EmitAlphaCounter(raw_ostream & os,unsigned n)11009c4b2225SAlexander Belyaev static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
11019c4b2225SAlexander Belyaev   unsigned x = n % ('z' - 'a');
11029c4b2225SAlexander Belyaev   n /= 'z' - 'a';
11039c4b2225SAlexander Belyaev 
11049c4b2225SAlexander Belyaev   if (n > 0)
11059c4b2225SAlexander Belyaev     EmitAlphaCounter(os, n);
11069c4b2225SAlexander Belyaev 
11079c4b2225SAlexander Belyaev   os << char('a' + x);
11089c4b2225SAlexander Belyaev }
11099c4b2225SAlexander Belyaev 
ProcessMacroPiece(raw_ostream & os,const PathDiagnosticMacroPiece & P,unsigned num)11109c4b2225SAlexander Belyaev unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
11119c4b2225SAlexander Belyaev                                             const PathDiagnosticMacroPiece& P,
11129c4b2225SAlexander Belyaev                                             unsigned num) {
11139c4b2225SAlexander Belyaev   for (const auto &subPiece : P.subPieces) {
11149c4b2225SAlexander Belyaev     if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
11159c4b2225SAlexander Belyaev       num = ProcessMacroPiece(os, *MP, num);
11169c4b2225SAlexander Belyaev       continue;
11179c4b2225SAlexander Belyaev     }
11189c4b2225SAlexander Belyaev 
11199c4b2225SAlexander Belyaev     if (const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
11209c4b2225SAlexander Belyaev       os << "<div class=\"msg msgEvent\" style=\"width:94%; "
11219c4b2225SAlexander Belyaev             "margin-left:5px\">"
11229c4b2225SAlexander Belyaev             "<table class=\"msgT\"><tr>"
11239c4b2225SAlexander Belyaev             "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
11249c4b2225SAlexander Belyaev       EmitAlphaCounter(os, num++);
11259c4b2225SAlexander Belyaev       os << "</div></td><td valign=\"top\">"
11269c4b2225SAlexander Belyaev          << html::EscapeText(EP->getString())
11279c4b2225SAlexander Belyaev          << "</td></tr></table></div>\n";
11289c4b2225SAlexander Belyaev     }
11299c4b2225SAlexander Belyaev   }
11309c4b2225SAlexander Belyaev 
11319c4b2225SAlexander Belyaev   return num;
11329c4b2225SAlexander Belyaev }
11339c4b2225SAlexander Belyaev 
addArrowSVGs(Rewriter & R,FileID BugFileID,const ArrowMap & ArrowIndices)113497bcafa2SValeriy Savchenko void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,
11359e02f587SValeriy Savchenko                                    const ArrowMap &ArrowIndices) {
113697bcafa2SValeriy Savchenko   std::string S;
113797bcafa2SValeriy Savchenko   llvm::raw_string_ostream OS(S);
113897bcafa2SValeriy Savchenko 
113997bcafa2SValeriy Savchenko   OS << R"<<<(
114097bcafa2SValeriy Savchenko <style type="text/css">
114197bcafa2SValeriy Savchenko   svg {
114297bcafa2SValeriy Savchenko       position:absolute;
114397bcafa2SValeriy Savchenko       top:0;
114497bcafa2SValeriy Savchenko       left:0;
114597bcafa2SValeriy Savchenko       height:100%;
114697bcafa2SValeriy Savchenko       width:100%;
114797bcafa2SValeriy Savchenko       pointer-events: none;
114897bcafa2SValeriy Savchenko       overflow: visible
114997bcafa2SValeriy Savchenko   }
11509e02f587SValeriy Savchenko   .arrow {
11519e02f587SValeriy Savchenko       stroke-opacity: 0.2;
11529e02f587SValeriy Savchenko       stroke-width: 1;
11539e02f587SValeriy Savchenko       marker-end: url(#arrowhead);
11549e02f587SValeriy Savchenko   }
11559e02f587SValeriy Savchenko 
11569e02f587SValeriy Savchenko   .arrow.selected {
11579e02f587SValeriy Savchenko       stroke-opacity: 0.6;
11589e02f587SValeriy Savchenko       stroke-width: 2;
11599e02f587SValeriy Savchenko       marker-end: url(#arrowheadSelected);
11609e02f587SValeriy Savchenko   }
11619e02f587SValeriy Savchenko 
11629e02f587SValeriy Savchenko   .arrowhead {
11639e02f587SValeriy Savchenko       orient: auto;
11649e02f587SValeriy Savchenko       stroke: none;
11659e02f587SValeriy Savchenko       opacity: 0.6;
11669e02f587SValeriy Savchenko       fill: blue;
11679e02f587SValeriy Savchenko   }
116897bcafa2SValeriy Savchenko </style>
116997bcafa2SValeriy Savchenko <svg xmlns="http://www.w3.org/2000/svg">
117097bcafa2SValeriy Savchenko   <defs>
11719e02f587SValeriy Savchenko     <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
11729e02f587SValeriy Savchenko             viewBox="0 0 10 10" refX="3" refY="5"
11739e02f587SValeriy Savchenko             markerWidth="4" markerHeight="4">
11749e02f587SValeriy Savchenko       <path d="M 0 0 L 10 5 L 0 10 z" />
11759e02f587SValeriy Savchenko     </marker>
11769e02f587SValeriy Savchenko     <marker id="arrowhead" class="arrowhead" opacity="0.2"
11779e02f587SValeriy Savchenko             viewBox="0 0 10 10" refX="3" refY="5"
11789e02f587SValeriy Savchenko             markerWidth="4" markerHeight="4">
117997bcafa2SValeriy Savchenko       <path d="M 0 0 L 10 5 L 0 10 z" />
118097bcafa2SValeriy Savchenko     </marker>
118197bcafa2SValeriy Savchenko   </defs>
11829e02f587SValeriy Savchenko   <g id="arrows" fill="none" stroke="blue" visibility="hidden">
118397bcafa2SValeriy Savchenko )<<<";
118497bcafa2SValeriy Savchenko 
11859e02f587SValeriy Savchenko   for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
11869e02f587SValeriy Savchenko     OS << "    <path class=\"arrow\" id=\"arrow" << Index << "\"/>\n";
118797bcafa2SValeriy Savchenko   }
118897bcafa2SValeriy Savchenko 
118997bcafa2SValeriy Savchenko   OS << R"<<<(
119097bcafa2SValeriy Savchenko   </g>
119197bcafa2SValeriy Savchenko </svg>
11929e02f587SValeriy Savchenko <script type='text/javascript'>
11939e02f587SValeriy Savchenko const arrowIndices = )<<<";
11949e02f587SValeriy Savchenko 
11959e02f587SValeriy Savchenko   OS << ArrowIndices << "\n</script>\n";
119697bcafa2SValeriy Savchenko 
119797bcafa2SValeriy Savchenko   R.InsertTextBefore(R.getSourceMgr().getLocForStartOfFile(BugFileID),
119897bcafa2SValeriy Savchenko                      OS.str());
119997bcafa2SValeriy Savchenko }
120097bcafa2SValeriy Savchenko 
getSpanBeginForControl(const char * ClassName,unsigned Index)120197bcafa2SValeriy Savchenko std::string getSpanBeginForControl(const char *ClassName, unsigned Index) {
120297bcafa2SValeriy Savchenko   std::string Result;
120397bcafa2SValeriy Savchenko   llvm::raw_string_ostream OS(Result);
120497bcafa2SValeriy Savchenko   OS << "<span id=\"" << ClassName << Index << "\">";
1205715c72b4SLogan Smith   return Result;
120697bcafa2SValeriy Savchenko }
120797bcafa2SValeriy Savchenko 
getSpanBeginForControlStart(unsigned Index)120897bcafa2SValeriy Savchenko std::string getSpanBeginForControlStart(unsigned Index) {
120997bcafa2SValeriy Savchenko   return getSpanBeginForControl("start", Index);
121097bcafa2SValeriy Savchenko }
121197bcafa2SValeriy Savchenko 
getSpanBeginForControlEnd(unsigned Index)121297bcafa2SValeriy Savchenko std::string getSpanBeginForControlEnd(unsigned Index) {
121397bcafa2SValeriy Savchenko   return getSpanBeginForControl("end", Index);
121497bcafa2SValeriy Savchenko }
121597bcafa2SValeriy Savchenko 
ProcessControlFlowPiece(Rewriter & R,FileID BugFileID,const PathDiagnosticControlFlowPiece & P,unsigned Number)121697bcafa2SValeriy Savchenko unsigned HTMLDiagnostics::ProcessControlFlowPiece(
121797bcafa2SValeriy Savchenko     Rewriter &R, FileID BugFileID, const PathDiagnosticControlFlowPiece &P,
121897bcafa2SValeriy Savchenko     unsigned Number) {
121997bcafa2SValeriy Savchenko   for (const PathDiagnosticLocationPair &LPair : P) {
122097bcafa2SValeriy Savchenko     std::string Start = getSpanBeginForControlStart(Number),
122197bcafa2SValeriy Savchenko                 End = getSpanBeginForControlEnd(Number++);
122297bcafa2SValeriy Savchenko 
122397bcafa2SValeriy Savchenko     HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
122497bcafa2SValeriy Savchenko                    Start.c_str());
122597bcafa2SValeriy Savchenko     HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
122697bcafa2SValeriy Savchenko                    End.c_str());
122797bcafa2SValeriy Savchenko   }
122897bcafa2SValeriy Savchenko 
122997bcafa2SValeriy Savchenko   return Number;
123097bcafa2SValeriy Savchenko }
123197bcafa2SValeriy Savchenko 
HighlightRange(Rewriter & R,FileID BugFileID,SourceRange Range,const char * HighlightStart,const char * HighlightEnd)12329c4b2225SAlexander Belyaev void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
12339c4b2225SAlexander Belyaev                                      SourceRange Range,
12349c4b2225SAlexander Belyaev                                      const char *HighlightStart,
12359c4b2225SAlexander Belyaev                                      const char *HighlightEnd) {
12369c4b2225SAlexander Belyaev   SourceManager &SM = R.getSourceMgr();
12379c4b2225SAlexander Belyaev   const LangOptions &LangOpts = R.getLangOpts();
12389c4b2225SAlexander Belyaev 
12399c4b2225SAlexander Belyaev   SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin());
12409c4b2225SAlexander Belyaev   unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);
12419c4b2225SAlexander Belyaev 
12429c4b2225SAlexander Belyaev   SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd());
12439c4b2225SAlexander Belyaev   unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);
12449c4b2225SAlexander Belyaev 
12459c4b2225SAlexander Belyaev   if (EndLineNo < StartLineNo)
12469c4b2225SAlexander Belyaev     return;
12479c4b2225SAlexander Belyaev 
12489c4b2225SAlexander Belyaev   if (SM.getFileID(InstantiationStart) != BugFileID ||
12499c4b2225SAlexander Belyaev       SM.getFileID(InstantiationEnd) != BugFileID)
12509c4b2225SAlexander Belyaev     return;
12519c4b2225SAlexander Belyaev 
12529c4b2225SAlexander Belyaev   // Compute the column number of the end.
12539c4b2225SAlexander Belyaev   unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);
12549c4b2225SAlexander Belyaev   unsigned OldEndColNo = EndColNo;
12559c4b2225SAlexander Belyaev 
12569c4b2225SAlexander Belyaev   if (EndColNo) {
12579c4b2225SAlexander Belyaev     // Add in the length of the token, so that we cover multi-char tokens.
12589c4b2225SAlexander Belyaev     EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1;
12599c4b2225SAlexander Belyaev   }
12609c4b2225SAlexander Belyaev 
12619c4b2225SAlexander Belyaev   // Highlight the range.  Make the span tag the outermost tag for the
12629c4b2225SAlexander Belyaev   // selected range.
12639c4b2225SAlexander Belyaev 
12649c4b2225SAlexander Belyaev   SourceLocation E =
12659c4b2225SAlexander Belyaev     InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo);
12669c4b2225SAlexander Belyaev 
12679c4b2225SAlexander Belyaev   html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
12689c4b2225SAlexander Belyaev }
12699c4b2225SAlexander Belyaev 
generateKeyboardNavigationJavascript()12709c4b2225SAlexander Belyaev StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
12719c4b2225SAlexander Belyaev   return R"<<<(
12729c4b2225SAlexander Belyaev <script type='text/javascript'>
12739c4b2225SAlexander Belyaev var digitMatcher = new RegExp("[0-9]+");
12749c4b2225SAlexander Belyaev 
12759c4b2225SAlexander Belyaev var querySelectorAllArray = function(selector) {
12769c4b2225SAlexander Belyaev   return Array.prototype.slice.call(
12779c4b2225SAlexander Belyaev     document.querySelectorAll(selector));
12789c4b2225SAlexander Belyaev }
12799c4b2225SAlexander Belyaev 
12809c4b2225SAlexander Belyaev document.addEventListener("DOMContentLoaded", function() {
12819c4b2225SAlexander Belyaev     querySelectorAllArray(".PathNav > a").forEach(
12829c4b2225SAlexander Belyaev         function(currentValue, currentIndex) {
12839c4b2225SAlexander Belyaev             var hrefValue = currentValue.getAttribute("href");
12849c4b2225SAlexander Belyaev             currentValue.onclick = function() {
12859c4b2225SAlexander Belyaev                 scrollTo(document.querySelector(hrefValue));
12869c4b2225SAlexander Belyaev                 return false;
12879c4b2225SAlexander Belyaev             };
12889c4b2225SAlexander Belyaev         });
12899c4b2225SAlexander Belyaev });
12909c4b2225SAlexander Belyaev 
12919c4b2225SAlexander Belyaev var findNum = function() {
12929e02f587SValeriy Savchenko     var s = document.querySelector(".msg.selected");
12939c4b2225SAlexander Belyaev     if (!s || s.id == "EndPath") {
12949c4b2225SAlexander Belyaev         return 0;
12959c4b2225SAlexander Belyaev     }
12969c4b2225SAlexander Belyaev     var out = parseInt(digitMatcher.exec(s.id)[0]);
12979c4b2225SAlexander Belyaev     return out;
12989c4b2225SAlexander Belyaev };
12999c4b2225SAlexander Belyaev 
13009dabacd0SDenys Petrov var classListAdd = function(el, theClass) {
13019dabacd0SDenys Petrov   if(!el.className.baseVal)
13029dabacd0SDenys Petrov     el.className += " " + theClass;
13039dabacd0SDenys Petrov   else
13049dabacd0SDenys Petrov     el.className.baseVal += " " + theClass;
13059dabacd0SDenys Petrov };
13069dabacd0SDenys Petrov 
13079dabacd0SDenys Petrov var classListRemove = function(el, theClass) {
13089dabacd0SDenys Petrov   var className = (!el.className.baseVal) ?
13099dabacd0SDenys Petrov       el.className : el.className.baseVal;
13109dabacd0SDenys Petrov     className = className.replace(" " + theClass, "");
13119dabacd0SDenys Petrov   if(!el.className.baseVal)
13129dabacd0SDenys Petrov     el.className = className;
13139dabacd0SDenys Petrov   else
13149dabacd0SDenys Petrov     el.className.baseVal = className;
13159dabacd0SDenys Petrov };
13169dabacd0SDenys Petrov 
13179c4b2225SAlexander Belyaev var scrollTo = function(el) {
13189c4b2225SAlexander Belyaev     querySelectorAllArray(".selected").forEach(function(s) {
13199dabacd0SDenys Petrov       classListRemove(s, "selected");
13209c4b2225SAlexander Belyaev     });
13219dabacd0SDenys Petrov     classListAdd(el, "selected");
13229c4b2225SAlexander Belyaev     window.scrollBy(0, el.getBoundingClientRect().top -
13239c4b2225SAlexander Belyaev         (window.innerHeight / 2));
13249e02f587SValeriy Savchenko     highlightArrowsForSelectedEvent();
13259dabacd0SDenys Petrov };
13269c4b2225SAlexander Belyaev 
13279c4b2225SAlexander Belyaev var move = function(num, up, numItems) {
13289c4b2225SAlexander Belyaev   if (num == 1 && up || num == numItems - 1 && !up) {
13299c4b2225SAlexander Belyaev     return 0;
13309c4b2225SAlexander Belyaev   } else if (num == 0 && up) {
13319c4b2225SAlexander Belyaev     return numItems - 1;
13329c4b2225SAlexander Belyaev   } else if (num == 0 && !up) {
13339c4b2225SAlexander Belyaev     return 1 % numItems;
13349c4b2225SAlexander Belyaev   }
13359c4b2225SAlexander Belyaev   return up ? num - 1 : num + 1;
13369c4b2225SAlexander Belyaev }
13379c4b2225SAlexander Belyaev 
13389c4b2225SAlexander Belyaev var numToId = function(num) {
13399c4b2225SAlexander Belyaev   if (num == 0) {
13409c4b2225SAlexander Belyaev     return document.getElementById("EndPath")
13419c4b2225SAlexander Belyaev   }
13429c4b2225SAlexander Belyaev   return document.getElementById("Path" + num);
13439c4b2225SAlexander Belyaev };
13449c4b2225SAlexander Belyaev 
13459c4b2225SAlexander Belyaev var navigateTo = function(up) {
13469c4b2225SAlexander Belyaev   var numItems = document.querySelectorAll(
13479c4b2225SAlexander Belyaev       ".line > .msgEvent, .line > .msgControl").length;
13489c4b2225SAlexander Belyaev   var currentSelected = findNum();
13499c4b2225SAlexander Belyaev   var newSelected = move(currentSelected, up, numItems);
13509c4b2225SAlexander Belyaev   var newEl = numToId(newSelected, numItems);
13519c4b2225SAlexander Belyaev 
13529c4b2225SAlexander Belyaev   // Scroll element into center.
13539c4b2225SAlexander Belyaev   scrollTo(newEl);
13549c4b2225SAlexander Belyaev };
13559c4b2225SAlexander Belyaev 
13569c4b2225SAlexander Belyaev window.addEventListener("keydown", function (event) {
13579c4b2225SAlexander Belyaev   if (event.defaultPrevented) {
13589c4b2225SAlexander Belyaev     return;
13599c4b2225SAlexander Belyaev   }
13609dabacd0SDenys Petrov   // key 'j'
13619dabacd0SDenys Petrov   if (event.keyCode == 74) {
13629c4b2225SAlexander Belyaev     navigateTo(/*up=*/false);
13639dabacd0SDenys Petrov   // key 'k'
13649dabacd0SDenys Petrov   } else if (event.keyCode == 75) {
13659c4b2225SAlexander Belyaev     navigateTo(/*up=*/true);
13669c4b2225SAlexander Belyaev   } else {
13679c4b2225SAlexander Belyaev     return;
13689c4b2225SAlexander Belyaev   }
13699c4b2225SAlexander Belyaev   event.preventDefault();
13709c4b2225SAlexander Belyaev }, true);
13719c4b2225SAlexander Belyaev </script>
13729c4b2225SAlexander Belyaev   )<<<";
13739c4b2225SAlexander Belyaev }
137497bcafa2SValeriy Savchenko 
generateArrowDrawingJavascript()137597bcafa2SValeriy Savchenko StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
137697bcafa2SValeriy Savchenko   return R"<<<(
137797bcafa2SValeriy Savchenko <script type='text/javascript'>
13789e02f587SValeriy Savchenko // Return range of numbers from a range [lower, upper).
13799e02f587SValeriy Savchenko function range(lower, upper) {
13809dabacd0SDenys Petrov   var array = [];
13819dabacd0SDenys Petrov   for (var i = lower; i <= upper; ++i) {
13829dabacd0SDenys Petrov       array.push(i);
13839dabacd0SDenys Petrov   }
13849dabacd0SDenys Petrov   return array;
13859e02f587SValeriy Savchenko }
13869e02f587SValeriy Savchenko 
13879e02f587SValeriy Savchenko var getRelatedArrowIndices = function(pathId) {
13889e02f587SValeriy Savchenko   // HTML numeration of events is a bit different than it is in the path.
13899e02f587SValeriy Savchenko   // Everything is rotated one step to the right, so the last element
13909e02f587SValeriy Savchenko   // (error diagnostic) has index 0.
13919e02f587SValeriy Savchenko   if (pathId == 0) {
13929e02f587SValeriy Savchenko     // arrowIndices has at least 2 elements
13939e02f587SValeriy Savchenko     pathId = arrowIndices.length - 1;
13949e02f587SValeriy Savchenko   }
13959e02f587SValeriy Savchenko 
13969e02f587SValeriy Savchenko   return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
13979e02f587SValeriy Savchenko }
13989e02f587SValeriy Savchenko 
13999e02f587SValeriy Savchenko var highlightArrowsForSelectedEvent = function() {
14009e02f587SValeriy Savchenko   const selectedNum = findNum();
14019e02f587SValeriy Savchenko   const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
14029e02f587SValeriy Savchenko   arrowIndicesToHighlight.forEach((index) => {
14039e02f587SValeriy Savchenko     var arrow = document.querySelector("#arrow" + index);
14049dabacd0SDenys Petrov     if(arrow) {
14059dabacd0SDenys Petrov       classListAdd(arrow, "selected")
14069dabacd0SDenys Petrov     }
14079e02f587SValeriy Savchenko   });
14089e02f587SValeriy Savchenko }
14099e02f587SValeriy Savchenko 
141097bcafa2SValeriy Savchenko var getAbsoluteBoundingRect = function(element) {
141197bcafa2SValeriy Savchenko   const relative = element.getBoundingClientRect();
141297bcafa2SValeriy Savchenko   return {
141397bcafa2SValeriy Savchenko     left: relative.left + window.pageXOffset,
141497bcafa2SValeriy Savchenko     right: relative.right + window.pageXOffset,
141597bcafa2SValeriy Savchenko     top: relative.top + window.pageYOffset,
141697bcafa2SValeriy Savchenko     bottom: relative.bottom + window.pageYOffset,
141797bcafa2SValeriy Savchenko     height: relative.height,
141897bcafa2SValeriy Savchenko     width: relative.width
141997bcafa2SValeriy Savchenko   };
142097bcafa2SValeriy Savchenko }
142197bcafa2SValeriy Savchenko 
142297bcafa2SValeriy Savchenko var drawArrow = function(index) {
142397bcafa2SValeriy Savchenko   // This function is based on the great answer from SO:
142497bcafa2SValeriy Savchenko   //   https://stackoverflow.com/a/39575674/11582326
142597bcafa2SValeriy Savchenko   var start = document.querySelector("#start" + index);
142697bcafa2SValeriy Savchenko   var end   = document.querySelector("#end" + index);
142797bcafa2SValeriy Savchenko   var arrow = document.querySelector("#arrow" + index);
142897bcafa2SValeriy Savchenko 
142997bcafa2SValeriy Savchenko   var startRect = getAbsoluteBoundingRect(start);
143097bcafa2SValeriy Savchenko   var endRect   = getAbsoluteBoundingRect(end);
143197bcafa2SValeriy Savchenko 
143297bcafa2SValeriy Savchenko   // It is an arrow from a token to itself, no need to visualize it.
143397bcafa2SValeriy Savchenko   if (startRect.top == endRect.top &&
143497bcafa2SValeriy Savchenko       startRect.left == endRect.left)
143597bcafa2SValeriy Savchenko     return;
143697bcafa2SValeriy Savchenko 
143797bcafa2SValeriy Savchenko   // Each arrow is a very simple Bézier curve, with two nodes and
143897bcafa2SValeriy Savchenko   // two handles.  So, we need to calculate four points in the window:
143997bcafa2SValeriy Savchenko   //   * start node
144097bcafa2SValeriy Savchenko   var posStart    = { x: 0, y: 0 };
144197bcafa2SValeriy Savchenko   //   * end node
144297bcafa2SValeriy Savchenko   var posEnd      = { x: 0, y: 0 };
144397bcafa2SValeriy Savchenko   //   * handle for the start node
144497bcafa2SValeriy Savchenko   var startHandle = { x: 0, y: 0 };
144597bcafa2SValeriy Savchenko   //   * handle for the end node
144697bcafa2SValeriy Savchenko   var endHandle   = { x: 0, y: 0 };
144797bcafa2SValeriy Savchenko   // One can visualize it as follows:
144897bcafa2SValeriy Savchenko   //
144997bcafa2SValeriy Savchenko   //         start handle
145097bcafa2SValeriy Savchenko   //        /
145197bcafa2SValeriy Savchenko   //       X"""_.-""""X
145297bcafa2SValeriy Savchenko   //         .'        \
145397bcafa2SValeriy Savchenko   //        /           start node
145497bcafa2SValeriy Savchenko   //       |
145597bcafa2SValeriy Savchenko   //       |
145697bcafa2SValeriy Savchenko   //       |      end node
145797bcafa2SValeriy Savchenko   //        \    /
145897bcafa2SValeriy Savchenko   //         `->X
145997bcafa2SValeriy Savchenko   //        X-'
146097bcafa2SValeriy Savchenko   //         \
146197bcafa2SValeriy Savchenko   //          end handle
146297bcafa2SValeriy Savchenko   //
146397bcafa2SValeriy Savchenko   // NOTE: (0, 0) is the top left corner of the window.
146497bcafa2SValeriy Savchenko 
146597bcafa2SValeriy Savchenko   // We have 3 similar, but still different scenarios to cover:
146697bcafa2SValeriy Savchenko   //
146797bcafa2SValeriy Savchenko   //   1. Two tokens on different lines.
146897bcafa2SValeriy Savchenko   //             -xxx
146997bcafa2SValeriy Savchenko   //           /
147097bcafa2SValeriy Savchenko   //           \
147197bcafa2SValeriy Savchenko   //             -> xxx
147297bcafa2SValeriy Savchenko   //      In this situation, we draw arrow on the left curving to the left.
147397bcafa2SValeriy Savchenko   //   2. Two tokens on the same line, and the destination is on the right.
147497bcafa2SValeriy Savchenko   //             ____
147597bcafa2SValeriy Savchenko   //            /    \
147697bcafa2SValeriy Savchenko   //           /      V
147797bcafa2SValeriy Savchenko   //        xxx        xxx
147897bcafa2SValeriy Savchenko   //      In this situation, we draw arrow above curving upwards.
147997bcafa2SValeriy Savchenko   //   3. Two tokens on the same line, and the destination is on the left.
148097bcafa2SValeriy Savchenko   //        xxx        xxx
148197bcafa2SValeriy Savchenko   //           ^      /
148297bcafa2SValeriy Savchenko   //            \____/
148397bcafa2SValeriy Savchenko   //      In this situation, we draw arrow below curving downwards.
148497bcafa2SValeriy Savchenko   const onDifferentLines = startRect.top <= endRect.top - 5 ||
148597bcafa2SValeriy Savchenko     startRect.top >= endRect.top + 5;
148697bcafa2SValeriy Savchenko   const leftToRight = startRect.left < endRect.left;
148797bcafa2SValeriy Savchenko 
148897bcafa2SValeriy Savchenko   // NOTE: various magic constants are chosen empirically for
148997bcafa2SValeriy Savchenko   //       better positioning and look
149097bcafa2SValeriy Savchenko   if (onDifferentLines) {
149197bcafa2SValeriy Savchenko     // Case #1
149297bcafa2SValeriy Savchenko     const topToBottom = startRect.top < endRect.top;
149397bcafa2SValeriy Savchenko     posStart.x = startRect.left - 1;
149497bcafa2SValeriy Savchenko     // We don't want to start it at the top left corner of the token,
149597bcafa2SValeriy Savchenko     // it doesn't feel like this is where the arrow comes from.
149697bcafa2SValeriy Savchenko     // For this reason, we start it in the middle of the left side
149797bcafa2SValeriy Savchenko     // of the token.
149897bcafa2SValeriy Savchenko     posStart.y = startRect.top + startRect.height / 2;
149997bcafa2SValeriy Savchenko 
150097bcafa2SValeriy Savchenko     // End node has arrow head and we give it a bit more space.
150197bcafa2SValeriy Savchenko     posEnd.x = endRect.left - 4;
150297bcafa2SValeriy Savchenko     posEnd.y = endRect.top;
150397bcafa2SValeriy Savchenko 
150497bcafa2SValeriy Savchenko     // Utility object with x and y offsets for handles.
150597bcafa2SValeriy Savchenko     var curvature = {
150697bcafa2SValeriy Savchenko       // We want bottom-to-top arrow to curve a bit more, so it doesn't
150797bcafa2SValeriy Savchenko       // overlap much with top-to-bottom curves (much more frequent).
150897bcafa2SValeriy Savchenko       x: topToBottom ? 15 : 25,
150997bcafa2SValeriy Savchenko       y: Math.min((posEnd.y - posStart.y) / 3, 10)
151097bcafa2SValeriy Savchenko     }
151197bcafa2SValeriy Savchenko 
151297bcafa2SValeriy Savchenko     // When destination is on the different line, we can make a
151397bcafa2SValeriy Savchenko     // curvier arrow because we have space for it.
151497bcafa2SValeriy Savchenko     // So, instead of using
151597bcafa2SValeriy Savchenko     //
151697bcafa2SValeriy Savchenko     //   startHandle.x = posStart.x - curvature.x
151797bcafa2SValeriy Savchenko     //   endHandle.x   = posEnd.x - curvature.x
151897bcafa2SValeriy Savchenko     //
151997bcafa2SValeriy Savchenko     // We use the leftmost of these two values for both handles.
152097bcafa2SValeriy Savchenko     startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
152197bcafa2SValeriy Savchenko     endHandle.x = startHandle.x;
152297bcafa2SValeriy Savchenko 
152397bcafa2SValeriy Savchenko     // Curving downwards from the start node...
152497bcafa2SValeriy Savchenko     startHandle.y = posStart.y + curvature.y;
152597bcafa2SValeriy Savchenko     // ... and upwards from the end node.
152697bcafa2SValeriy Savchenko     endHandle.y = posEnd.y - curvature.y;
152797bcafa2SValeriy Savchenko 
152897bcafa2SValeriy Savchenko   } else if (leftToRight) {
152997bcafa2SValeriy Savchenko     // Case #2
153097bcafa2SValeriy Savchenko     // Starting from the top right corner...
153197bcafa2SValeriy Savchenko     posStart.x = startRect.right - 1;
153297bcafa2SValeriy Savchenko     posStart.y = startRect.top;
153397bcafa2SValeriy Savchenko 
153497bcafa2SValeriy Savchenko     // ...and ending at the top left corner of the end token.
153597bcafa2SValeriy Savchenko     posEnd.x = endRect.left + 1;
153697bcafa2SValeriy Savchenko     posEnd.y = endRect.top - 1;
153797bcafa2SValeriy Savchenko 
153897bcafa2SValeriy Savchenko     // Utility object with x and y offsets for handles.
153997bcafa2SValeriy Savchenko     var curvature = {
154097bcafa2SValeriy Savchenko       x: Math.min((posEnd.x - posStart.x) / 3, 15),
154197bcafa2SValeriy Savchenko       y: 5
154297bcafa2SValeriy Savchenko     }
154397bcafa2SValeriy Savchenko 
154497bcafa2SValeriy Savchenko     // Curving to the right...
154597bcafa2SValeriy Savchenko     startHandle.x = posStart.x + curvature.x;
154697bcafa2SValeriy Savchenko     // ... and upwards from the start node.
154797bcafa2SValeriy Savchenko     startHandle.y = posStart.y - curvature.y;
154897bcafa2SValeriy Savchenko 
154997bcafa2SValeriy Savchenko     // And to the left...
155097bcafa2SValeriy Savchenko     endHandle.x = posEnd.x - curvature.x;
155197bcafa2SValeriy Savchenko     // ... and upwards from the end node.
155297bcafa2SValeriy Savchenko     endHandle.y = posEnd.y - curvature.y;
155397bcafa2SValeriy Savchenko 
155497bcafa2SValeriy Savchenko   } else {
155597bcafa2SValeriy Savchenko     // Case #3
155697bcafa2SValeriy Savchenko     // Starting from the bottom right corner...
155797bcafa2SValeriy Savchenko     posStart.x = startRect.right;
155897bcafa2SValeriy Savchenko     posStart.y = startRect.bottom;
155997bcafa2SValeriy Savchenko 
156097bcafa2SValeriy Savchenko     // ...and ending also at the bottom right corner, but of the end token.
156197bcafa2SValeriy Savchenko     posEnd.x = endRect.right - 1;
156297bcafa2SValeriy Savchenko     posEnd.y = endRect.bottom + 1;
156397bcafa2SValeriy Savchenko 
156497bcafa2SValeriy Savchenko     // Utility object with x and y offsets for handles.
156597bcafa2SValeriy Savchenko     var curvature = {
156697bcafa2SValeriy Savchenko       x: Math.min((posStart.x - posEnd.x) / 3, 15),
156797bcafa2SValeriy Savchenko       y: 5
156897bcafa2SValeriy Savchenko     }
156997bcafa2SValeriy Savchenko 
157097bcafa2SValeriy Savchenko     // Curving to the left...
157197bcafa2SValeriy Savchenko     startHandle.x = posStart.x - curvature.x;
157297bcafa2SValeriy Savchenko     // ... and downwards from the start node.
157397bcafa2SValeriy Savchenko     startHandle.y = posStart.y + curvature.y;
157497bcafa2SValeriy Savchenko 
157597bcafa2SValeriy Savchenko     // And to the right...
157697bcafa2SValeriy Savchenko     endHandle.x = posEnd.x + curvature.x;
157797bcafa2SValeriy Savchenko     // ... and downwards from the end node.
157897bcafa2SValeriy Savchenko     endHandle.y = posEnd.y + curvature.y;
157997bcafa2SValeriy Savchenko   }
158097bcafa2SValeriy Savchenko 
158197bcafa2SValeriy Savchenko   // Put it all together into a path.
158297bcafa2SValeriy Savchenko   // More information on the format:
158397bcafa2SValeriy Savchenko   //   https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
158497bcafa2SValeriy Savchenko   var pathStr = "M" + posStart.x + "," + posStart.y + " " +
158597bcafa2SValeriy Savchenko     "C" + startHandle.x + "," + startHandle.y + " " +
158697bcafa2SValeriy Savchenko     endHandle.x + "," + endHandle.y + " " +
158797bcafa2SValeriy Savchenko     posEnd.x + "," + posEnd.y;
158897bcafa2SValeriy Savchenko 
158997bcafa2SValeriy Savchenko   arrow.setAttribute("d", pathStr);
159097bcafa2SValeriy Savchenko };
159197bcafa2SValeriy Savchenko 
159297bcafa2SValeriy Savchenko var drawArrows = function() {
159397bcafa2SValeriy Savchenko   const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
159497bcafa2SValeriy Savchenko   for (var i = 0; i < numOfArrows; ++i) {
159597bcafa2SValeriy Savchenko     drawArrow(i);
159697bcafa2SValeriy Savchenko   }
159797bcafa2SValeriy Savchenko }
159897bcafa2SValeriy Savchenko 
159997bcafa2SValeriy Savchenko var toggleArrows = function(event) {
160097bcafa2SValeriy Savchenko   const arrows = document.querySelector("#arrows");
160197bcafa2SValeriy Savchenko   if (event.target.checked) {
160297bcafa2SValeriy Savchenko     arrows.setAttribute("visibility", "visible");
160397bcafa2SValeriy Savchenko   } else {
160497bcafa2SValeriy Savchenko     arrows.setAttribute("visibility", "hidden");
160597bcafa2SValeriy Savchenko   }
160697bcafa2SValeriy Savchenko }
160797bcafa2SValeriy Savchenko 
160897bcafa2SValeriy Savchenko window.addEventListener("resize", drawArrows);
160997bcafa2SValeriy Savchenko document.addEventListener("DOMContentLoaded", function() {
161097bcafa2SValeriy Savchenko   // Whenever we show invocation, locations change, i.e. we
161197bcafa2SValeriy Savchenko   // need to redraw arrows.
161297bcafa2SValeriy Savchenko   document
161397bcafa2SValeriy Savchenko     .querySelector('input[id="showinvocation"]')
161497bcafa2SValeriy Savchenko     .addEventListener("click", drawArrows);
161597bcafa2SValeriy Savchenko   // Hiding irrelevant lines also should cause arrow rerender.
161697bcafa2SValeriy Savchenko   document
161797bcafa2SValeriy Savchenko     .querySelector('input[name="showCounterexample"]')
161897bcafa2SValeriy Savchenko     .addEventListener("change", drawArrows);
161997bcafa2SValeriy Savchenko   document
162097bcafa2SValeriy Savchenko     .querySelector('input[name="showArrows"]')
162197bcafa2SValeriy Savchenko     .addEventListener("change", toggleArrows);
162297bcafa2SValeriy Savchenko   drawArrows();
16239e02f587SValeriy Savchenko   // Default highlighting for the last event.
16249e02f587SValeriy Savchenko   highlightArrowsForSelectedEvent();
162597bcafa2SValeriy Savchenko });
162697bcafa2SValeriy Savchenko </script>
162797bcafa2SValeriy Savchenko   )<<<";
162897bcafa2SValeriy Savchenko }
1629