llvm-capstone/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

858 lines
31 KiB
C++

//===--- 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/Support/raw_ostream.h"
#include <utility>
using namespace clang;
using namespace clang::serialized_diags;
namespace {
class AbbreviationMap {
llvm::DenseMap<unsigned, unsigned> Abbrevs;
public:
AbbreviationMap() {}
void set(unsigned recordID, unsigned abbrevID) {
assert(Abbrevs.find(recordID) == Abbrevs.end()
&& "Abbreviation already set.");
Abbrevs[recordID] = abbrevID;
}
unsigned get(unsigned recordID) {
assert(Abbrevs.find(recordID) != Abbrevs.end() &&
"Abbreviation not set.");
return Abbrevs[recordID];
}
};
typedef SmallVector<uint64_t, 64> RecordData;
typedef SmallVectorImpl<uint64_t> RecordDataImpl;
typedef ArrayRef<uint64_t> 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<CharSourceRange> Ranges,
DiagOrStoredDiag D) override;
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
ArrayRef<CharSourceRange> Ranges) override {}
void emitNote(FullSourceLoc Loc, StringRef Message) override;
void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange> &Ranges,
ArrayRef<FixItHint> Hints) override;
void beginDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level) override;
void endDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level) override;
};
typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup;
class SDiagsMerger : SerializedDiagnosticReader {
SDiagsWriter &Writer;
AbbrevLookup FileLookup;
AbbrevLookup CategoryLookup;
AbbrevLookup DiagFlagLookup;
public:
SDiagsMerger(SDiagsWriter &Writer)
: SerializedDiagnosticReader(), 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<SharedState> 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<SharedState>(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<CharSourceRange> &Ranges,
ArrayRef<FixItHint> 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;
/// 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<DiagnosticOptions> 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<unsigned> Categories;
/// The collection of files used.
llvm::DenseMap<const char *, unsigned> Files;
typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> >
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<DiagnosticsEngine> MetaDiagnostics;
};
/// State shared among the various clones of this diagnostic consumer.
std::shared_ptr<SharedState> State;
};
} // end anonymous namespace
namespace clang {
namespace serialized_diags {
std::unique_ptr<DiagnosticConsumer>
create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) {
return llvm::make_unique<SDiagsWriter>(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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<BitCodeAbbrev>();
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<unsigned, StringRef> &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) {
// 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<const Diagnostic*>()) {
// 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<clang::CharSourceRange> 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<CharSourceRange> &Ranges,
ArrayRef<FixItHint> Hints,
const SourceManager &SM) {
llvm::BitstreamWriter &Stream = State->Stream;
RecordData &Record = State->Record;
AbbreviationMap &Abbrevs = State->Abbrevs;
// Emit Source Ranges.
for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
I != E; ++I)
if (I->isValid())
EmitCharSourceRange(*I, SM);
// Emit FixIts.
for (ArrayRef<FixItHint>::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<CharSourceRange> &Ranges,
ArrayRef<FixItHint> 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<DiagnosticIDs> IDs(new DiagnosticIDs());
auto Client =
new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get());
State->MetaDiagnostics = llvm::make_unique<DiagnosticsEngine>(
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() {
// 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 = llvm::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(),
EC, llvm::sys::fs::F_None);
if (EC) {
getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure)
<< State->OutputFile << EC.message();
return;
}
// Write the generated bitstream to "Out".
OS->write((char *)&State->Buffer.front(), State->Buffer.size());
OS->flush();
}
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();
}