diff --git a/clang/lib/Rewrite/HTMLRewrite.cpp b/clang/lib/Rewrite/HTMLRewrite.cpp --- a/clang/lib/Rewrite/HTMLRewrite.cpp +++ b/clang/lib/Rewrite/HTMLRewrite.cpp @@ -392,7 +392,7 @@ .CodeInsertionHint { font-weight: bold; background-color: #10dd10 } .CodeRemovalHint { background-color:#de1010 } .CodeRemovalHint { border-bottom:1px solid #6F9DBE } -.selected{ background-color:orange !important; } +.msg.selected{ background-color:orange !important; } table.simpletable { padding: 5px; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -27,6 +27,7 @@ #include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -58,6 +59,8 @@ namespace { +class ArrowMap; + class HTMLDiagnostics : public PathDiagnosticConsumer { PathDiagnosticConsumerOptions DiagOpts; std::string Directory; @@ -119,7 +122,8 @@ } private: - void addArrowSVGs(Rewriter &R, FileID BugFileID, unsigned NumberOfArrows); + void addArrowSVGs(Rewriter &R, FileID BugFileID, + const ArrowMap &ArrowIndices); /// \return Javascript for displaying shortcuts help; StringRef showHelpJavascript(); @@ -150,6 +154,20 @@ return TotalPieces - TotalArrowPieces; } +class ArrowMap : public std::vector { + using Base = std::vector; + +public: + ArrowMap(unsigned Size) : Base(Size, 0) {} + unsigned getTotalNumberOfArrows() const { return at(0); } +}; + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) { + OS << "[ "; + llvm::interleave(Indices, OS, ","); + return OS << " ]"; +} + } // namespace void ento::createHTMLDiagnosticConsumer( @@ -761,6 +779,7 @@ unsigned NumberOfArrows = 0; // Stores the count of the regular piece indices. std::map IndexMap; + ArrowMap ArrowIndices(TotalRegularPieces + 1); // Stores the different ranges where we have reported something. std::vector PopUpRanges; @@ -779,13 +798,30 @@ } else if (isArrowPiece(Piece)) { NumberOfArrows = ProcessControlFlowPiece( R, FID, cast(Piece), NumberOfArrows); + ArrowIndices[NumRegularPieces] = NumberOfArrows; } else { HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces, TotalRegularPieces); --NumRegularPieces; + ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1]; } } + ArrowIndices[0] = NumberOfArrows; + + // At this point ArrowIndices represent the following data structure: + // [a_0, a_1, ..., a_N] + // where N is the number of events in the path. + // + // Then for every event with index i \in [0, N - 1], we can say that + // arrows with indices \in [a_(i+1), a_i) correspond to that event. + // We can say that because arrows with these indices appeared in the + // path in between the i-th and the (i+1)-th events. + assert(ArrowIndices.back() == 0 && + "No arrows should be after the last event"); + // This assertion also guarantees that all indices in are <= NumberOfArrows. + assert(llvm::is_sorted(ArrowIndices, std::greater()) && + "Incorrect arrow indices map"); // Secondary indexing if we are having multiple pop-ups between two notes. // (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...) @@ -819,7 +855,7 @@ html::EscapeText(R, FID); html::AddLineNumbers(R, FID); - addArrowSVGs(R, FID, NumberOfArrows); + addArrowSVGs(R, FID, ArrowIndices); // If we have a preprocessor, relex the file and syntax highlight. // We might not have a preprocessor if we come from a deserialized AST file, @@ -1088,7 +1124,7 @@ } void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID, - unsigned NumberOfArrows) { + const ArrowMap &ArrowIndices) { std::string S; llvm::raw_string_ostream OS(S); @@ -1103,27 +1139,52 @@ pointer-events: none; overflow: visible } + .arrow { + stroke-opacity: 0.2; + stroke-width: 1; + marker-end: url(#arrowhead); + } + + .arrow.selected { + stroke-opacity: 0.6; + stroke-width: 2; + marker-end: url(#arrowheadSelected); + } + + .arrowhead { + orient: auto; + stroke: none; + opacity: 0.6; + fill: blue; + } - + + + + - + )<<<"; - for (unsigned Index : llvm::seq(0u, NumberOfArrows)) { - OS << " \n"; + for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) { + OS << " \n"; } OS << R"<<<( -)<<<"; +\n"; R.InsertTextBefore(R.getSourceMgr().getLocForStartOfFile(BugFileID), OS.str()); @@ -1220,7 +1281,7 @@ }); var findNum = function() { - var s = document.querySelector(".selected"); + var s = document.querySelector(".msg.selected"); if (!s || s.id == "EndPath") { return 0; } @@ -1235,6 +1296,7 @@ el.classList.add("selected"); window.scrollBy(0, el.getBoundingClientRect().top - (window.innerHeight / 2)); + highlightArrowsForSelectedEvent(); } var move = function(num, up, numItems) { @@ -1286,6 +1348,33 @@ StringRef HTMLDiagnostics::generateArrowDrawingJavascript() { return R"<<<( )<<<"; diff --git a/clang/test/Analysis/html_diagnostics/control-arrows.cpp b/clang/test/Analysis/html_diagnostics/control-arrows.cpp --- a/clang/test/Analysis/html_diagnostics/control-arrows.cpp +++ b/clang/test/Analysis/html_diagnostics/control-arrows.cpp @@ -16,10 +16,13 @@ // CHECK: -// CHECK-NOT: +// CHECK-COUNT-9: +// CHECK-NOT: // CHECK: // CHECK-NEXT: +// CHECK-NEXT: // // Except for arrows we still want to have grey bubbles with control notes. // CHECK: