Path: blob/main/contrib/llvm-project/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
35233 views
//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Frontend/SerializedDiagnosticPrinter.h"9#include "clang/Basic/Diagnostic.h"10#include "clang/Basic/DiagnosticOptions.h"11#include "clang/Basic/SourceManager.h"12#include "clang/Frontend/DiagnosticRenderer.h"13#include "clang/Frontend/FrontendDiagnostic.h"14#include "clang/Frontend/SerializedDiagnosticReader.h"15#include "clang/Frontend/SerializedDiagnostics.h"16#include "clang/Frontend/TextDiagnosticPrinter.h"17#include "clang/Lex/Lexer.h"18#include "llvm/ADT/DenseSet.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/SmallString.h"21#include "llvm/ADT/StringRef.h"22#include "llvm/Bitstream/BitCodes.h"23#include "llvm/Bitstream/BitstreamReader.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/raw_ostream.h"26#include <utility>2728using namespace clang;29using namespace clang::serialized_diags;3031namespace {3233class AbbreviationMap {34llvm::DenseMap<unsigned, unsigned> Abbrevs;35public:36AbbreviationMap() {}3738void set(unsigned recordID, unsigned abbrevID) {39assert(!Abbrevs.contains(recordID) && "Abbreviation already set.");40Abbrevs[recordID] = abbrevID;41}4243unsigned get(unsigned recordID) {44assert(Abbrevs.contains(recordID) && "Abbreviation not set.");45return Abbrevs[recordID];46}47};4849typedef SmallVector<uint64_t, 64> RecordData;50typedef SmallVectorImpl<uint64_t> RecordDataImpl;51typedef ArrayRef<uint64_t> RecordDataRef;5253class SDiagsWriter;5455class SDiagsRenderer : public DiagnosticNoteRenderer {56SDiagsWriter &Writer;57public:58SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts,59DiagnosticOptions *DiagOpts)60: DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {}6162~SDiagsRenderer() override {}6364protected:65void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,66DiagnosticsEngine::Level Level, StringRef Message,67ArrayRef<CharSourceRange> Ranges,68DiagOrStoredDiag D) override;6970void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,71DiagnosticsEngine::Level Level,72ArrayRef<CharSourceRange> Ranges) override {}7374void emitNote(FullSourceLoc Loc, StringRef Message) override;7576void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,77SmallVectorImpl<CharSourceRange> &Ranges,78ArrayRef<FixItHint> Hints) override;7980void beginDiagnostic(DiagOrStoredDiag D,81DiagnosticsEngine::Level Level) override;82void endDiagnostic(DiagOrStoredDiag D,83DiagnosticsEngine::Level Level) override;84};8586typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup;8788class SDiagsMerger : SerializedDiagnosticReader {89SDiagsWriter &Writer;90AbbrevLookup FileLookup;91AbbrevLookup CategoryLookup;92AbbrevLookup DiagFlagLookup;9394public:95SDiagsMerger(SDiagsWriter &Writer) : Writer(Writer) {}9697std::error_code mergeRecordsFromFile(const char *File) {98return readDiagnostics(File);99}100101protected:102std::error_code visitStartOfDiagnostic() override;103std::error_code visitEndOfDiagnostic() override;104std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override;105std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override;106std::error_code visitDiagnosticRecord(107unsigned Severity, const serialized_diags::Location &Location,108unsigned Category, unsigned Flag, StringRef Message) override;109std::error_code visitFilenameRecord(unsigned ID, unsigned Size,110unsigned Timestamp,111StringRef Name) override;112std::error_code visitFixitRecord(const serialized_diags::Location &Start,113const serialized_diags::Location &End,114StringRef CodeToInsert) override;115std::error_code116visitSourceRangeRecord(const serialized_diags::Location &Start,117const serialized_diags::Location &End) override;118119private:120std::error_code adjustSourceLocFilename(RecordData &Record,121unsigned int offset);122123void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup,124unsigned NewAbbrev);125126void writeRecordWithAbbrev(unsigned ID, RecordData &Record);127128void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob);129};130131class SDiagsWriter : public DiagnosticConsumer {132friend class SDiagsRenderer;133friend class SDiagsMerger;134135struct SharedState;136137explicit SDiagsWriter(std::shared_ptr<SharedState> State)138: LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false),139State(std::move(State)) {}140141public:142SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords)143: LangOpts(nullptr), OriginalInstance(true),144MergeChildRecords(MergeChildRecords),145State(std::make_shared<SharedState>(File, Diags)) {146if (MergeChildRecords)147RemoveOldDiagnostics();148EmitPreamble();149}150151~SDiagsWriter() override {}152153void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,154const Diagnostic &Info) override;155156void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override {157LangOpts = &LO;158}159160void finish() override;161162private:163/// Build a DiagnosticsEngine to emit diagnostics about the diagnostics164DiagnosticsEngine *getMetaDiags();165166/// Remove old copies of the serialized diagnostics. This is necessary167/// so that we can detect when subprocesses write diagnostics that we should168/// merge into our own.169void RemoveOldDiagnostics();170171/// Emit the preamble for the serialized diagnostics.172void EmitPreamble();173174/// Emit the BLOCKINFO block.175void EmitBlockInfoBlock();176177/// Emit the META data block.178void EmitMetaBlock();179180/// Start a DIAG block.181void EnterDiagBlock();182183/// End a DIAG block.184void ExitDiagBlock();185186/// Emit a DIAG record.187void EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,188DiagnosticsEngine::Level Level, StringRef Message,189DiagOrStoredDiag D);190191/// Emit FIXIT and SOURCE_RANGE records for a diagnostic.192void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges,193ArrayRef<FixItHint> Hints,194const SourceManager &SM);195196/// Emit a record for a CharSourceRange.197void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM);198199/// Emit the string information for the category.200unsigned getEmitCategory(unsigned category = 0);201202/// Emit the string information for diagnostic flags.203unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,204unsigned DiagID = 0);205206unsigned getEmitDiagnosticFlag(StringRef DiagName);207208/// Emit (lazily) the file string and retrieved the file identifier.209unsigned getEmitFile(const char *Filename);210211/// Add SourceLocation information the specified record.212void AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc,213RecordDataImpl &Record, unsigned TokSize = 0);214215/// Add SourceLocation information the specified record.216void AddLocToRecord(FullSourceLoc Loc, RecordDataImpl &Record,217unsigned TokSize = 0) {218AddLocToRecord(Loc, Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(),219Record, TokSize);220}221222/// Add CharSourceRange information the specified record.223void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record,224const SourceManager &SM);225226/// Language options, which can differ from one clone of this client227/// to another.228const LangOptions *LangOpts;229230/// Whether this is the original instance (rather than one of its231/// clones), responsible for writing the file at the end.232bool OriginalInstance;233234/// Whether this instance should aggregate diagnostics that are235/// generated from child processes.236bool MergeChildRecords;237238/// Whether we've started finishing and tearing down this instance.239bool IsFinishing = false;240241/// State that is shared among the various clones of this diagnostic242/// consumer.243struct SharedState {244SharedState(StringRef File, DiagnosticOptions *Diags)245: DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()),246EmittedAnyDiagBlocks(false) {}247248/// Diagnostic options.249IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;250251/// The byte buffer for the serialized content.252SmallString<1024> Buffer;253254/// The BitStreamWriter for the serialized diagnostics.255llvm::BitstreamWriter Stream;256257/// The name of the diagnostics file.258std::string OutputFile;259260/// The set of constructed record abbreviations.261AbbreviationMap Abbrevs;262263/// A utility buffer for constructing record content.264RecordData Record;265266/// A text buffer for rendering diagnostic text.267SmallString<256> diagBuf;268269/// The collection of diagnostic categories used.270llvm::DenseSet<unsigned> Categories;271272/// The collection of files used.273llvm::DenseMap<const char *, unsigned> Files;274275typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> >276DiagFlagsTy;277278/// Map for uniquing strings.279DiagFlagsTy DiagFlags;280281/// Whether we have already started emission of any DIAG blocks. Once282/// this becomes \c true, we never close a DIAG block until we know that we're283/// starting another one or we're done.284bool EmittedAnyDiagBlocks;285286/// Engine for emitting diagnostics about the diagnostics.287std::unique_ptr<DiagnosticsEngine> MetaDiagnostics;288};289290/// State shared among the various clones of this diagnostic consumer.291std::shared_ptr<SharedState> State;292};293} // end anonymous namespace294295namespace clang {296namespace serialized_diags {297std::unique_ptr<DiagnosticConsumer>298create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) {299return std::make_unique<SDiagsWriter>(OutputFile, Diags, MergeChildRecords);300}301302} // end namespace serialized_diags303} // end namespace clang304305//===----------------------------------------------------------------------===//306// Serialization methods.307//===----------------------------------------------------------------------===//308309/// Emits a block ID in the BLOCKINFO block.310static void EmitBlockID(unsigned ID, const char *Name,311llvm::BitstreamWriter &Stream,312RecordDataImpl &Record) {313Record.clear();314Record.push_back(ID);315Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);316317// Emit the block name if present.318if (!Name || Name[0] == 0)319return;320321Record.clear();322323while (*Name)324Record.push_back(*Name++);325326Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);327}328329/// Emits a record ID in the BLOCKINFO block.330static void EmitRecordID(unsigned ID, const char *Name,331llvm::BitstreamWriter &Stream,332RecordDataImpl &Record){333Record.clear();334Record.push_back(ID);335336while (*Name)337Record.push_back(*Name++);338339Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);340}341342void SDiagsWriter::AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc,343RecordDataImpl &Record, unsigned TokSize) {344if (PLoc.isInvalid()) {345// Emit a "sentinel" location.346Record.push_back((unsigned)0); // File.347Record.push_back((unsigned)0); // Line.348Record.push_back((unsigned)0); // Column.349Record.push_back((unsigned)0); // Offset.350return;351}352353Record.push_back(getEmitFile(PLoc.getFilename()));354Record.push_back(PLoc.getLine());355Record.push_back(PLoc.getColumn()+TokSize);356Record.push_back(Loc.getFileOffset());357}358359void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range,360RecordDataImpl &Record,361const SourceManager &SM) {362AddLocToRecord(FullSourceLoc(Range.getBegin(), SM), Record);363unsigned TokSize = 0;364if (Range.isTokenRange())365TokSize = Lexer::MeasureTokenLength(Range.getEnd(),366SM, *LangOpts);367368AddLocToRecord(FullSourceLoc(Range.getEnd(), SM), Record, TokSize);369}370371unsigned SDiagsWriter::getEmitFile(const char *FileName){372if (!FileName)373return 0;374375unsigned &entry = State->Files[FileName];376if (entry)377return entry;378379// Lazily generate the record for the file.380entry = State->Files.size();381StringRef Name(FileName);382RecordData::value_type Record[] = {RECORD_FILENAME, entry, 0 /* For legacy */,3830 /* For legacy */, Name.size()};384State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record,385Name);386387return entry;388}389390void SDiagsWriter::EmitCharSourceRange(CharSourceRange R,391const SourceManager &SM) {392State->Record.clear();393State->Record.push_back(RECORD_SOURCE_RANGE);394AddCharSourceRangeToRecord(R, State->Record, SM);395State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE),396State->Record);397}398399/// Emits the preamble of the diagnostics file.400void SDiagsWriter::EmitPreamble() {401// Emit the file header.402State->Stream.Emit((unsigned)'D', 8);403State->Stream.Emit((unsigned)'I', 8);404State->Stream.Emit((unsigned)'A', 8);405State->Stream.Emit((unsigned)'G', 8);406407EmitBlockInfoBlock();408EmitMetaBlock();409}410411static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) {412using namespace llvm;413Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID.414Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.415Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.416Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;417}418419static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) {420AddSourceLocationAbbrev(Abbrev);421AddSourceLocationAbbrev(Abbrev);422}423424void SDiagsWriter::EmitBlockInfoBlock() {425State->Stream.EnterBlockInfoBlock();426427using namespace llvm;428llvm::BitstreamWriter &Stream = State->Stream;429RecordData &Record = State->Record;430AbbreviationMap &Abbrevs = State->Abbrevs;431432// ==---------------------------------------------------------------------==//433// The subsequent records and Abbrevs are for the "Meta" block.434// ==---------------------------------------------------------------------==//435436EmitBlockID(BLOCK_META, "Meta", Stream, Record);437EmitRecordID(RECORD_VERSION, "Version", Stream, Record);438auto Abbrev = std::make_shared<BitCodeAbbrev>();439Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION));440Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));441Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev));442443// ==---------------------------------------------------------------------==//444// The subsequent records and Abbrevs are for the "Diagnostic" block.445// ==---------------------------------------------------------------------==//446447EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);448EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);449EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);450EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);451EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);452EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);453EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record);454455// Emit abbreviation for RECORD_DIAG.456Abbrev = std::make_shared<BitCodeAbbrev>();457Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));458Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.459AddSourceLocationAbbrev(*Abbrev);460Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.461Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.462Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Text size.463Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.464Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));465466// Emit abbreviation for RECORD_CATEGORY.467Abbrev = std::make_shared<BitCodeAbbrev>();468Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));469Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.470Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.471Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.472Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));473474// Emit abbreviation for RECORD_SOURCE_RANGE.475Abbrev = std::make_shared<BitCodeAbbrev>();476Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));477AddRangeLocationAbbrev(*Abbrev);478Abbrevs.set(RECORD_SOURCE_RANGE,479Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));480481// Emit the abbreviation for RECORD_DIAG_FLAG.482Abbrev = std::make_shared<BitCodeAbbrev>();483Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));484Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.485Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.486Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.487Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,488Abbrev));489490// Emit the abbreviation for RECORD_FILENAME.491Abbrev = std::make_shared<BitCodeAbbrev>();492Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));493Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID.494Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size.495Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modification time.496Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.497Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.498Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,499Abbrev));500501// Emit the abbreviation for RECORD_FIXIT.502Abbrev = std::make_shared<BitCodeAbbrev>();503Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT));504AddRangeLocationAbbrev(*Abbrev);505Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.506Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text.507Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,508Abbrev));509510Stream.ExitBlock();511}512513void SDiagsWriter::EmitMetaBlock() {514llvm::BitstreamWriter &Stream = State->Stream;515AbbreviationMap &Abbrevs = State->Abbrevs;516517Stream.EnterSubblock(BLOCK_META, 3);518RecordData::value_type Record[] = {RECORD_VERSION, VersionNumber};519Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record);520Stream.ExitBlock();521}522523unsigned SDiagsWriter::getEmitCategory(unsigned int category) {524if (!State->Categories.insert(category).second)525return category;526527// We use a local version of 'Record' so that we can be generating528// another record when we lazily generate one for the category entry.529StringRef catName = DiagnosticIDs::getCategoryNameFromID(category);530RecordData::value_type Record[] = {RECORD_CATEGORY, category, catName.size()};531State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record,532catName);533534return category;535}536537unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,538unsigned DiagID) {539if (DiagLevel == DiagnosticsEngine::Note)540return 0; // No flag for notes.541542StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID);543return getEmitDiagnosticFlag(FlagName);544}545546unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) {547if (FlagName.empty())548return 0;549550// Here we assume that FlagName points to static data whose pointer551// value is fixed. This allows us to unique by diagnostic groups.552const void *data = FlagName.data();553std::pair<unsigned, StringRef> &entry = State->DiagFlags[data];554if (entry.first == 0) {555entry.first = State->DiagFlags.size();556entry.second = FlagName;557558// Lazily emit the string in a separate record.559RecordData::value_type Record[] = {RECORD_DIAG_FLAG, entry.first,560FlagName.size()};561State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG),562Record, FlagName);563}564565return entry.first;566}567568void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,569const Diagnostic &Info) {570assert(!IsFinishing &&571"Received a diagnostic after we've already started teardown.");572if (IsFinishing) {573SmallString<256> diagnostic;574Info.FormatDiagnostic(diagnostic);575getMetaDiags()->Report(576diag::warn_fe_serialized_diag_failure_during_finalization)577<< diagnostic;578return;579}580581// Enter the block for a non-note diagnostic immediately, rather than waiting582// for beginDiagnostic, in case associated notes are emitted before we get583// there.584if (DiagLevel != DiagnosticsEngine::Note) {585if (State->EmittedAnyDiagBlocks)586ExitDiagBlock();587588EnterDiagBlock();589State->EmittedAnyDiagBlocks = true;590}591592// Compute the diagnostic text.593State->diagBuf.clear();594Info.FormatDiagnostic(State->diagBuf);595596if (Info.getLocation().isInvalid()) {597// Special-case diagnostics with no location. We may not have entered a598// source file in this case, so we can't use the normal DiagnosticsRenderer599// machinery.600601// Make sure we bracket all notes as "sub-diagnostics". This matches602// the behavior in SDiagsRenderer::emitDiagnostic().603if (DiagLevel == DiagnosticsEngine::Note)604EnterDiagBlock();605606EmitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagLevel,607State->diagBuf, &Info);608609if (DiagLevel == DiagnosticsEngine::Note)610ExitDiagBlock();611612return;613}614615assert(Info.hasSourceManager() && LangOpts &&616"Unexpected diagnostic with valid location outside of a source file");617SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts);618Renderer.emitDiagnostic(619FullSourceLoc(Info.getLocation(), Info.getSourceManager()), DiagLevel,620State->diagBuf, Info.getRanges(), Info.getFixItHints(), &Info);621}622623static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) {624switch (Level) {625#define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X;626CASE(Ignored)627CASE(Note)628CASE(Remark)629CASE(Warning)630CASE(Error)631CASE(Fatal)632#undef CASE633}634635llvm_unreachable("invalid diagnostic level");636}637638void SDiagsWriter::EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,639DiagnosticsEngine::Level Level,640StringRef Message,641DiagOrStoredDiag D) {642llvm::BitstreamWriter &Stream = State->Stream;643RecordData &Record = State->Record;644AbbreviationMap &Abbrevs = State->Abbrevs;645646// Emit the RECORD_DIAG record.647Record.clear();648Record.push_back(RECORD_DIAG);649Record.push_back(getStableLevel(Level));650AddLocToRecord(Loc, PLoc, Record);651652if (const Diagnostic *Info = D.dyn_cast<const Diagnostic*>()) {653// Emit the category string lazily and get the category ID.654unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID());655Record.push_back(getEmitCategory(DiagID));656// Emit the diagnostic flag string lazily and get the mapped ID.657Record.push_back(getEmitDiagnosticFlag(Level, Info->getID()));658} else {659Record.push_back(getEmitCategory());660Record.push_back(getEmitDiagnosticFlag(Level));661}662663Record.push_back(Message.size());664Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message);665}666667void SDiagsRenderer::emitDiagnosticMessage(668FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,669StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,670DiagOrStoredDiag D) {671Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, D);672}673674void SDiagsWriter::EnterDiagBlock() {675State->Stream.EnterSubblock(BLOCK_DIAG, 4);676}677678void SDiagsWriter::ExitDiagBlock() {679State->Stream.ExitBlock();680}681682void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D,683DiagnosticsEngine::Level Level) {684if (Level == DiagnosticsEngine::Note)685Writer.EnterDiagBlock();686}687688void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D,689DiagnosticsEngine::Level Level) {690// Only end note diagnostics here, because we can't be sure when we've seen691// the last note associated with a non-note diagnostic.692if (Level == DiagnosticsEngine::Note)693Writer.ExitDiagBlock();694}695696void SDiagsWriter::EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges,697ArrayRef<FixItHint> Hints,698const SourceManager &SM) {699llvm::BitstreamWriter &Stream = State->Stream;700RecordData &Record = State->Record;701AbbreviationMap &Abbrevs = State->Abbrevs;702703// Emit Source Ranges.704for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end();705I != E; ++I)706if (I->isValid())707EmitCharSourceRange(*I, SM);708709// Emit FixIts.710for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();711I != E; ++I) {712const FixItHint &Fix = *I;713if (Fix.isNull())714continue;715Record.clear();716Record.push_back(RECORD_FIXIT);717AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM);718Record.push_back(Fix.CodeToInsert.size());719Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record,720Fix.CodeToInsert);721}722}723724void SDiagsRenderer::emitCodeContext(FullSourceLoc Loc,725DiagnosticsEngine::Level Level,726SmallVectorImpl<CharSourceRange> &Ranges,727ArrayRef<FixItHint> Hints) {728Writer.EmitCodeContext(Ranges, Hints, Loc.getManager());729}730731void SDiagsRenderer::emitNote(FullSourceLoc Loc, StringRef Message) {732Writer.EnterDiagBlock();733PresumedLoc PLoc = Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc();734Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, Message,735DiagOrStoredDiag());736Writer.ExitDiagBlock();737}738739DiagnosticsEngine *SDiagsWriter::getMetaDiags() {740// FIXME: It's slightly absurd to create a new diagnostics engine here, but741// the other options that are available today are worse:742//743// 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a744// part of. The DiagnosticsEngine would need to know not to send745// diagnostics back to the consumer that failed. This would require us to746// rework ChainedDiagnosticsConsumer and teach the engine about multiple747// consumers, which is difficult today because most APIs interface with748// consumers rather than the engine itself.749//750// 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need751// to be distinct from the engine the writer was being added to and would752// normally not be used.753if (!State->MetaDiagnostics) {754IntrusiveRefCntPtr<DiagnosticIDs> IDs(new DiagnosticIDs());755auto Client =756new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get());757State->MetaDiagnostics = std::make_unique<DiagnosticsEngine>(758IDs, State->DiagOpts.get(), Client);759}760return State->MetaDiagnostics.get();761}762763void SDiagsWriter::RemoveOldDiagnostics() {764if (!llvm::sys::fs::remove(State->OutputFile))765return;766767getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure);768// Disable merging child records, as whatever is in this file may be769// misleading.770MergeChildRecords = false;771}772773void SDiagsWriter::finish() {774assert(!IsFinishing);775IsFinishing = true;776777// The original instance is responsible for writing the file.778if (!OriginalInstance)779return;780781// Finish off any diagnostic we were in the process of emitting.782if (State->EmittedAnyDiagBlocks)783ExitDiagBlock();784785if (MergeChildRecords) {786if (!State->EmittedAnyDiagBlocks)787// We have no diagnostics of our own, so we can just leave the child788// process' output alone789return;790791if (llvm::sys::fs::exists(State->OutputFile))792if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str()))793getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure);794}795796std::error_code EC;797auto OS = std::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(),798EC, llvm::sys::fs::OF_None);799if (EC) {800getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure)801<< State->OutputFile << EC.message();802OS->clear_error();803return;804}805806// Write the generated bitstream to "Out".807OS->write((char *)&State->Buffer.front(), State->Buffer.size());808OS->flush();809810assert(!OS->has_error());811if (OS->has_error()) {812getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure)813<< State->OutputFile << OS->error().message();814OS->clear_error();815}816}817818std::error_code SDiagsMerger::visitStartOfDiagnostic() {819Writer.EnterDiagBlock();820return std::error_code();821}822823std::error_code SDiagsMerger::visitEndOfDiagnostic() {824Writer.ExitDiagBlock();825return std::error_code();826}827828std::error_code829SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start,830const serialized_diags::Location &End) {831RecordData::value_type Record[] = {832RECORD_SOURCE_RANGE, FileLookup[Start.FileID], Start.Line, Start.Col,833Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset};834Writer.State->Stream.EmitRecordWithAbbrev(835Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record);836return std::error_code();837}838839std::error_code SDiagsMerger::visitDiagnosticRecord(840unsigned Severity, const serialized_diags::Location &Location,841unsigned Category, unsigned Flag, StringRef Message) {842RecordData::value_type Record[] = {843RECORD_DIAG, Severity, FileLookup[Location.FileID], Location.Line,844Location.Col, Location.Offset, CategoryLookup[Category],845Flag ? DiagFlagLookup[Flag] : 0, Message.size()};846847Writer.State->Stream.EmitRecordWithBlob(848Writer.State->Abbrevs.get(RECORD_DIAG), Record, Message);849return std::error_code();850}851852std::error_code853SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start,854const serialized_diags::Location &End,855StringRef Text) {856RecordData::value_type Record[] = {RECORD_FIXIT, FileLookup[Start.FileID],857Start.Line, Start.Col, Start.Offset,858FileLookup[End.FileID], End.Line, End.Col,859End.Offset, Text.size()};860861Writer.State->Stream.EmitRecordWithBlob(862Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text);863return std::error_code();864}865866std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size,867unsigned Timestamp,868StringRef Name) {869FileLookup[ID] = Writer.getEmitFile(Name.str().c_str());870return std::error_code();871}872873std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) {874CategoryLookup[ID] = Writer.getEmitCategory(ID);875return std::error_code();876}877878std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) {879DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name);880return std::error_code();881}882883884