//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DiagnosticRenderer.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/SerializedDiagnosticReader.h" #include "clang/Frontend/SerializedDiagnostics.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Bitstream/BitCodes.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" #include using namespace clang; using namespace clang::serialized_diags; namespace { class AbbreviationMap { llvm::DenseMap Abbrevs; public: AbbreviationMap() {} void set(unsigned recordID, unsigned abbrevID) { assert(!Abbrevs.contains(recordID) && "Abbreviation already set."); Abbrevs[recordID] = abbrevID; } unsigned get(unsigned recordID) { assert(Abbrevs.contains(recordID) && "Abbreviation not set."); return Abbrevs[recordID]; } }; typedef SmallVector RecordData; typedef SmallVectorImpl RecordDataImpl; typedef ArrayRef RecordDataRef; class SDiagsWriter; class SDiagsRenderer : public DiagnosticNoteRenderer { SDiagsWriter &Writer; public: SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts, DiagnosticOptions *DiagOpts) : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {} ~SDiagsRenderer() override {} protected: void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef Ranges, DiagOrStoredDiag D) override; void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef Ranges) override {} void emitNote(FullSourceLoc Loc, StringRef Message) override; void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl &Ranges, ArrayRef Hints) override; void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) override; void endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) override; }; typedef llvm::DenseMap AbbrevLookup; class SDiagsMerger : SerializedDiagnosticReader { SDiagsWriter &Writer; AbbrevLookup FileLookup; AbbrevLookup CategoryLookup; AbbrevLookup DiagFlagLookup; public: SDiagsMerger(SDiagsWriter &Writer) : Writer(Writer) {} std::error_code mergeRecordsFromFile(const char *File) { return readDiagnostics(File); } protected: std::error_code visitStartOfDiagnostic() override; std::error_code visitEndOfDiagnostic() override; std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; std::error_code visitDiagnosticRecord( unsigned Severity, const serialized_diags::Location &Location, unsigned Category, unsigned Flag, StringRef Message) override; std::error_code visitFilenameRecord(unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name) override; std::error_code visitFixitRecord(const serialized_diags::Location &Start, const serialized_diags::Location &End, StringRef CodeToInsert) override; std::error_code visitSourceRangeRecord(const serialized_diags::Location &Start, const serialized_diags::Location &End) override; private: std::error_code adjustSourceLocFilename(RecordData &Record, unsigned int offset); void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup, unsigned NewAbbrev); void writeRecordWithAbbrev(unsigned ID, RecordData &Record); void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob); }; class SDiagsWriter : public DiagnosticConsumer { friend class SDiagsRenderer; friend class SDiagsMerger; struct SharedState; explicit SDiagsWriter(std::shared_ptr State) : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false), State(std::move(State)) {} public: SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) : LangOpts(nullptr), OriginalInstance(true), MergeChildRecords(MergeChildRecords), State(std::make_shared(File, Diags)) { if (MergeChildRecords) RemoveOldDiagnostics(); EmitPreamble(); } ~SDiagsWriter() override {} void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override; void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override { LangOpts = &LO; } void finish() override; private: /// Build a DiagnosticsEngine to emit diagnostics about the diagnostics DiagnosticsEngine *getMetaDiags(); /// Remove old copies of the serialized diagnostics. This is necessary /// so that we can detect when subprocesses write diagnostics that we should /// merge into our own. void RemoveOldDiagnostics(); /// Emit the preamble for the serialized diagnostics. void EmitPreamble(); /// Emit the BLOCKINFO block. void EmitBlockInfoBlock(); /// Emit the META data block. void EmitMetaBlock(); /// Start a DIAG block. void EnterDiagBlock(); /// End a DIAG block. void ExitDiagBlock(); /// Emit a DIAG record. void EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, DiagOrStoredDiag D); /// Emit FIXIT and SOURCE_RANGE records for a diagnostic. void EmitCodeContext(SmallVectorImpl &Ranges, ArrayRef Hints, const SourceManager &SM); /// Emit a record for a CharSourceRange. void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); /// Emit the string information for the category. unsigned getEmitCategory(unsigned category = 0); /// Emit the string information for diagnostic flags. unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, unsigned DiagID = 0); unsigned getEmitDiagnosticFlag(StringRef DiagName); /// Emit (lazily) the file string and retrieved the file identifier. unsigned getEmitFile(const char *Filename); /// Add SourceLocation information the specified record. void AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc, RecordDataImpl &Record, unsigned TokSize = 0); /// Add SourceLocation information the specified record. void AddLocToRecord(FullSourceLoc Loc, RecordDataImpl &Record, unsigned TokSize = 0) { AddLocToRecord(Loc, Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(), Record, TokSize); } /// Add CharSourceRange information the specified record. void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, const SourceManager &SM); /// Language options, which can differ from one clone of this client /// to another. const LangOptions *LangOpts; /// Whether this is the original instance (rather than one of its /// clones), responsible for writing the file at the end. bool OriginalInstance; /// Whether this instance should aggregate diagnostics that are /// generated from child processes. bool MergeChildRecords; /// Whether we've started finishing and tearing down this instance. bool IsFinishing = false; /// State that is shared among the various clones of this diagnostic /// consumer. struct SharedState { SharedState(StringRef File, DiagnosticOptions *Diags) : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), EmittedAnyDiagBlocks(false) {} /// Diagnostic options. IntrusiveRefCntPtr DiagOpts; /// The byte buffer for the serialized content. SmallString<1024> Buffer; /// The BitStreamWriter for the serialized diagnostics. llvm::BitstreamWriter Stream; /// The name of the diagnostics file. std::string OutputFile; /// The set of constructed record abbreviations. AbbreviationMap Abbrevs; /// A utility buffer for constructing record content. RecordData Record; /// A text buffer for rendering diagnostic text. SmallString<256> diagBuf; /// The collection of diagnostic categories used. llvm::DenseSet Categories; /// The collection of files used. llvm::DenseMap Files; typedef llvm::DenseMap > DiagFlagsTy; /// Map for uniquing strings. DiagFlagsTy DiagFlags; /// Whether we have already started emission of any DIAG blocks. Once /// this becomes \c true, we never close a DIAG block until we know that we're /// starting another one or we're done. bool EmittedAnyDiagBlocks; /// Engine for emitting diagnostics about the diagnostics. std::unique_ptr MetaDiagnostics; }; /// State shared among the various clones of this diagnostic consumer. std::shared_ptr State; }; } // end anonymous namespace namespace clang { namespace serialized_diags { std::unique_ptr create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { return std::make_unique(OutputFile, Diags, MergeChildRecords); } } // end namespace serialized_diags } // end namespace clang //===----------------------------------------------------------------------===// // Serialization methods. //===----------------------------------------------------------------------===// /// Emits a block ID in the BLOCKINFO block. static void EmitBlockID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, RecordDataImpl &Record) { Record.clear(); Record.push_back(ID); Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); // Emit the block name if present. if (!Name || Name[0] == 0) return; Record.clear(); while (*Name) Record.push_back(*Name++); Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); } /// Emits a record ID in the BLOCKINFO block. static void EmitRecordID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, RecordDataImpl &Record){ Record.clear(); Record.push_back(ID); while (*Name) Record.push_back(*Name++); Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); } void SDiagsWriter::AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc, RecordDataImpl &Record, unsigned TokSize) { if (PLoc.isInvalid()) { // Emit a "sentinel" location. Record.push_back((unsigned)0); // File. Record.push_back((unsigned)0); // Line. Record.push_back((unsigned)0); // Column. Record.push_back((unsigned)0); // Offset. return; } Record.push_back(getEmitFile(PLoc.getFilename())); Record.push_back(PLoc.getLine()); Record.push_back(PLoc.getColumn()+TokSize); Record.push_back(Loc.getFileOffset()); } void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, RecordDataImpl &Record, const SourceManager &SM) { AddLocToRecord(FullSourceLoc(Range.getBegin(), SM), Record); unsigned TokSize = 0; if (Range.isTokenRange()) TokSize = Lexer::MeasureTokenLength(Range.getEnd(), SM, *LangOpts); AddLocToRecord(FullSourceLoc(Range.getEnd(), SM), Record, TokSize); } unsigned SDiagsWriter::getEmitFile(const char *FileName){ if (!FileName) return 0; unsigned &entry = State->Files[FileName]; if (entry) return entry; // Lazily generate the record for the file. entry = State->Files.size(); StringRef Name(FileName); RecordData::value_type Record[] = {RECORD_FILENAME, entry, 0 /* For legacy */, 0 /* For legacy */, Name.size()}; State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record, Name); return entry; } void SDiagsWriter::EmitCharSourceRange(CharSourceRange R, const SourceManager &SM) { State->Record.clear(); State->Record.push_back(RECORD_SOURCE_RANGE); AddCharSourceRangeToRecord(R, State->Record, SM); State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE), State->Record); } /// Emits the preamble of the diagnostics file. void SDiagsWriter::EmitPreamble() { // Emit the file header. State->Stream.Emit((unsigned)'D', 8); State->Stream.Emit((unsigned)'I', 8); State->Stream.Emit((unsigned)'A', 8); State->Stream.Emit((unsigned)'G', 8); EmitBlockInfoBlock(); EmitMetaBlock(); } static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) { using namespace llvm; Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; } static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) { AddSourceLocationAbbrev(Abbrev); AddSourceLocationAbbrev(Abbrev); } void SDiagsWriter::EmitBlockInfoBlock() { State->Stream.EnterBlockInfoBlock(); using namespace llvm; llvm::BitstreamWriter &Stream = State->Stream; RecordData &Record = State->Record; AbbreviationMap &Abbrevs = State->Abbrevs; // ==---------------------------------------------------------------------==// // The subsequent records and Abbrevs are for the "Meta" block. // ==---------------------------------------------------------------------==// EmitBlockID(BLOCK_META, "Meta", Stream, Record); EmitRecordID(RECORD_VERSION, "Version", Stream, Record); auto Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev)); // ==---------------------------------------------------------------------==// // The subsequent records and Abbrevs are for the "Diagnostic" block. // ==---------------------------------------------------------------------==// EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); // Emit abbreviation for RECORD_DIAG. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. AddSourceLocationAbbrev(*Abbrev); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Text size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); // Emit abbreviation for RECORD_CATEGORY. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); // Emit abbreviation for RECORD_SOURCE_RANGE. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); AddRangeLocationAbbrev(*Abbrev); Abbrevs.set(RECORD_SOURCE_RANGE, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); // Emit the abbreviation for RECORD_DIAG_FLAG. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); // Emit the abbreviation for RECORD_FILENAME. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modification time. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); // Emit the abbreviation for RECORD_FIXIT. Abbrev = std::make_shared(); Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); AddRangeLocationAbbrev(*Abbrev); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); Stream.ExitBlock(); } void SDiagsWriter::EmitMetaBlock() { llvm::BitstreamWriter &Stream = State->Stream; AbbreviationMap &Abbrevs = State->Abbrevs; Stream.EnterSubblock(BLOCK_META, 3); RecordData::value_type Record[] = {RECORD_VERSION, VersionNumber}; Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); Stream.ExitBlock(); } unsigned SDiagsWriter::getEmitCategory(unsigned int category) { if (!State->Categories.insert(category).second) return category; // We use a local version of 'Record' so that we can be generating // another record when we lazily generate one for the category entry. StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); RecordData::value_type Record[] = {RECORD_CATEGORY, category, catName.size()}; State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record, catName); return category; } unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, unsigned DiagID) { if (DiagLevel == DiagnosticsEngine::Note) return 0; // No flag for notes. StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); return getEmitDiagnosticFlag(FlagName); } unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) { if (FlagName.empty()) return 0; // Here we assume that FlagName points to static data whose pointer // value is fixed. This allows us to unique by diagnostic groups. const void *data = FlagName.data(); std::pair &entry = State->DiagFlags[data]; if (entry.first == 0) { entry.first = State->DiagFlags.size(); entry.second = FlagName; // Lazily emit the string in a separate record. RecordData::value_type Record[] = {RECORD_DIAG_FLAG, entry.first, FlagName.size()}; State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), Record, FlagName); } return entry.first; } void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { assert(!IsFinishing && "Received a diagnostic after we've already started teardown."); if (IsFinishing) { SmallString<256> diagnostic; Info.FormatDiagnostic(diagnostic); getMetaDiags()->Report( diag::warn_fe_serialized_diag_failure_during_finalization) << diagnostic; return; } // Enter the block for a non-note diagnostic immediately, rather than waiting // for beginDiagnostic, in case associated notes are emitted before we get // there. if (DiagLevel != DiagnosticsEngine::Note) { if (State->EmittedAnyDiagBlocks) ExitDiagBlock(); EnterDiagBlock(); State->EmittedAnyDiagBlocks = true; } // Compute the diagnostic text. State->diagBuf.clear(); Info.FormatDiagnostic(State->diagBuf); if (Info.getLocation().isInvalid()) { // Special-case diagnostics with no location. We may not have entered a // source file in this case, so we can't use the normal DiagnosticsRenderer // machinery. // Make sure we bracket all notes as "sub-diagnostics". This matches // the behavior in SDiagsRenderer::emitDiagnostic(). if (DiagLevel == DiagnosticsEngine::Note) EnterDiagBlock(); EmitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagLevel, State->diagBuf, &Info); if (DiagLevel == DiagnosticsEngine::Note) ExitDiagBlock(); return; } assert(Info.hasSourceManager() && LangOpts && "Unexpected diagnostic with valid location outside of a source file"); SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts); Renderer.emitDiagnostic( FullSourceLoc(Info.getLocation(), Info.getSourceManager()), DiagLevel, State->diagBuf, Info.getRanges(), Info.getFixItHints(), &Info); } static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) { switch (Level) { #define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X; CASE(Ignored) CASE(Note) CASE(Remark) CASE(Warning) CASE(Error) CASE(Fatal) #undef CASE } llvm_unreachable("invalid diagnostic level"); } void SDiagsWriter::EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, DiagOrStoredDiag D) { llvm::BitstreamWriter &Stream = State->Stream; RecordData &Record = State->Record; AbbreviationMap &Abbrevs = State->Abbrevs; // Emit the RECORD_DIAG record. Record.clear(); Record.push_back(RECORD_DIAG); Record.push_back(getStableLevel(Level)); AddLocToRecord(Loc, PLoc, Record); if (const Diagnostic *Info = D.dyn_cast()) { // Emit the category string lazily and get the category ID. unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); Record.push_back(getEmitCategory(DiagID)); // Emit the diagnostic flag string lazily and get the mapped ID. Record.push_back(getEmitDiagnosticFlag(Level, Info->getID())); } else { Record.push_back(getEmitCategory()); Record.push_back(getEmitDiagnosticFlag(Level)); } Record.push_back(Message.size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message); } void SDiagsRenderer::emitDiagnosticMessage( FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef Ranges, DiagOrStoredDiag D) { Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, D); } void SDiagsWriter::EnterDiagBlock() { State->Stream.EnterSubblock(BLOCK_DIAG, 4); } void SDiagsWriter::ExitDiagBlock() { State->Stream.ExitBlock(); } void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) { if (Level == DiagnosticsEngine::Note) Writer.EnterDiagBlock(); } void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) { // Only end note diagnostics here, because we can't be sure when we've seen // the last note associated with a non-note diagnostic. if (Level == DiagnosticsEngine::Note) Writer.ExitDiagBlock(); } void SDiagsWriter::EmitCodeContext(SmallVectorImpl &Ranges, ArrayRef Hints, const SourceManager &SM) { llvm::BitstreamWriter &Stream = State->Stream; RecordData &Record = State->Record; AbbreviationMap &Abbrevs = State->Abbrevs; // Emit Source Ranges. for (ArrayRef::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) if (I->isValid()) EmitCharSourceRange(*I, SM); // Emit FixIts. for (ArrayRef::iterator I = Hints.begin(), E = Hints.end(); I != E; ++I) { const FixItHint &Fix = *I; if (Fix.isNull()) continue; Record.clear(); Record.push_back(RECORD_FIXIT); AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM); Record.push_back(Fix.CodeToInsert.size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, Fix.CodeToInsert); } } void SDiagsRenderer::emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl &Ranges, ArrayRef Hints) { Writer.EmitCodeContext(Ranges, Hints, Loc.getManager()); } void SDiagsRenderer::emitNote(FullSourceLoc Loc, StringRef Message) { Writer.EnterDiagBlock(); PresumedLoc PLoc = Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(); Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, Message, DiagOrStoredDiag()); Writer.ExitDiagBlock(); } DiagnosticsEngine *SDiagsWriter::getMetaDiags() { // FIXME: It's slightly absurd to create a new diagnostics engine here, but // the other options that are available today are worse: // // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a // part of. The DiagnosticsEngine would need to know not to send // diagnostics back to the consumer that failed. This would require us to // rework ChainedDiagnosticsConsumer and teach the engine about multiple // consumers, which is difficult today because most APIs interface with // consumers rather than the engine itself. // // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need // to be distinct from the engine the writer was being added to and would // normally not be used. if (!State->MetaDiagnostics) { IntrusiveRefCntPtr IDs(new DiagnosticIDs()); auto Client = new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get()); State->MetaDiagnostics = std::make_unique( IDs, State->DiagOpts.get(), Client); } return State->MetaDiagnostics.get(); } void SDiagsWriter::RemoveOldDiagnostics() { if (!llvm::sys::fs::remove(State->OutputFile)) return; getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); // Disable merging child records, as whatever is in this file may be // misleading. MergeChildRecords = false; } void SDiagsWriter::finish() { assert(!IsFinishing); IsFinishing = true; // The original instance is responsible for writing the file. if (!OriginalInstance) return; // Finish off any diagnostic we were in the process of emitting. if (State->EmittedAnyDiagBlocks) ExitDiagBlock(); if (MergeChildRecords) { if (!State->EmittedAnyDiagBlocks) // We have no diagnostics of our own, so we can just leave the child // process' output alone return; if (llvm::sys::fs::exists(State->OutputFile)) if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str())) getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); } std::error_code EC; auto OS = std::make_unique(State->OutputFile.c_str(), EC, llvm::sys::fs::OF_None); if (EC) { getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) << State->OutputFile << EC.message(); OS->clear_error(); return; } // Write the generated bitstream to "Out". OS->write((char *)&State->Buffer.front(), State->Buffer.size()); OS->flush(); assert(!OS->has_error()); if (OS->has_error()) { getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) << State->OutputFile << OS->error().message(); OS->clear_error(); } } std::error_code SDiagsMerger::visitStartOfDiagnostic() { Writer.EnterDiagBlock(); return std::error_code(); } std::error_code SDiagsMerger::visitEndOfDiagnostic() { Writer.ExitDiagBlock(); return std::error_code(); } std::error_code SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start, const serialized_diags::Location &End) { RecordData::value_type Record[] = { RECORD_SOURCE_RANGE, FileLookup[Start.FileID], Start.Line, Start.Col, Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset}; Writer.State->Stream.EmitRecordWithAbbrev( Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record); return std::error_code(); } std::error_code SDiagsMerger::visitDiagnosticRecord( unsigned Severity, const serialized_diags::Location &Location, unsigned Category, unsigned Flag, StringRef Message) { RecordData::value_type Record[] = { RECORD_DIAG, Severity, FileLookup[Location.FileID], Location.Line, Location.Col, Location.Offset, CategoryLookup[Category], Flag ? DiagFlagLookup[Flag] : 0, Message.size()}; Writer.State->Stream.EmitRecordWithBlob( Writer.State->Abbrevs.get(RECORD_DIAG), Record, Message); return std::error_code(); } std::error_code SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start, const serialized_diags::Location &End, StringRef Text) { RecordData::value_type Record[] = {RECORD_FIXIT, FileLookup[Start.FileID], Start.Line, Start.Col, Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset, Text.size()}; Writer.State->Stream.EmitRecordWithBlob( Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text); return std::error_code(); } std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name) { FileLookup[ID] = Writer.getEmitFile(Name.str().c_str()); return std::error_code(); } std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { CategoryLookup[ID] = Writer.getEmitCategory(ID); return std::error_code(); } std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) { DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name); return std::error_code(); }