summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp21
-rw-r--r--clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp124
-rw-r--r--clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp21
-rw-r--r--clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp34
-rw-r--r--clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp11
5 files changed, 185 insertions, 26 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 738ad9a062f..6627633f393 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -154,8 +154,6 @@ static void removeRedundantMsgs(PathPieces &path) {
case PathDiagnosticPiece::Macro:
removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces);
break;
- case PathDiagnosticPiece::ControlFlow:
- break;
case PathDiagnosticPiece::Event: {
if (i == N-1)
break;
@@ -175,7 +173,9 @@ static void removeRedundantMsgs(PathPieces &path) {
}
break;
}
+ case PathDiagnosticPiece::ControlFlow:
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
break;
}
path.push_back(std::move(piece));
@@ -230,9 +230,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
break;
}
case PathDiagnosticPiece::ControlFlow:
- break;
-
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
break;
}
@@ -242,6 +241,16 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
return containsSomethingInteresting;
}
+/// Same logic as above to remove extra pieces.
+static void removePopUpNotes(PathPieces &Path) {
+ for (unsigned int i = 0; i < Path.size(); ++i) {
+ auto Piece = std::move(Path.front());
+ Path.pop_front();
+ if (!isa<PathDiagnosticPopUpPiece>(*Piece))
+ Path.push_back(std::move(Piece));
+ }
+}
+
/// Returns true if the given decl has been implicitly given a body, either by
/// the analyzer or by the compiler proper.
static bool hasImplicitBody(const Decl *D) {
@@ -1981,6 +1990,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer(
(void)stillHasNotes;
}
+ // Remove pop-up notes if needed.
+ if (!Opts.ShouldAddPopUpNotes)
+ removePopUpNotes(PD->getMutablePieces());
+
// Redirect all call pieces to have valid locations.
adjustCallLocations(PD->getMutablePieces());
removePiecesWithInvalidLocations(PD->getMutablePieces());
diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 79aaae8cbb3..8ede3f15e60 100644
--- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -90,8 +90,9 @@ public:
const PathDiagnosticMacroPiece& P,
unsigned num);
- void HandlePiece(Rewriter& R, FileID BugFileID,
- const PathDiagnosticPiece& P, unsigned num, unsigned max);
+ void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P,
+ const std::vector<SourceRange> &PopUpRanges, unsigned num,
+ unsigned max);
void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
const char *HighlightStart = "<span class=\"mrange\">",
@@ -605,6 +606,53 @@ window.addEventListener("keydown", function (event) {
)<<<";
}
+static void
+HandlePopUpPieceStartTag(Rewriter &R,
+ const std::vector<SourceRange> &PopUpRanges) {
+ for (const auto &Range : PopUpRanges) {
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "",
+ "<table class='variable_popup'><tbody>",
+ /*IsTokenRange=*/true);
+ }
+}
+
+static void HandlePopUpPieceEndTag(Rewriter &R,
+ const PathDiagnosticPopUpPiece &Piece,
+ std::vector<SourceRange> &PopUpRanges,
+ unsigned int LastReportedPieceIndex,
+ unsigned int PopUpPieceIndex) {
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ SourceRange Range(Piece.getLocation().asRange());
+
+ // Write out the path indices with a right arrow and the message as a row.
+ Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
+ << LastReportedPieceIndex;
+
+ // Also annotate the state transition with extra indices.
+ Out << '.' << PopUpPieceIndex;
+
+ Out << "</div></td><td>" << Piece.getString() << "</td></tr>";
+
+ // If no report made at this range mark the variable and add the end tags.
+ if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) ==
+ PopUpRanges.end()) {
+ // Store that we create a report at this range.
+ PopUpRanges.push_back(Range);
+
+ Out << "</tbody></table></span>";
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(),
+ "<span class='variable'>", Buf.c_str(),
+ /*IsTokenRange=*/true);
+
+ // Otherwise inject just the new row at the end of the range.
+ } else {
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(),
+ /*IsTokenRange=*/true);
+ }
+}
+
void HTMLDiagnostics::RewriteFile(Rewriter &R,
const PathPieces& path, FileID FID) {
// Process the path.
@@ -615,39 +663,80 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
[](const std::shared_ptr<PathDiagnosticPiece> &p) {
return isa<PathDiagnosticNotePiece>(*p);
});
+ unsigned PopUpPieceCount =
+ std::count_if(path.begin(), path.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &p) {
+ return isa<PathDiagnosticPopUpPiece>(*p);
+ });
- unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
+ unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
unsigned NumRegularPieces = TotalRegularPieces;
unsigned NumNotePieces = TotalNotePieces;
+ // Stores the count of the regular piece indices.
+ std::map<int, int> IndexMap;
+ // Stores the different ranges where we have reported something.
+ std::vector<SourceRange> PopUpRanges;
for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- if (isa<PathDiagnosticNotePiece>(I->get())) {
+ const auto &Piece = *I->get();
+
+ if (isa<PathDiagnosticPopUpPiece>(Piece)) {
+ ++IndexMap[NumRegularPieces];
+ } else if (isa<PathDiagnosticNotePiece>(Piece)) {
// This adds diagnostic bubbles, but not navigation.
// Navigation through note pieces would be added later,
// as a separate pass through the piece list.
- HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces);
+ HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
--NumNotePieces;
} else {
- HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces);
+ HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
+ TotalRegularPieces);
--NumRegularPieces;
}
}
- // Add line numbers, header, footer, etc.
+ // 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...)
+ NumRegularPieces = TotalRegularPieces;
+ for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
+ const auto &Piece = *I->get();
+
+ if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
+ int PopUpPieceIndex = IndexMap[NumRegularPieces];
+
+ // Pop-up pieces needs the index of the last reported piece and its count
+ // how many times we report to handle multiple reports on the same range.
+ // This marks the variable, adds the </table> end tag and the message
+ // (list element) as a row. The <table> start tag will be added after the
+ // rows has been written out. Note: It stores every different range.
+ HandlePopUpPieceEndTag(R, *PopUpP, PopUpRanges, NumRegularPieces,
+ PopUpPieceIndex);
+
+ if (PopUpPieceIndex > 0)
+ --IndexMap[NumRegularPieces];
+
+ } else if (!isa<PathDiagnosticNotePiece>(Piece)) {
+ --NumRegularPieces;
+ }
+ }
+ // Add the <table> start tag of pop-up pieces based on the stored ranges.
+ HandlePopUpPieceStartTag(R, PopUpRanges);
+
+ // Add line numbers, header, footer, etc.
html::EscapeText(R, FID);
html::AddLineNumbers(R, FID);
// 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,
// for example.
-
html::SyntaxHighlight(R, FID, PP);
html::HighlightMacros(R, FID, PP);
}
-void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
- const PathDiagnosticPiece& P,
+void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
+ const PathDiagnosticPiece &P,
+ const std::vector<SourceRange> &PopUpRanges,
unsigned num, unsigned max) {
// For now, just draw a box above the line in question, and emit the
// warning.
@@ -689,9 +778,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
bool IsNote = false;
bool SuppressIndex = (max == 1);
switch (P.getKind()) {
- case PathDiagnosticPiece::Call:
- llvm_unreachable("Calls and extra notes should already be handled");
- case PathDiagnosticPiece::Event: Kind = "Event"; break;
+ case PathDiagnosticPiece::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
// Setting Kind to "Control" is intentional.
case PathDiagnosticPiece::Macro: Kind = "Control"; break;
@@ -700,6 +787,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
IsNote = true;
SuppressIndex = true;
break;
+ case PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::PopUp:
+ llvm_unreachable("Calls and extra notes should already be handled");
}
std::string sbuf;
@@ -859,8 +949,14 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Now highlight the ranges.
ArrayRef<SourceRange> Ranges = P.getRanges();
- for (const auto &Range : Ranges)
+ for (const auto &Range : Ranges) {
+ // If we have already highlighted the range as a pop-up there is no work.
+ if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) !=
+ PopUpRanges.end())
+ continue;
+
HighlightRange(R, LPosInfo.first, Range);
+ }
}
static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
diff --git a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 5889a979661..1f642064827 100644
--- a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -90,6 +90,8 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;
PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;
+PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default;
+
void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
bool ShouldFlattenMacros) const {
for (auto &Piece : *this) {
@@ -119,6 +121,7 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
case PathDiagnosticPiece::Event:
case PathDiagnosticPiece::ControlFlow:
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
Current.push_back(Piece);
break;
}
@@ -369,15 +372,16 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
case PathDiagnosticPiece::ControlFlow:
return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),
cast<PathDiagnosticControlFlowPiece>(Y));
- case PathDiagnosticPiece::Event:
- case PathDiagnosticPiece::Note:
- return None;
case PathDiagnosticPiece::Macro:
return compareMacro(cast<PathDiagnosticMacroPiece>(X),
cast<PathDiagnosticMacroPiece>(Y));
case PathDiagnosticPiece::Call:
return compareCall(cast<PathDiagnosticCallPiece>(X),
cast<PathDiagnosticCallPiece>(Y));
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
+ return None;
}
llvm_unreachable("all cases handled");
}
@@ -1287,6 +1291,10 @@ void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticSpotPiece::Profile(ID);
}
+void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticSpotPiece::Profile(ID);
+}
+
void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
ID.Add(getLocation());
ID.AddString(BugType);
@@ -1412,6 +1420,13 @@ LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
getLocation().dump();
}
+LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const {
+ llvm::errs() << "POP-UP\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
if (!isValid()) {
llvm::errs() << "<INVALID>\n";
diff --git a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index c03bab0fe16..231db1fa16f 100644
--- a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -120,6 +120,9 @@ private:
case PathDiagnosticPiece::Note:
ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
break;
+ case PathDiagnosticPiece::PopUp:
+ ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent);
+ break;
}
}
@@ -138,6 +141,9 @@ private:
unsigned indent, unsigned depth);
void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
unsigned indent);
+
+ void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P,
+ unsigned indent);
};
} // end of anonymous namespace
@@ -397,6 +403,34 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
Indent(o, indent); o << "</dict>\n";
}
+void PlistPrinter::ReportPopUp(raw_ostream &o,
+ const PathDiagnosticPopUpPiece &P,
+ unsigned indent) {
+ const SourceManager &SM = PP.getSourceManager();
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n";
+
+ // Output the location.
+ FullSourceLoc L = P.getLocation().asLocation();
+
+ Indent(o, indent) << "<key>location</key>\n";
+ EmitLocation(o, SM, L, FM, indent);
+
+ // Output the ranges (if any).
+ ArrayRef<SourceRange> Ranges = P.getRanges();
+ EmitRanges(o, Ranges, indent);
+
+ // Output the text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent) << "</dict>\n";
+}
+
//===----------------------------------------------------------------------===//
// Static function definitions.
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index a8f529b7d35..4233f25edb3 100644
--- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -191,15 +191,16 @@ static json::Object createLocation(json::Object &&PhysicalLocation,
static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
switch (Piece.getKind()) {
- case PathDiagnosticPiece::Kind::Call:
- case PathDiagnosticPiece::Kind::Macro:
- case PathDiagnosticPiece::Kind::Note:
+ case PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::Macro:
+ case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
// FIXME: What should be reported here?
break;
- case PathDiagnosticPiece::Kind::Event:
+ case PathDiagnosticPiece::Event:
return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important
: Importance::Essential;
- case PathDiagnosticPiece::Kind::ControlFlow:
+ case PathDiagnosticPiece::ControlFlow:
return Importance::Unimportant;
}
return Importance::Unimportant;
OpenPOWER on IntegriCloud