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
@@ -26,6 +26,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"
@@ -57,6 +58,8 @@
namespace {
+class ArrowMap;
+
class HTMLDiagnostics : public PathDiagnosticConsumer {
PathDiagnosticConsumerOptions DiagOpts;
std::string Directory;
@@ -122,7 +125,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();
@@ -154,6 +158,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(
@@ -744,6 +762,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;
@@ -762,13 +781,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...)
@@ -802,7 +838,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,
@@ -1071,7 +1107,7 @@
}
void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,
- unsigned NumberOfArrows) {
+ const ArrowMap &ArrowIndices) {
std::string S;
llvm::raw_string_ostream OS(S);
@@ -1086,27 +1122,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;
+ }
-)<<<";
+\n";
R.InsertTextBefore(R.getSourceMgr().getLocForStartOfFile(BugFileID),
OS.str());
@@ -1203,7 +1264,7 @@
});
var findNum = function() {
- var s = document.querySelector(".selected");
+ var s = document.querySelector(".msg.selected");
if (!s || s.id == "EndPath") {
return 0;
}
@@ -1218,6 +1279,7 @@
el.classList.add("selected");
window.scrollBy(0, el.getBoundingClientRect().top -
(window.innerHeight / 2));
+ highlightArrowsForSelectedEvent();
}
var move = function(num, up, numItems) {
@@ -1269,6 +1331,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: