diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core/PlistDiagnostics.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/PlistDiagnostics.cpp | 142 |
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>"; } |