brintos

brintos / llvm-project-archived public Read only

0
0
Text · 31.7 KiB · 1dffe7c Raw
882 lines · cpp
1//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===//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#include "clang/Frontend/SerializedDiagnosticPrinter.h"10#include "clang/Basic/Diagnostic.h"11#include "clang/Basic/DiagnosticFrontend.h"12#include "clang/Basic/DiagnosticOptions.h"13#include "clang/Basic/SourceManager.h"14#include "clang/Frontend/DiagnosticRenderer.h"15#include "clang/Frontend/SerializedDiagnosticReader.h"16#include "clang/Frontend/SerializedDiagnostics.h"17#include "clang/Frontend/TextDiagnosticPrinter.h"18#include "clang/Lex/Lexer.h"19#include "llvm/ADT/DenseSet.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/Bitstream/BitCodes.h"22#include "llvm/Support/FileSystem.h"23#include "llvm/Support/raw_ostream.h"24#include <utility>25 26using namespace clang;27using namespace clang::serialized_diags;28 29namespace {30 31class AbbreviationMap {32  llvm::DenseMap<unsigned, unsigned> Abbrevs;33public:34  AbbreviationMap() {}35 36  void set(unsigned recordID, unsigned abbrevID) {37    assert(!Abbrevs.contains(recordID) && "Abbreviation already set.");38    Abbrevs[recordID] = abbrevID;39  }40 41  unsigned get(unsigned recordID) {42    assert(Abbrevs.contains(recordID) && "Abbreviation not set.");43    return Abbrevs[recordID];44  }45};46 47typedef SmallVector<uint64_t, 64> RecordData;48typedef SmallVectorImpl<uint64_t> RecordDataImpl;49typedef ArrayRef<uint64_t> RecordDataRef;50 51class SDiagsWriter;52 53class SDiagsRenderer : public DiagnosticNoteRenderer {54  SDiagsWriter &Writer;55public:56  SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts,57                 DiagnosticOptions &DiagOpts)58      : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {}59 60  ~SDiagsRenderer() override {}61 62protected:63  void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,64                             DiagnosticsEngine::Level Level, StringRef Message,65                             ArrayRef<CharSourceRange> Ranges,66                             DiagOrStoredDiag D) override;67 68  void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,69                         DiagnosticsEngine::Level Level,70                         ArrayRef<CharSourceRange> Ranges) override {}71 72  void emitNote(FullSourceLoc Loc, StringRef Message) override;73 74  void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,75                       SmallVectorImpl<CharSourceRange> &Ranges,76                       ArrayRef<FixItHint> Hints) override;77 78  void beginDiagnostic(DiagOrStoredDiag D,79                       DiagnosticsEngine::Level Level) override;80  void endDiagnostic(DiagOrStoredDiag D,81                     DiagnosticsEngine::Level Level) override;82};83 84typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup;85 86class SDiagsMerger : SerializedDiagnosticReader {87  SDiagsWriter &Writer;88  AbbrevLookup FileLookup;89  AbbrevLookup CategoryLookup;90  AbbrevLookup DiagFlagLookup;91 92public:93  SDiagsMerger(SDiagsWriter &Writer) : Writer(Writer) {}94 95  std::error_code mergeRecordsFromFile(const char *File) {96    return readDiagnostics(File);97  }98 99protected:100  std::error_code visitStartOfDiagnostic() override;101  std::error_code visitEndOfDiagnostic() override;102  std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override;103  std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override;104  std::error_code visitDiagnosticRecord(105      unsigned Severity, const serialized_diags::Location &Location,106      unsigned Category, unsigned Flag, StringRef Message) override;107  std::error_code visitFilenameRecord(unsigned ID, unsigned Size,108                                      unsigned Timestamp,109                                      StringRef Name) override;110  std::error_code visitFixitRecord(const serialized_diags::Location &Start,111                                   const serialized_diags::Location &End,112                                   StringRef CodeToInsert) override;113  std::error_code114  visitSourceRangeRecord(const serialized_diags::Location &Start,115                         const serialized_diags::Location &End) override;116 117private:118  std::error_code adjustSourceLocFilename(RecordData &Record,119                                          unsigned int offset);120 121  void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup,122                      unsigned NewAbbrev);123 124  void writeRecordWithAbbrev(unsigned ID, RecordData &Record);125 126  void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob);127};128 129class SDiagsWriter : public DiagnosticConsumer {130  friend class SDiagsRenderer;131  friend class SDiagsMerger;132 133  struct SharedState;134 135  explicit SDiagsWriter(std::shared_ptr<SharedState> State)136      : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false),137        State(std::move(State)) {}138 139public:140  SDiagsWriter(StringRef File, DiagnosticOptions &Diags, bool MergeChildRecords)141      : LangOpts(nullptr), OriginalInstance(true),142        MergeChildRecords(MergeChildRecords),143        State(std::make_shared<SharedState>(File, Diags)) {144    if (MergeChildRecords)145      RemoveOldDiagnostics();146    EmitPreamble();147  }148 149  ~SDiagsWriter() override {}150 151  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,152                        const Diagnostic &Info) override;153 154  void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override {155    LangOpts = &LO;156  }157 158  void finish() override;159 160private:161  /// Build a DiagnosticsEngine to emit diagnostics about the diagnostics162  DiagnosticsEngine *getMetaDiags();163 164  /// Remove old copies of the serialized diagnostics. This is necessary165  /// so that we can detect when subprocesses write diagnostics that we should166  /// merge into our own.167  void RemoveOldDiagnostics();168 169  /// Emit the preamble for the serialized diagnostics.170  void EmitPreamble();171 172  /// Emit the BLOCKINFO block.173  void EmitBlockInfoBlock();174 175  /// Emit the META data block.176  void EmitMetaBlock();177 178  /// Start a DIAG block.179  void EnterDiagBlock();180 181  /// End a DIAG block.182  void ExitDiagBlock();183 184  /// Emit a DIAG record.185  void EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,186                             DiagnosticsEngine::Level Level, StringRef Message,187                             DiagOrStoredDiag D);188 189  /// Emit FIXIT and SOURCE_RANGE records for a diagnostic.190  void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges,191                       ArrayRef<FixItHint> Hints,192                       const SourceManager &SM);193 194  /// Emit a record for a CharSourceRange.195  void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM);196 197  /// Emit the string information for the category.198  unsigned getEmitCategory(unsigned category = 0);199 200  /// Emit the string information for diagnostic flags.201  unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,202                                 const Diagnostic *Diag = nullptr);203 204  unsigned getEmitDiagnosticFlag(StringRef DiagName);205 206  /// Emit (lazily) the file string and retrieved the file identifier.207  unsigned getEmitFile(const char *Filename);208 209  /// Add SourceLocation information the specified record.210  void AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc,211                      RecordDataImpl &Record, unsigned TokSize = 0);212 213  /// Add SourceLocation information the specified record.214  void AddLocToRecord(FullSourceLoc Loc, RecordDataImpl &Record,215                      unsigned TokSize = 0) {216    AddLocToRecord(Loc, Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(),217                   Record, TokSize);218  }219 220  /// Add CharSourceRange information the specified record.221  void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record,222                                  const SourceManager &SM);223 224  /// Language options, which can differ from one clone of this client225  /// to another.226  const LangOptions *LangOpts;227 228  /// Whether this is the original instance (rather than one of its229  /// clones), responsible for writing the file at the end.230  bool OriginalInstance;231 232  /// Whether this instance should aggregate diagnostics that are233  /// generated from child processes.234  bool MergeChildRecords;235 236  /// Whether we've started finishing and tearing down this instance.237  bool IsFinishing = false;238 239  /// State that is shared among the various clones of this diagnostic240  /// consumer.241  struct SharedState {242    SharedState(StringRef File, DiagnosticOptions &DiagOpts)243        : DiagOpts(DiagOpts), Stream(Buffer), OutputFile(File.str()),244          EmittedAnyDiagBlocks(false) {}245 246    /// Diagnostic options.247    DiagnosticOptions DiagOpts;248 249    /// The byte buffer for the serialized content.250    SmallString<1024> Buffer;251 252    /// The BitStreamWriter for the serialized diagnostics.253    llvm::BitstreamWriter Stream;254 255    /// The name of the diagnostics file.256    std::string OutputFile;257 258    /// The set of constructed record abbreviations.259    AbbreviationMap Abbrevs;260 261    /// A utility buffer for constructing record content.262    RecordData Record;263 264    /// A text buffer for rendering diagnostic text.265    SmallString<256> diagBuf;266 267    /// The collection of diagnostic categories used.268    llvm::DenseSet<unsigned> Categories;269 270    /// The collection of files used.271    llvm::DenseMap<const char *, unsigned> Files;272 273    typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> >274    DiagFlagsTy;275 276    /// Map for uniquing strings.277    DiagFlagsTy DiagFlags;278 279    /// Whether we have already started emission of any DIAG blocks. Once280    /// this becomes \c true, we never close a DIAG block until we know that we're281    /// starting another one or we're done.282    bool EmittedAnyDiagBlocks;283 284    /// Engine for emitting diagnostics about the diagnostics.285    std::unique_ptr<DiagnosticsEngine> MetaDiagnostics;286  };287 288  /// State shared among the various clones of this diagnostic consumer.289  std::shared_ptr<SharedState> State;290};291} // end anonymous namespace292 293namespace clang {294namespace serialized_diags {295std::unique_ptr<DiagnosticConsumer> create(StringRef OutputFile,296                                           DiagnosticOptions &DiagOpts,297                                           bool MergeChildRecords) {298  return std::make_unique<SDiagsWriter>(OutputFile, DiagOpts,299                                        MergeChildRecords);300}301 302} // end namespace serialized_diags303} // end namespace clang304 305//===----------------------------------------------------------------------===//306// Serialization methods.307//===----------------------------------------------------------------------===//308 309/// Emits a block ID in the BLOCKINFO block.310static void EmitBlockID(unsigned ID, const char *Name,311                        llvm::BitstreamWriter &Stream,312                        RecordDataImpl &Record) {313  Record.clear();314  Record.push_back(ID);315  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);316 317  // Emit the block name if present.318  if (!Name || Name[0] == 0)319    return;320 321  Record.clear();322 323  while (*Name)324    Record.push_back(*Name++);325 326  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);327}328 329/// Emits a record ID in the BLOCKINFO block.330static void EmitRecordID(unsigned ID, const char *Name,331                         llvm::BitstreamWriter &Stream,332                         RecordDataImpl &Record){333  Record.clear();334  Record.push_back(ID);335 336  while (*Name)337    Record.push_back(*Name++);338 339  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);340}341 342void SDiagsWriter::AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc,343                                  RecordDataImpl &Record, unsigned TokSize) {344  if (PLoc.isInvalid()) {345    // Emit a "sentinel" location.346    Record.push_back((unsigned)0); // File.347    Record.push_back((unsigned)0); // Line.348    Record.push_back((unsigned)0); // Column.349    Record.push_back((unsigned)0); // Offset.350    return;351  }352 353  Record.push_back(getEmitFile(PLoc.getFilename()));354  Record.push_back(PLoc.getLine());355  Record.push_back(PLoc.getColumn()+TokSize);356  Record.push_back(Loc.getFileOffset());357}358 359void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range,360                                              RecordDataImpl &Record,361                                              const SourceManager &SM) {362  AddLocToRecord(FullSourceLoc(Range.getBegin(), SM), Record);363  unsigned TokSize = 0;364  if (Range.isTokenRange())365    TokSize = Lexer::MeasureTokenLength(Range.getEnd(),366                                        SM, *LangOpts);367 368  AddLocToRecord(FullSourceLoc(Range.getEnd(), SM), Record, TokSize);369}370 371unsigned SDiagsWriter::getEmitFile(const char *FileName){372  if (!FileName)373    return 0;374 375  unsigned &entry = State->Files[FileName];376  if (entry)377    return entry;378 379  // Lazily generate the record for the file.380  entry = State->Files.size();381  StringRef Name(FileName);382  RecordData::value_type Record[] = {RECORD_FILENAME, entry, 0 /* For legacy */,383                                     0 /* For legacy */, Name.size()};384  State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record,385                                   Name);386 387  return entry;388}389 390void SDiagsWriter::EmitCharSourceRange(CharSourceRange R,391                                       const SourceManager &SM) {392  State->Record.clear();393  State->Record.push_back(RECORD_SOURCE_RANGE);394  AddCharSourceRangeToRecord(R, State->Record, SM);395  State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE),396                                     State->Record);397}398 399/// Emits the preamble of the diagnostics file.400void SDiagsWriter::EmitPreamble() {401  // Emit the file header.402  State->Stream.Emit((unsigned)'D', 8);403  State->Stream.Emit((unsigned)'I', 8);404  State->Stream.Emit((unsigned)'A', 8);405  State->Stream.Emit((unsigned)'G', 8);406 407  EmitBlockInfoBlock();408  EmitMetaBlock();409}410 411static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) {412  using namespace llvm;413  Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID.414  Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.415  Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.416  Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;417}418 419static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) {420  AddSourceLocationAbbrev(Abbrev);421  AddSourceLocationAbbrev(Abbrev);422}423 424void SDiagsWriter::EmitBlockInfoBlock() {425  State->Stream.EnterBlockInfoBlock();426 427  using namespace llvm;428  llvm::BitstreamWriter &Stream = State->Stream;429  RecordData &Record = State->Record;430  AbbreviationMap &Abbrevs = State->Abbrevs;431 432  // ==---------------------------------------------------------------------==//433  // The subsequent records and Abbrevs are for the "Meta" block.434  // ==---------------------------------------------------------------------==//435 436  EmitBlockID(BLOCK_META, "Meta", Stream, Record);437  EmitRecordID(RECORD_VERSION, "Version", Stream, Record);438  auto Abbrev = std::make_shared<BitCodeAbbrev>();439  Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION));440  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));441  Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev));442 443  // ==---------------------------------------------------------------------==//444  // The subsequent records and Abbrevs are for the "Diagnostic" block.445  // ==---------------------------------------------------------------------==//446 447  EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);448  EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);449  EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);450  EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);451  EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);452  EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);453  EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record);454 455  // Emit abbreviation for RECORD_DIAG.456  Abbrev = std::make_shared<BitCodeAbbrev>();457  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));458  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3));  // Diag level.459  AddSourceLocationAbbrev(*Abbrev);460  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.461  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.462  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Text size.463  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.464  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));465 466  // Emit abbreviation for RECORD_CATEGORY.467  Abbrev = std::make_shared<BitCodeAbbrev>();468  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));469  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.470  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8));  // Text size.471  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // Category text.472  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));473 474  // Emit abbreviation for RECORD_SOURCE_RANGE.475  Abbrev = std::make_shared<BitCodeAbbrev>();476  Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));477  AddRangeLocationAbbrev(*Abbrev);478  Abbrevs.set(RECORD_SOURCE_RANGE,479              Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));480 481  // Emit the abbreviation for RECORD_DIAG_FLAG.482  Abbrev = std::make_shared<BitCodeAbbrev>();483  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));484  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.485  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.486  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.487  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,488                                                           Abbrev));489 490  // Emit the abbreviation for RECORD_FILENAME.491  Abbrev = std::make_shared<BitCodeAbbrev>();492  Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));493  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID.494  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size.495  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modification time.496  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.497  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.498  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,499                                                          Abbrev));500 501  // Emit the abbreviation for RECORD_FIXIT.502  Abbrev = std::make_shared<BitCodeAbbrev>();503  Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT));504  AddRangeLocationAbbrev(*Abbrev);505  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.506  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // FixIt text.507  Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,508                                                       Abbrev));509 510  Stream.ExitBlock();511}512 513void SDiagsWriter::EmitMetaBlock() {514  llvm::BitstreamWriter &Stream = State->Stream;515  AbbreviationMap &Abbrevs = State->Abbrevs;516 517  Stream.EnterSubblock(BLOCK_META, 3);518  RecordData::value_type Record[] = {RECORD_VERSION, VersionNumber};519  Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record);520  Stream.ExitBlock();521}522 523unsigned SDiagsWriter::getEmitCategory(unsigned int category) {524  if (!State->Categories.insert(category).second)525    return category;526 527  // We use a local version of 'Record' so that we can be generating528  // another record when we lazily generate one for the category entry.529  StringRef catName = DiagnosticIDs::getCategoryNameFromID(category);530  RecordData::value_type Record[] = {RECORD_CATEGORY, category, catName.size()};531  State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record,532                                   catName);533 534  return category;535}536 537unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,538                                             const Diagnostic *Diag) {539  if (!Diag || DiagLevel == DiagnosticsEngine::Note)540    return 0; // No flag for notes.541 542  StringRef FlagName =543      Diag->getDiags()->getDiagnosticIDs()->getWarningOptionForDiag(544          Diag->getID());545  return getEmitDiagnosticFlag(FlagName);546}547 548unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) {549  if (FlagName.empty())550    return 0;551 552  // Here we assume that FlagName points to static data whose pointer553  // value is fixed.  This allows us to unique by diagnostic groups.554  const void *data = FlagName.data();555  std::pair<unsigned, StringRef> &entry = State->DiagFlags[data];556  if (entry.first == 0) {557    entry.first = State->DiagFlags.size();558    entry.second = FlagName;559 560    // Lazily emit the string in a separate record.561    RecordData::value_type Record[] = {RECORD_DIAG_FLAG, entry.first,562                                       FlagName.size()};563    State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG),564                                     Record, FlagName);565  }566 567  return entry.first;568}569 570void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,571                                    const Diagnostic &Info) {572  assert(!IsFinishing &&573         "Received a diagnostic after we've already started teardown.");574  if (IsFinishing) {575    SmallString<256> diagnostic;576    Info.FormatDiagnostic(diagnostic);577    getMetaDiags()->Report(578        diag::warn_fe_serialized_diag_failure_during_finalization)579        << diagnostic;580    return;581  }582 583  // Enter the block for a non-note diagnostic immediately, rather than waiting584  // for beginDiagnostic, in case associated notes are emitted before we get585  // there.586  if (DiagLevel != DiagnosticsEngine::Note) {587    if (State->EmittedAnyDiagBlocks)588      ExitDiagBlock();589 590    EnterDiagBlock();591    State->EmittedAnyDiagBlocks = true;592  }593 594  // Compute the diagnostic text.595  State->diagBuf.clear();596  Info.FormatDiagnostic(State->diagBuf);597 598  if (Info.getLocation().isInvalid()) {599    // Special-case diagnostics with no location. We may not have entered a600    // source file in this case, so we can't use the normal DiagnosticsRenderer601    // machinery.602 603    // Make sure we bracket all notes as "sub-diagnostics".  This matches604    // the behavior in SDiagsRenderer::emitDiagnostic().605    if (DiagLevel == DiagnosticsEngine::Note)606      EnterDiagBlock();607 608    EmitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagLevel,609                          State->diagBuf, &Info);610 611    if (DiagLevel == DiagnosticsEngine::Note)612      ExitDiagBlock();613 614    return;615  }616 617  assert(Info.hasSourceManager() && LangOpts &&618         "Unexpected diagnostic with valid location outside of a source file");619  SDiagsRenderer Renderer(*this, *LangOpts, State->DiagOpts);620  Renderer.emitDiagnostic(621      FullSourceLoc(Info.getLocation(), Info.getSourceManager()), DiagLevel,622      State->diagBuf, Info.getRanges(), Info.getFixItHints(), &Info);623}624 625static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) {626  switch (Level) {627#define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X;628  CASE(Ignored)629  CASE(Note)630  CASE(Remark)631  CASE(Warning)632  CASE(Error)633  CASE(Fatal)634#undef CASE635  }636 637  llvm_unreachable("invalid diagnostic level");638}639 640void SDiagsWriter::EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,641                                         DiagnosticsEngine::Level Level,642                                         StringRef Message,643                                         DiagOrStoredDiag D) {644  llvm::BitstreamWriter &Stream = State->Stream;645  RecordData &Record = State->Record;646  AbbreviationMap &Abbrevs = State->Abbrevs;647 648  // Emit the RECORD_DIAG record.649  Record.clear();650  Record.push_back(RECORD_DIAG);651  Record.push_back(getStableLevel(Level));652  AddLocToRecord(Loc, PLoc, Record);653 654  if (const Diagnostic *Info = dyn_cast_if_present<const Diagnostic *>(D)) {655    // Emit the category string lazily and get the category ID.656    unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID());657    Record.push_back(getEmitCategory(DiagID));658    // Emit the diagnostic flag string lazily and get the mapped ID.659    Record.push_back(getEmitDiagnosticFlag(Level, Info));660  } else {661    Record.push_back(getEmitCategory());662    Record.push_back(getEmitDiagnosticFlag(Level));663  }664 665  Record.push_back(Message.size());666  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message);667}668 669void SDiagsRenderer::emitDiagnosticMessage(670    FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,671    StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,672    DiagOrStoredDiag D) {673  Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, D);674}675 676void SDiagsWriter::EnterDiagBlock() {677  State->Stream.EnterSubblock(BLOCK_DIAG, 4);678}679 680void SDiagsWriter::ExitDiagBlock() {681  State->Stream.ExitBlock();682}683 684void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D,685                                     DiagnosticsEngine::Level Level) {686  if (Level == DiagnosticsEngine::Note)687    Writer.EnterDiagBlock();688}689 690void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D,691                                   DiagnosticsEngine::Level Level) {692  // Only end note diagnostics here, because we can't be sure when we've seen693  // the last note associated with a non-note diagnostic.694  if (Level == DiagnosticsEngine::Note)695    Writer.ExitDiagBlock();696}697 698void SDiagsWriter::EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges,699                                   ArrayRef<FixItHint> Hints,700                                   const SourceManager &SM) {701  llvm::BitstreamWriter &Stream = State->Stream;702  RecordData &Record = State->Record;703  AbbreviationMap &Abbrevs = State->Abbrevs;704 705  // Emit Source Ranges.706  for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end();707       I != E; ++I)708    if (I->isValid())709      EmitCharSourceRange(*I, SM);710 711  // Emit FixIts.712  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();713       I != E; ++I) {714    const FixItHint &Fix = *I;715    if (Fix.isNull())716      continue;717    Record.clear();718    Record.push_back(RECORD_FIXIT);719    AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM);720    Record.push_back(Fix.CodeToInsert.size());721    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record,722                              Fix.CodeToInsert);723  }724}725 726void SDiagsRenderer::emitCodeContext(FullSourceLoc Loc,727                                     DiagnosticsEngine::Level Level,728                                     SmallVectorImpl<CharSourceRange> &Ranges,729                                     ArrayRef<FixItHint> Hints) {730  Writer.EmitCodeContext(Ranges, Hints, Loc.getManager());731}732 733void SDiagsRenderer::emitNote(FullSourceLoc Loc, StringRef Message) {734  Writer.EnterDiagBlock();735  PresumedLoc PLoc = Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc();736  Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, Message,737                               DiagOrStoredDiag());738  Writer.ExitDiagBlock();739}740 741DiagnosticsEngine *SDiagsWriter::getMetaDiags() {742  // FIXME: It's slightly absurd to create a new diagnostics engine here, but743  // the other options that are available today are worse:744  //745  // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a746  //    part of. The DiagnosticsEngine would need to know not to send747  //    diagnostics back to the consumer that failed. This would require us to748  //    rework ChainedDiagnosticsConsumer and teach the engine about multiple749  //    consumers, which is difficult today because most APIs interface with750  //    consumers rather than the engine itself.751  //752  // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need753  //    to be distinct from the engine the writer was being added to and would754  //    normally not be used.755  if (!State->MetaDiagnostics) {756    auto Client = new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts);757    State->MetaDiagnostics = std::make_unique<DiagnosticsEngine>(758        DiagnosticIDs::create(), State->DiagOpts, Client);759  }760  return State->MetaDiagnostics.get();761}762 763void SDiagsWriter::RemoveOldDiagnostics() {764  if (!llvm::sys::fs::remove(State->OutputFile))765    return;766 767  getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure);768  // Disable merging child records, as whatever is in this file may be769  // misleading.770  MergeChildRecords = false;771}772 773void SDiagsWriter::finish() {774  assert(!IsFinishing);775  IsFinishing = true;776 777  // The original instance is responsible for writing the file.778  if (!OriginalInstance)779    return;780 781  // Finish off any diagnostic we were in the process of emitting.782  if (State->EmittedAnyDiagBlocks)783    ExitDiagBlock();784 785  if (MergeChildRecords) {786    if (!State->EmittedAnyDiagBlocks)787      // We have no diagnostics of our own, so we can just leave the child788      // process' output alone789      return;790 791    if (llvm::sys::fs::exists(State->OutputFile))792      if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str()))793        getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure);794  }795 796  std::error_code EC;797  auto OS = std::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(),798                                                    EC, llvm::sys::fs::OF_None);799  if (EC) {800    getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure)801        << State->OutputFile << EC.message();802    OS->clear_error();803    return;804  }805 806  // Write the generated bitstream to "Out".807  OS->write((char *)&State->Buffer.front(), State->Buffer.size());808  OS->flush();809 810  assert(!OS->has_error());811  if (OS->has_error()) {812    getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure)813        << State->OutputFile << OS->error().message();814    OS->clear_error();815  }816}817 818std::error_code SDiagsMerger::visitStartOfDiagnostic() {819  Writer.EnterDiagBlock();820  return std::error_code();821}822 823std::error_code SDiagsMerger::visitEndOfDiagnostic() {824  Writer.ExitDiagBlock();825  return std::error_code();826}827 828std::error_code829SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start,830                                     const serialized_diags::Location &End) {831  RecordData::value_type Record[] = {832      RECORD_SOURCE_RANGE, FileLookup[Start.FileID], Start.Line, Start.Col,833      Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset};834  Writer.State->Stream.EmitRecordWithAbbrev(835      Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record);836  return std::error_code();837}838 839std::error_code SDiagsMerger::visitDiagnosticRecord(840    unsigned Severity, const serialized_diags::Location &Location,841    unsigned Category, unsigned Flag, StringRef Message) {842  RecordData::value_type Record[] = {843      RECORD_DIAG, Severity, FileLookup[Location.FileID], Location.Line,844      Location.Col, Location.Offset, CategoryLookup[Category],845      Flag ? DiagFlagLookup[Flag] : 0, Message.size()};846 847  Writer.State->Stream.EmitRecordWithBlob(848      Writer.State->Abbrevs.get(RECORD_DIAG), Record, Message);849  return std::error_code();850}851 852std::error_code853SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start,854                               const serialized_diags::Location &End,855                               StringRef Text) {856  RecordData::value_type Record[] = {RECORD_FIXIT, FileLookup[Start.FileID],857                                     Start.Line, Start.Col, Start.Offset,858                                     FileLookup[End.FileID], End.Line, End.Col,859                                     End.Offset, Text.size()};860 861  Writer.State->Stream.EmitRecordWithBlob(862      Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text);863  return std::error_code();864}865 866std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size,867                                                  unsigned Timestamp,868                                                  StringRef Name) {869  FileLookup[ID] = Writer.getEmitFile(Name.str().c_str());870  return std::error_code();871}872 873std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) {874  CategoryLookup[ID] = Writer.getEmitCategory(ID);875  return std::error_code();876}877 878std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) {879  DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name);880  return std::error_code();881}882