Home | History | Annotate | Line # | Download | only in Core
      1 //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 //  This file defines the PlistDiagnostics object.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "clang/Analysis/IssueHash.h"
     14 #include "clang/Analysis/MacroExpansionContext.h"
     15 #include "clang/Analysis/PathDiagnostic.h"
     16 #include "clang/Basic/FileManager.h"
     17 #include "clang/Basic/PlistSupport.h"
     18 #include "clang/Basic/SourceManager.h"
     19 #include "clang/Basic/Version.h"
     20 #include "clang/CrossTU/CrossTranslationUnit.h"
     21 #include "clang/Frontend/ASTUnit.h"
     22 #include "clang/Lex/Preprocessor.h"
     23 #include "clang/Lex/TokenConcatenation.h"
     24 #include "clang/Rewrite/Core/HTMLRewrite.h"
     25 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
     26 #include "llvm/ADT/SmallPtrSet.h"
     27 #include "llvm/ADT/SmallVector.h"
     28 #include "llvm/ADT/Statistic.h"
     29 #include "llvm/Support/Casting.h"
     30 #include <memory>
     31 
     32 using namespace clang;
     33 using namespace ento;
     34 using namespace markup;
     35 
     36 //===----------------------------------------------------------------------===//
     37 // Declarations of helper classes and functions for emitting bug reports in
     38 // plist format.
     39 //===----------------------------------------------------------------------===//
     40 
     41 namespace {
     42   class PlistDiagnostics : public PathDiagnosticConsumer {
     43     PathDiagnosticConsumerOptions DiagOpts;
     44     const std::string OutputFile;
     45     const Preprocessor &PP;
     46     const cross_tu::CrossTranslationUnitContext &CTU;
     47     const MacroExpansionContext &MacroExpansions;
     48     const bool SupportsCrossFileDiagnostics;
     49 
     50     void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
     51                       const PathPieces &Path);
     52 
     53   public:
     54     PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
     55                      const std::string &OutputFile, const Preprocessor &PP,
     56                      const cross_tu::CrossTranslationUnitContext &CTU,
     57                      const MacroExpansionContext &MacroExpansions,
     58                      bool supportsMultipleFiles);
     59 
     60     ~PlistDiagnostics() override {}
     61 
     62     void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
     63                               FilesMade *filesMade) override;
     64 
     65     StringRef getName() const override {
     66       return "PlistDiagnostics";
     67     }
     68 
     69     PathGenerationScheme getGenerationScheme() const override {
     70       return Extensive;
     71     }
     72     bool supportsLogicalOpControlFlow() const override { return true; }
     73     bool supportsCrossFileDiagnostics() const override {
     74       return SupportsCrossFileDiagnostics;
     75     }
     76   };
     77 } // end anonymous namespace
     78 
     79 namespace {
     80 
     81 /// A helper class for emitting a single report.
     82 class PlistPrinter {
     83   const FIDMap& FM;
     84   const Preprocessor &PP;
     85   const cross_tu::CrossTranslationUnitContext &CTU;
     86   const MacroExpansionContext &MacroExpansions;
     87   llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
     88 
     89 public:
     90   PlistPrinter(const FIDMap &FM, const Preprocessor &PP,
     91                const cross_tu::CrossTranslationUnitContext &CTU,
     92                const MacroExpansionContext &MacroExpansions)
     93       : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
     94 
     95   void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
     96     ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
     97   }
     98 
     99   /// Print the expansions of the collected macro pieces.
    100   ///
    101   /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one
    102   /// is found through a call piece, etc), it's subpieces are reported, and the
    103   /// piece itself is collected. Call this function after the entire bugpath
    104   /// was reported.
    105   void ReportMacroExpansions(raw_ostream &o, unsigned indent);
    106 
    107 private:
    108   void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P,
    109                    unsigned indent, unsigned depth, bool includeControlFlow,
    110                    bool isKeyEvent = false) {
    111     switch (P.getKind()) {
    112       case PathDiagnosticPiece::ControlFlow:
    113         if (includeControlFlow)
    114           ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent);
    115         break;
    116       case PathDiagnosticPiece::Call:
    117         ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent,
    118                    depth);
    119         break;
    120       case PathDiagnosticPiece::Event:
    121         ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth,
    122                     isKeyEvent);
    123         break;
    124       case PathDiagnosticPiece::Macro:
    125         ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent,
    126                              depth);
    127         break;
    128       case PathDiagnosticPiece::Note:
    129         ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
    130         break;
    131       case PathDiagnosticPiece::PopUp:
    132         ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent);
    133         break;
    134     }
    135   }
    136 
    137   void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges,
    138                   unsigned indent);
    139   void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
    140   void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent);
    141 
    142   void ReportControlFlow(raw_ostream &o,
    143                          const PathDiagnosticControlFlowPiece& P,
    144                          unsigned indent);
    145   void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
    146                    unsigned indent, unsigned depth, bool isKeyEvent = false);
    147   void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
    148                   unsigned indent, unsigned depth);
    149   void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P,
    150                             unsigned indent, unsigned depth);
    151   void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
    152                   unsigned indent);
    153 
    154   void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P,
    155                    unsigned indent);
    156 };
    157 
    158 } // end of anonymous namespace
    159 
    160 /// Print coverage information to output stream @c o.
    161 /// May modify the used list of files @c Fids by inserting new ones.
    162 static void printCoverage(const PathDiagnostic *D,
    163                           unsigned InputIndentLevel,
    164                           SmallVectorImpl<FileID> &Fids,
    165                           FIDMap &FM,
    166                           llvm::raw_fd_ostream &o);
    167 
    168 static Optional<StringRef> getExpandedMacro(
    169     SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU,
    170     const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
    171 
    172 //===----------------------------------------------------------------------===//
    173 // Methods of PlistPrinter.
    174 //===----------------------------------------------------------------------===//
    175 
    176 void PlistPrinter::EmitRanges(raw_ostream &o,
    177                               const ArrayRef<SourceRange> Ranges,
    178                               unsigned indent) {
    179 
    180   if (Ranges.empty())
    181     return;
    182 
    183   Indent(o, indent) << "<key>ranges</key>\n";
    184   Indent(o, indent) << "<array>\n";
    185   ++indent;
    186 
    187   const SourceManager &SM = PP.getSourceManager();
    188   const LangOptions &LangOpts = PP.getLangOpts();
    189 
    190   for (auto &R : Ranges)
    191     EmitRange(o, SM,
    192               Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
    193               FM, indent + 1);
    194   --indent;
    195   Indent(o, indent) << "</array>\n";
    196 }
    197 
    198 void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
    199                                unsigned indent) {
    200   // Output the text.
    201   assert(!Message.empty());
    202   Indent(o, indent) << "<key>extended_message</key>\n";
    203   Indent(o, indent);
    204   EmitString(o, Message) << '\n';
    205 
    206   // Output the short text.
    207   // FIXME: Really use a short string.
    208   Indent(o, indent) << "<key>message</key>\n";
    209   Indent(o, indent);
    210   EmitString(o, Message) << '\n';
    211 }
    212 
    213 void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
    214                               unsigned indent) {
    215   if (fixits.size() == 0)
    216     return;
    217 
    218   const SourceManager &SM = PP.getSourceManager();
    219   const LangOptions &LangOpts = PP.getLangOpts();
    220 
    221   Indent(o, indent) << "<key>fixits</key>\n";
    222   Indent(o, indent) << "<array>\n";
    223   for (const auto &fixit : fixits) {
    224     assert(!fixit.isNull());
    225     // FIXME: Add support for InsertFromRange and BeforePreviousInsertion.
    226     assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");
    227     assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");
    228     Indent(o, indent) << " <dict>\n";
    229     Indent(o, indent) << "  <key>remove_range</key>\n";
    230     EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts),
    231               FM, indent + 2);
    232     Indent(o, indent) << "  <key>insert_string</key>";
    233     EmitString(o, fixit.CodeToInsert);
    234     o << "\n";
    235     Indent(o, indent) << " </dict>\n";
    236   }
    237   Indent(o, indent) << "</array>\n";
    238 }
    239 
    240 void PlistPrinter::ReportControlFlow(raw_ostream &o,
    241                                      const PathDiagnosticControlFlowPiece& P,
    242                                      unsigned indent) {
    243 
    244   const SourceManager &SM = PP.getSourceManager();
    245   const LangOptions &LangOpts = PP.getLangOpts();
    246 
    247   Indent(o, indent) << "<dict>\n";
    248   ++indent;
    249 
    250   Indent(o, indent) << "<key>kind</key><string>control</string>\n";
    251 
    252   // Emit edges.
    253   Indent(o, indent) << "<key>edges</key>\n";
    254   ++indent;
    255   Indent(o, indent) << "<array>\n";
    256   ++indent;
    257   for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
    258        I!=E; ++I) {
    259     Indent(o, indent) << "<dict>\n";
    260     ++indent;
    261 
    262     // Make the ranges of the start and end point self-consistent with adjacent edges
    263     // by forcing to use only the beginning of the range.  This simplifies the layout
    264     // logic for clients.
    265     Indent(o, indent) << "<key>start</key>\n";
    266     SourceRange StartEdge(
    267         SM.getExpansionLoc(I->getStart().asRange().getBegin()));
    268     EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
    269               indent + 1);
    270 
    271     Indent(o, indent) << "<key>end</key>\n";
    272     SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
    273     EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
    274               indent + 1);
    275 
    276     --indent;
    277     Indent(o, indent) << "</dict>\n";
    278   }
    279   --indent;
    280   Indent(o, indent) << "</array>\n";
    281   --indent;
    282 
    283   // Output any helper text.
    284   const auto &s = P.getString();
    285   if (!s.empty()) {
    286     Indent(o, indent) << "<key>alternate</key>";
    287     EmitString(o, s) << '\n';
    288   }
    289 
    290   assert(P.getFixits().size() == 0 &&
    291          "Fixits on constrol flow pieces are not implemented yet!");
    292 
    293   --indent;
    294   Indent(o, indent) << "</dict>\n";
    295 }
    296 
    297 void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
    298                                unsigned indent, unsigned depth,
    299                                bool isKeyEvent) {
    300 
    301   const SourceManager &SM = PP.getSourceManager();
    302 
    303   Indent(o, indent) << "<dict>\n";
    304   ++indent;
    305 
    306   Indent(o, indent) << "<key>kind</key><string>event</string>\n";
    307 
    308   if (isKeyEvent) {
    309     Indent(o, indent) << "<key>key_event</key><true/>\n";
    310   }
    311 
    312   // Output the location.
    313   FullSourceLoc L = P.getLocation().asLocation();
    314 
    315   Indent(o, indent) << "<key>location</key>\n";
    316   EmitLocation(o, SM, L, FM, indent);
    317 
    318   // Output the ranges (if any).
    319   ArrayRef<SourceRange> Ranges = P.getRanges();
    320   EmitRanges(o, Ranges, indent);
    321 
    322   // Output the call depth.
    323   Indent(o, indent) << "<key>depth</key>";
    324   EmitInteger(o, depth) << '\n';
    325 
    326   // Output the text.
    327   EmitMessage(o, P.getString(), indent);
    328 
    329   // Output the fixits.
    330   EmitFixits(o, P.getFixits(), indent);
    331 
    332   // Finish up.
    333   --indent;
    334   Indent(o, indent); o << "</dict>\n";
    335 }
    336 
    337 void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
    338                               unsigned indent,
    339                               unsigned depth) {
    340 
    341   if (auto callEnter = P.getCallEnterEvent())
    342     ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true,
    343                 P.isLastInMainSourceFile());
    344 
    345 
    346   ++depth;
    347 
    348   if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
    349     ReportPiece(o, *callEnterWithinCaller, indent, depth,
    350                 /*includeControlFlow*/ true);
    351 
    352   for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
    353     ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true);
    354 
    355   --depth;
    356 
    357   if (auto callExit = P.getCallExitEvent())
    358     ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true);
    359 
    360   assert(P.getFixits().size() == 0 &&
    361          "Fixits on call pieces are not implemented yet!");
    362 }
    363 
    364 void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
    365                                         const PathDiagnosticMacroPiece& P,
    366                                         unsigned indent, unsigned depth) {
    367   MacroPieces.push_back(&P);
    368 
    369   for (PathPieces::const_iterator I = P.subPieces.begin(),
    370                                   E = P.subPieces.end();
    371        I != E; ++I) {
    372     ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false);
    373   }
    374 
    375   assert(P.getFixits().size() == 0 &&
    376          "Fixits on constrol flow pieces are not implemented yet!");
    377 }
    378 
    379 void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
    380 
    381   for (const PathDiagnosticMacroPiece *P : MacroPieces) {
    382     const SourceManager &SM = PP.getSourceManager();
    383 
    384     SourceLocation MacroExpansionLoc =
    385         P->getLocation().asLocation().getExpansionLoc();
    386 
    387     const Optional<StringRef> MacroName =
    388         MacroExpansions.getOriginalText(MacroExpansionLoc);
    389     const Optional<StringRef> ExpansionText =
    390         getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
    391 
    392     if (!MacroName.hasValue() || !ExpansionText.hasValue())
    393       continue;
    394 
    395     Indent(o, indent) << "<dict>\n";
    396     ++indent;
    397 
    398     // Output the location.
    399     FullSourceLoc L = P->getLocation().asLocation();
    400 
    401     Indent(o, indent) << "<key>location</key>\n";
    402     EmitLocation(o, SM, L, FM, indent);
    403 
    404     // Output the ranges (if any).
    405     ArrayRef<SourceRange> Ranges = P->getRanges();
    406     EmitRanges(o, Ranges, indent);
    407 
    408     // Output the macro name.
    409     Indent(o, indent) << "<key>name</key>";
    410     EmitString(o, MacroName.getValue()) << '\n';
    411 
    412     // Output what it expands into.
    413     Indent(o, indent) << "<key>expansion</key>";
    414     EmitString(o, ExpansionText.getValue()) << '\n';
    415 
    416     // Finish up.
    417     --indent;
    418     Indent(o, indent);
    419     o << "</dict>\n";
    420   }
    421 }
    422 
    423 void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
    424                               unsigned indent) {
    425 
    426   const SourceManager &SM = PP.getSourceManager();
    427 
    428   Indent(o, indent) << "<dict>\n";
    429   ++indent;
    430 
    431   // Output the location.
    432   FullSourceLoc L = P.getLocation().asLocation();
    433 
    434   Indent(o, indent) << "<key>location</key>\n";
    435   EmitLocation(o, SM, L, FM, indent);
    436 
    437   // Output the ranges (if any).
    438   ArrayRef<SourceRange> Ranges = P.getRanges();
    439   EmitRanges(o, Ranges, indent);
    440 
    441   // Output the text.
    442   EmitMessage(o, P.getString(), indent);
    443 
    444   // Output the fixits.
    445   EmitFixits(o, P.getFixits(), indent);
    446 
    447   // Finish up.
    448   --indent;
    449   Indent(o, indent); o << "</dict>\n";
    450 }
    451 
    452 void PlistPrinter::ReportPopUp(raw_ostream &o,
    453                                const PathDiagnosticPopUpPiece &P,
    454                                unsigned indent) {
    455   const SourceManager &SM = PP.getSourceManager();
    456 
    457   Indent(o, indent) << "<dict>\n";
    458   ++indent;
    459 
    460   Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n";
    461 
    462   // Output the location.
    463   FullSourceLoc L = P.getLocation().asLocation();
    464 
    465   Indent(o, indent) << "<key>location</key>\n";
    466   EmitLocation(o, SM, L, FM, indent);
    467 
    468   // Output the ranges (if any).
    469   ArrayRef<SourceRange> Ranges = P.getRanges();
    470   EmitRanges(o, Ranges, indent);
    471 
    472   // Output the text.
    473   EmitMessage(o, P.getString(), indent);
    474 
    475   assert(P.getFixits().size() == 0 &&
    476          "Fixits on pop-up pieces are not implemented yet!");
    477 
    478   // Finish up.
    479   --indent;
    480   Indent(o, indent) << "</dict>\n";
    481 }
    482 
    483 //===----------------------------------------------------------------------===//
    484 // Static function definitions.
    485 //===----------------------------------------------------------------------===//
    486 
    487 /// Print coverage information to output stream @c o.
    488 /// May modify the used list of files @c Fids by inserting new ones.
    489 static void printCoverage(const PathDiagnostic *D,
    490                           unsigned InputIndentLevel,
    491                           SmallVectorImpl<FileID> &Fids,
    492                           FIDMap &FM,
    493                           llvm::raw_fd_ostream &o) {
    494   unsigned IndentLevel = InputIndentLevel;
    495 
    496   Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n";
    497   Indent(o, IndentLevel) << "<dict>\n";
    498   IndentLevel++;
    499 
    500   // Mapping from file IDs to executed lines.
    501   const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines();
    502   for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; ++I) {
    503     unsigned FileKey = AddFID(FM, Fids, I->first);
    504     Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n";
    505     Indent(o, IndentLevel) << "<array>\n";
    506     IndentLevel++;
    507     for (unsigned LineNo : I->second) {
    508       Indent(o, IndentLevel);
    509       EmitInteger(o, LineNo) << "\n";
    510     }
    511     IndentLevel--;
    512     Indent(o, IndentLevel) << "</array>\n";
    513   }
    514   IndentLevel--;
    515   Indent(o, IndentLevel) << "</dict>\n";
    516 
    517   assert(IndentLevel == InputIndentLevel);
    518 }
    519 
    520 //===----------------------------------------------------------------------===//
    521 // Methods of PlistDiagnostics.
    522 //===----------------------------------------------------------------------===//
    523 
    524 PlistDiagnostics::PlistDiagnostics(
    525     PathDiagnosticConsumerOptions DiagOpts, const std::string &output,
    526     const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
    527     const MacroExpansionContext &MacroExpansions, bool supportsMultipleFiles)
    528     : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
    529       MacroExpansions(MacroExpansions),
    530       SupportsCrossFileDiagnostics(supportsMultipleFiles) {
    531   // FIXME: Will be used by a later planned change.
    532   (void)this->CTU;
    533 }
    534 
    535 void ento::createPlistDiagnosticConsumer(
    536     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
    537     const std::string &OutputFile, const Preprocessor &PP,
    538     const cross_tu::CrossTranslationUnitContext &CTU,
    539     const MacroExpansionContext &MacroExpansions) {
    540 
    541   // TODO: Emit an error here.
    542   if (OutputFile.empty())
    543     return;
    544 
    545   C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
    546                                    MacroExpansions,
    547                                    /*supportsMultipleFiles=*/false));
    548   createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
    549                                           PP, CTU, MacroExpansions);
    550 }
    551 
    552 void ento::createPlistMultiFileDiagnosticConsumer(
    553     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
    554     const std::string &OutputFile, const Preprocessor &PP,
    555     const cross_tu::CrossTranslationUnitContext &CTU,
    556     const MacroExpansionContext &MacroExpansions) {
    557 
    558   // TODO: Emit an error here.
    559   if (OutputFile.empty())
    560     return;
    561 
    562   C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
    563                                    MacroExpansions,
    564                                    /*supportsMultipleFiles=*/true));
    565   createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
    566                                           PP, CTU, MacroExpansions);
    567 }
    568 
    569 void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
    570                                     const PathPieces &Path) {
    571   PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
    572   assert(std::is_partitioned(Path.begin(), Path.end(),
    573                              [](const PathDiagnosticPieceRef &E) {
    574                                return E->getKind() == PathDiagnosticPiece::Note;
    575                              }) &&
    576          "PathDiagnostic is not partitioned so that notes precede the rest");
    577 
    578   PathPieces::const_iterator FirstNonNote = std::partition_point(
    579       Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) {
    580         return E->getKind() == PathDiagnosticPiece::Note;
    581       });
    582 
    583   PathPieces::const_iterator I = Path.begin();
    584 
    585   if (FirstNonNote != Path.begin()) {
    586     o << "   <key>notes</key>\n"
    587          "   <array>\n";
    588 
    589     for (; I != FirstNonNote; ++I)
    590       Printer.ReportDiag(o, **I);
    591 
    592     o << "   </array>\n";
    593   }
    594 
    595   o << "   <key>path</key>\n";
    596 
    597   o << "   <array>\n";
    598 
    599   for (PathPieces::const_iterator E = Path.end(); I != E; ++I)
    600     Printer.ReportDiag(o, **I);
    601 
    602   o << "   </array>\n";
    603 
    604   if (!DiagOpts.ShouldDisplayMacroExpansions)
    605     return;
    606 
    607   o << "   <key>macro_expansions</key>\n"
    608        "   <array>\n";
    609   Printer.ReportMacroExpansions(o, /* indent */ 4);
    610   o << "   </array>\n";
    611 }
    612 
    613 void PlistDiagnostics::FlushDiagnosticsImpl(
    614                                     std::vector<const PathDiagnostic *> &Diags,
    615                                     FilesMade *filesMade) {
    616   // Build up a set of FIDs that we use by scanning the locations and
    617   // ranges of the diagnostics.
    618   FIDMap FM;
    619   SmallVector<FileID, 10> Fids;
    620   const SourceManager& SM = PP.getSourceManager();
    621   const LangOptions &LangOpts = PP.getLangOpts();
    622 
    623   auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) {
    624     AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
    625     ArrayRef<SourceRange> Ranges = Piece.getRanges();
    626     for (const SourceRange &Range : Ranges) {
    627       AddFID(FM, Fids, SM, Range.getBegin());
    628       AddFID(FM, Fids, SM, Range.getEnd());
    629     }
    630   };
    631 
    632   for (const PathDiagnostic *D : Diags) {
    633 
    634     SmallVector<const PathPieces *, 5> WorkList;
    635     WorkList.push_back(&D->path);
    636 
    637     while (!WorkList.empty()) {
    638       const PathPieces &Path = *WorkList.pop_back_val();
    639 
    640       for (const auto &Iter : Path) {
    641         const PathDiagnosticPiece &Piece = *Iter;
    642         AddPieceFID(Piece);
    643 
    644         if (const PathDiagnosticCallPiece *Call =
    645                 dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
    646           if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
    647             AddPieceFID(*CallEnterWithin);
    648 
    649           if (auto CallEnterEvent = Call->getCallEnterEvent())
    650             AddPieceFID(*CallEnterEvent);
    651 
    652           WorkList.push_back(&Call->path);
    653         } else if (const PathDiagnosticMacroPiece *Macro =
    654                        dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
    655           WorkList.push_back(&Macro->subPieces);
    656         }
    657       }
    658     }
    659   }
    660 
    661   // Open the file.
    662   std::error_code EC;
    663   llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
    664   if (EC) {
    665     llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
    666     return;
    667   }
    668 
    669   EmitPlistHeader(o);
    670 
    671   // Write the root object: a <dict> containing...
    672   //  - "clang_version", the string representation of clang version
    673   //  - "files", an <array> mapping from FIDs to file names
    674   //  - "diagnostics", an <array> containing the path diagnostics
    675   o << "<dict>\n" <<
    676        " <key>clang_version</key>\n";
    677   EmitString(o, getClangFullVersion()) << '\n';
    678   o << " <key>diagnostics</key>\n"
    679        " <array>\n";
    680 
    681   for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
    682        DE = Diags.end(); DI!=DE; ++DI) {
    683 
    684     o << "  <dict>\n";
    685 
    686     const PathDiagnostic *D = *DI;
    687     printBugPath(o, FM, D->path);
    688 
    689     // Output the bug type and bug category.
    690     o << "   <key>description</key>";
    691     EmitString(o, D->getShortDescription()) << '\n';
    692     o << "   <key>category</key>";
    693     EmitString(o, D->getCategory()) << '\n';
    694     o << "   <key>type</key>";
    695     EmitString(o, D->getBugType()) << '\n';
    696     o << "   <key>check_name</key>";
    697     EmitString(o, D->getCheckerName()) << '\n';
    698 
    699     o << "   <!-- This hash is experimental and going to change! -->\n";
    700     o << "   <key>issue_hash_content_of_line_in_context</key>";
    701     PathDiagnosticLocation UPDLoc = D->getUniqueingLoc();
    702     FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid()
    703                                             ? UPDLoc.asLocation()
    704                                             : D->getLocation().asLocation()),
    705                     SM);
    706     const Decl *DeclWithIssue = D->getDeclWithIssue();
    707     EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(),
    708                                DeclWithIssue, LangOpts))
    709         << '\n';
    710 
    711     // Output information about the semantic context where
    712     // the issue occurred.
    713     if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
    714       // FIXME: handle blocks, which have no name.
    715       if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
    716         StringRef declKind;
    717         switch (ND->getKind()) {
    718           case Decl::CXXRecord:
    719             declKind = "C++ class";
    720             break;
    721           case Decl::CXXMethod:
    722             declKind = "C++ method";
    723             break;
    724           case Decl::ObjCMethod:
    725             declKind = "Objective-C method";
    726             break;
    727           case Decl::Function:
    728             declKind = "function";
    729             break;
    730           default:
    731             break;
    732         }
    733         if (!declKind.empty()) {
    734           const std::string &declName = ND->getDeclName().getAsString();
    735           o << "  <key>issue_context_kind</key>";
    736           EmitString(o, declKind) << '\n';
    737           o << "  <key>issue_context</key>";
    738           EmitString(o, declName) << '\n';
    739         }
    740 
    741         // Output the bug hash for issue unique-ing. Currently, it's just an
    742         // offset from the beginning of the function.
    743         if (const Stmt *Body = DeclWithIssue->getBody()) {
    744 
    745           // If the bug uniqueing location exists, use it for the hash.
    746           // For example, this ensures that two leaks reported on the same line
    747           // will have different issue_hashes and that the hash will identify
    748           // the leak location even after code is added between the allocation
    749           // site and the end of scope (leak report location).
    750           if (UPDLoc.isValid()) {
    751             FullSourceLoc UFunL(
    752                 SM.getExpansionLoc(
    753                     D->getUniqueingDecl()->getBody()->getBeginLoc()),
    754                 SM);
    755             o << "  <key>issue_hash_function_offset</key><string>"
    756               << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
    757               << "</string>\n";
    758 
    759           // Otherwise, use the location on which the bug is reported.
    760           } else {
    761             FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM);
    762             o << "  <key>issue_hash_function_offset</key><string>"
    763               << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
    764               << "</string>\n";
    765           }
    766 
    767         }
    768       }
    769     }
    770 
    771     // Output the location of the bug.
    772     o << "  <key>location</key>\n";
    773     EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2);
    774 
    775     // Output the diagnostic to the sub-diagnostic client, if any.
    776     if (!filesMade->empty()) {
    777       StringRef lastName;
    778       PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
    779       if (files) {
    780         for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
    781                 CE = files->end(); CI != CE; ++CI) {
    782           StringRef newName = CI->first;
    783           if (newName != lastName) {
    784             if (!lastName.empty()) {
    785               o << "  </array>\n";
    786             }
    787             lastName = newName;
    788             o <<  "  <key>" << lastName << "_files</key>\n";
    789             o << "  <array>\n";
    790           }
    791           o << "   <string>" << CI->second << "</string>\n";
    792         }
    793         o << "  </array>\n";
    794       }
    795     }
    796 
    797     printCoverage(D, /*IndentLevel=*/2, Fids, FM, o);
    798 
    799     // Close up the entry.
    800     o << "  </dict>\n";
    801   }
    802 
    803   o << " </array>\n";
    804 
    805   o << " <key>files</key>\n"
    806        " <array>\n";
    807   for (FileID FID : Fids)
    808     EmitString(o << "  ", SM.getFileEntryForID(FID)->getName()) << '\n';
    809   o << " </array>\n";
    810 
    811   if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) {
    812     o << " <key>statistics</key>\n";
    813     std::string stats;
    814     llvm::raw_string_ostream os(stats);
    815     llvm::PrintStatisticsJSON(os);
    816     os.flush();
    817     EmitString(o, html::EscapeText(stats)) << '\n';
    818   }
    819 
    820   // Finish.
    821   o << "</dict>\n</plist>\n";
    822 }
    823 
    824 //===----------------------------------------------------------------------===//
    825 // Definitions of helper functions and methods for expanding macros.
    826 //===----------------------------------------------------------------------===//
    827 
    828 static Optional<StringRef>
    829 getExpandedMacro(SourceLocation MacroExpansionLoc,
    830                  const cross_tu::CrossTranslationUnitContext &CTU,
    831                  const MacroExpansionContext &MacroExpansions,
    832                  const SourceManager &SM) {
    833   if (auto CTUMacroExpCtx =
    834           CTU.getMacroExpansionContextForSourceLocation(MacroExpansionLoc)) {
    835     return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
    836   }
    837   return MacroExpansions.getExpandedText(MacroExpansionLoc);
    838 }
    839