brintos

brintos / llvm-project-archived public Read only

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