aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core/PlistDiagnostics.cpp')
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp142
1 files changed, 112 insertions, 30 deletions
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 66812ed8ff5b..cfe780db9ec9 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -16,9 +16,12 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/HTMLRewrite.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
using namespace clang;
@@ -30,6 +33,7 @@ namespace {
const std::string OutputFile;
const LangOptions &LangOpts;
const bool SupportsCrossFileDiagnostics;
+ const bool SerializeStatistics;
public:
PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
const std::string& prefix,
@@ -61,7 +65,8 @@ PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
bool supportsMultipleFiles)
: OutputFile(output),
LangOpts(LO),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
+ SupportsCrossFileDiagnostics(supportsMultipleFiles),
+ SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {}
void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
PathDiagnosticConsumers &C,
@@ -79,6 +84,41 @@ void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
PP.getLangOpts(), true));
}
+static void EmitRanges(raw_ostream &o,
+ const ArrayRef<SourceRange> Ranges,
+ const FIDMap& FM,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent) {
+
+ if (Ranges.empty())
+ return;
+
+ Indent(o, indent) << "<key>ranges</key>\n";
+ Indent(o, indent) << "<array>\n";
+ ++indent;
+ for (auto &R : Ranges)
+ EmitRange(o, SM,
+ Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
+ FM, indent + 1);
+ --indent;
+ Indent(o, indent) << "</array>\n";
+}
+
+static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) {
+ // Output the text.
+ assert(!Message.empty());
+ Indent(o, indent) << "<key>extended_message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+
+ // Output the short text.
+ // FIXME: Really use a short string.
+ Indent(o, indent) << "<key>message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+}
+
static void ReportControlFlow(raw_ostream &o,
const PathDiagnosticControlFlowPiece& P,
const FIDMap& FM,
@@ -133,7 +173,7 @@ static void ReportControlFlow(raw_ostream &o,
Indent(o, indent) << "</dict>\n";
}
-static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
+static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
const FIDMap& FM,
const SourceManager &SM,
const LangOptions &LangOpts,
@@ -158,34 +198,14 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
// Output the ranges (if any).
ArrayRef<SourceRange> Ranges = P.getRanges();
-
- if (!Ranges.empty()) {
- Indent(o, indent) << "<key>ranges</key>\n";
- Indent(o, indent) << "<array>\n";
- ++indent;
- for (auto &R : Ranges)
- EmitRange(o, SM,
- Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
- FM, indent + 1);
- --indent;
- Indent(o, indent) << "</array>\n";
- }
+ EmitRanges(o, Ranges, FM, SM, LangOpts, indent);
// Output the call depth.
Indent(o, indent) << "<key>depth</key>";
EmitInteger(o, depth) << '\n';
// Output the text.
- assert(!P.getString().empty());
- Indent(o, indent) << "<key>extended_message</key>\n";
- Indent(o, indent);
- EmitString(o, P.getString()) << '\n';
-
- // Output the short text.
- // FIXME: Really use a short string.
- Indent(o, indent) << "<key>message</key>\n";
- Indent(o, indent);
- EmitString(o, P.getString()) << '\n';
+ EmitMessage(o, P.getString(), indent);
// Finish up.
--indent;
@@ -241,6 +261,34 @@ static void ReportMacro(raw_ostream &o,
}
}
+static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
+ const FIDMap& FM,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent,
+ unsigned depth) {
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ // 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, FM, SM, LangOpts, indent);
+
+ // Output the text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent); o << "</dict>\n";
+}
+
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts) {
@@ -266,7 +314,7 @@ static void ReportPiece(raw_ostream &o,
indent, depth);
break;
case PathDiagnosticPiece::Event:
- ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
+ ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
indent, depth, isKeyEvent);
break;
case PathDiagnosticPiece::Macro:
@@ -274,7 +322,8 @@ static void ReportPiece(raw_ostream &o,
indent, depth);
break;
case PathDiagnosticPiece::Note:
- // FIXME: Extend the plist format to support those.
+ ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts,
+ indent, depth);
break;
}
}
@@ -359,15 +408,39 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
DE = Diags.end(); DI!=DE; ++DI) {
- o << " <dict>\n"
- " <key>path</key>\n";
+ o << " <dict>\n";
const PathDiagnostic *D = *DI;
+ const PathPieces &PP = D->path;
+
+ assert(std::is_partitioned(
+ PP.begin(), PP.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; }) &&
+ "PathDiagnostic is not partitioned so that notes precede the rest");
+
+ PathPieces::const_iterator FirstNonNote = std::partition_point(
+ PP.begin(), PP.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; });
+
+ PathPieces::const_iterator I = PP.begin();
+
+ if (FirstNonNote != PP.begin()) {
+ o << " <key>notes</key>\n"
+ " <array>\n";
+
+ for (; I != FirstNonNote; ++I)
+ ReportDiag(o, **I, FM, *SM, LangOpts);
+
+ o << " </array>\n";
+ }
+
+ o << " <key>path</key>\n";
o << " <array>\n";
- for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
- I != E; ++I)
+ for (PathPieces::const_iterator E = PP.end(); I != E; ++I)
ReportDiag(o, **I, FM, *SM, LangOpts);
o << " </array>\n";
@@ -484,6 +557,15 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " </array>\n";
+ if (llvm::AreStatisticsEnabled() && SerializeStatistics) {
+ o << " <key>statistics</key>\n";
+ std::string stats;
+ llvm::raw_string_ostream os(stats);
+ llvm::PrintStatisticsJSON(os);
+ os.flush();
+ EmitString(o, html::EscapeText(stats)) << '\n';
+ }
+
// Finish.
o << "</dict>\n</plist>";
}