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 << "\">←</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 << "\">→</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 << ")\">←</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 << ")\">→</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 << ")\">→</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