Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -84,6 +84,41 @@ PP.getLangOpts(), true)); } +static void EmitRanges(raw_ostream &o, + const ArrayRef Ranges, + const FIDMap& FM, + const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent) { + + if (Ranges.empty()) + return; + + Indent(o, indent) << "ranges\n"; + Indent(o, indent) << "\n"; + ++indent; + for (auto &R : Ranges) + EmitRange(o, SM, + Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), + FM, indent + 1); + --indent; + Indent(o, indent) << "\n"; +} + +static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) { + // Output the text. + assert(!Message.empty()); + Indent(o, indent) << "extended_message\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; + + // Output the short text. + // FIXME: Really use a short string. + Indent(o, indent) << "message\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; +} + static void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, const FIDMap& FM, @@ -138,7 +173,7 @@ Indent(o, indent) << "\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, @@ -163,34 +198,14 @@ // Output the ranges (if any). ArrayRef Ranges = P.getRanges(); - - if (!Ranges.empty()) { - Indent(o, indent) << "ranges\n"; - Indent(o, indent) << "\n"; - ++indent; - for (auto &R : Ranges) - EmitRange(o, SM, - Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), - FM, indent + 1); - --indent; - Indent(o, indent) << "\n"; - } + EmitRanges(o, Ranges, FM, SM, LangOpts, indent); // Output the call depth. Indent(o, indent) << "depth"; EmitInteger(o, depth) << '\n'; // Output the text. - assert(!P.getString().empty()); - Indent(o, indent) << "extended_message\n"; - Indent(o, indent); - EmitString(o, P.getString()) << '\n'; - - // Output the short text. - // FIXME: Really use a short string. - Indent(o, indent) << "message\n"; - Indent(o, indent); - EmitString(o, P.getString()) << '\n'; + EmitMessage(o, P.getString(), indent); // Finish up. --indent; @@ -246,6 +261,34 @@ } } +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) << "\n"; + ++indent; + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "location\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef 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 << "\n"; +} + static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { @@ -271,7 +314,7 @@ indent, depth); break; case PathDiagnosticPiece::Event: - ReportEvent(o, cast(P), FM, SM, LangOpts, + ReportEvent(o, cast(P), FM, SM, LangOpts, indent, depth, isKeyEvent); break; case PathDiagnosticPiece::Macro: @@ -279,7 +322,8 @@ indent, depth); break; case PathDiagnosticPiece::Note: - // FIXME: Extend the plist format to support those. + ReportNote(o, cast(P), FM, SM, LangOpts, + indent, depth); break; } } @@ -364,15 +408,39 @@ for (std::vector::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { - o << " \n" - " path\n"; + o << " \n"; const PathDiagnostic *D = *DI; + const PathPieces &PP = D->path; + + assert(std::is_partitioned( + PP.begin(), PP.end(), + [](const std::shared_ptr &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 &E) + { return E->getKind() == PathDiagnosticPiece::Note; }); + + PathPieces::const_iterator I = PP.begin(); + + if (FirstNonNote != PP.begin()) { + o << " notes\n" + " \n"; + + for (; I != FirstNonNote; ++I) + ReportDiag(o, **I, FM, *SM, LangOpts); + + o << " \n"; + } + + o << " path\n"; o << " \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 << " \n"; Index: test/Analysis/copypaste/plist-diagnostics.cpp =================================================================== --- test/Analysis/copypaste/plist-diagnostics.cpp +++ test/Analysis/copypaste/plist-diagnostics.cpp @@ -17,12 +17,39 @@ return b; } -// FIXME: This plist output doesn't include the extra note on line 13. -// It should be updated once the format for extra notes in plists is defined. - // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: notes +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line13 +// CHECK-NEXT: col28 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line13 +// CHECK-NEXT: col28 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line18 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: extended_message +// CHECK-NEXT: Similar code here +// CHECK-NEXT: message +// CHECK-NEXT: Similar code here +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: