forked from OSchip/llvm-project
664 lines
20 KiB
C++
664 lines
20 KiB
C++
//===-- CXLoadedDiagnostic.cpp - Handling of persisent diags ----*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Implements handling of persisent diagnostics.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CXLoadedDiagnostic.h"
|
|
#include "CXString.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Frontend/SerializedDiagnosticPrinter.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Bitcode/BitstreamReader.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
using namespace clang;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Extend CXDiagnosticSetImpl which contains strings for diagnostics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
typedef llvm::DenseMap<unsigned, const char *> Strings;
|
|
|
|
namespace {
|
|
class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl {
|
|
public:
|
|
CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {}
|
|
virtual ~CXLoadedDiagnosticSetImpl() {}
|
|
|
|
llvm::BumpPtrAllocator Alloc;
|
|
Strings Categories;
|
|
Strings WarningFlags;
|
|
Strings FileNames;
|
|
|
|
FileSystemOptions FO;
|
|
FileManager FakeFiles;
|
|
llvm::DenseMap<unsigned, const FileEntry *> Files;
|
|
|
|
/// \brief Copy the string into our own allocator.
|
|
const char *copyString(StringRef Blob) {
|
|
char *mem = Alloc.Allocate<char>(Blob.size() + 1);
|
|
memcpy(mem, Blob.data(), Blob.size());
|
|
mem[Blob.size()] = '\0';
|
|
return mem;
|
|
}
|
|
};
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Cleanup.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
CXLoadedDiagnostic::~CXLoadedDiagnostic() {}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Public CXLoadedDiagnostic methods.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const {
|
|
// FIXME: Fail more softly if the diagnostic level is unknown?
|
|
auto severityAsLevel = static_cast<serialized_diags::Level>(severity);
|
|
assert(severity == static_cast<unsigned>(severityAsLevel) &&
|
|
"unknown serialized diagnostic level");
|
|
|
|
switch (severityAsLevel) {
|
|
#define CASE(X) case serialized_diags::X: return CXDiagnostic_##X;
|
|
CASE(Ignored)
|
|
CASE(Note)
|
|
CASE(Warning)
|
|
CASE(Error)
|
|
CASE(Fatal)
|
|
CASE(Remark)
|
|
#undef CASE
|
|
}
|
|
|
|
llvm_unreachable("Invalid diagnostic level");
|
|
}
|
|
|
|
static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) {
|
|
// The lowest bit of ptr_data[0] is always set to 1 to indicate this
|
|
// is a persistent diagnostic.
|
|
uintptr_t V = (uintptr_t) DLoc;
|
|
V |= 0x1;
|
|
CXSourceLocation Loc = { { (void*) V, 0 }, 0 };
|
|
return Loc;
|
|
}
|
|
|
|
CXSourceLocation CXLoadedDiagnostic::getLocation() const {
|
|
// The lowest bit of ptr_data[0] is always set to 1 to indicate this
|
|
// is a persistent diagnostic.
|
|
return makeLocation(&DiagLoc);
|
|
}
|
|
|
|
CXString CXLoadedDiagnostic::getSpelling() const {
|
|
return cxstring::createRef(Spelling);
|
|
}
|
|
|
|
CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const {
|
|
if (DiagOption.empty())
|
|
return cxstring::createEmpty();
|
|
|
|
// FIXME: possibly refactor with logic in CXStoredDiagnostic.
|
|
if (Disable)
|
|
*Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str());
|
|
return cxstring::createDup((Twine("-W") + DiagOption).str());
|
|
}
|
|
|
|
unsigned CXLoadedDiagnostic::getCategory() const {
|
|
return category;
|
|
}
|
|
|
|
CXString CXLoadedDiagnostic::getCategoryText() const {
|
|
return cxstring::createDup(CategoryText);
|
|
}
|
|
|
|
unsigned CXLoadedDiagnostic::getNumRanges() const {
|
|
return Ranges.size();
|
|
}
|
|
|
|
CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const {
|
|
assert(Range < Ranges.size());
|
|
return Ranges[Range];
|
|
}
|
|
|
|
unsigned CXLoadedDiagnostic::getNumFixIts() const {
|
|
return FixIts.size();
|
|
}
|
|
|
|
CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt,
|
|
CXSourceRange *ReplacementRange) const {
|
|
assert(FixIt < FixIts.size());
|
|
if (ReplacementRange)
|
|
*ReplacementRange = FixIts[FixIt].first;
|
|
return cxstring::createRef(FixIts[FixIt].second);
|
|
}
|
|
|
|
void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location,
|
|
CXFile *file,
|
|
unsigned int *line,
|
|
unsigned int *column,
|
|
unsigned int *offset) {
|
|
|
|
|
|
// CXSourceLocation consists of the following fields:
|
|
//
|
|
// void *ptr_data[2];
|
|
// unsigned int_data;
|
|
//
|
|
// The lowest bit of ptr_data[0] is always set to 1 to indicate this
|
|
// is a persistent diagnostic.
|
|
//
|
|
// For now, do the unoptimized approach and store the data in a side
|
|
// data structure. We can optimize this case later.
|
|
|
|
uintptr_t V = (uintptr_t) location.ptr_data[0];
|
|
assert((V & 0x1) == 1);
|
|
V &= ~(uintptr_t)1;
|
|
|
|
const Location &Loc = *((Location*)V);
|
|
|
|
if (file)
|
|
*file = Loc.file;
|
|
if (line)
|
|
*line = Loc.line;
|
|
if (column)
|
|
*column = Loc.column;
|
|
if (offset)
|
|
*offset = Loc.offset;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Deserialize diagnostics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
enum { MaxSupportedVersion = 2 };
|
|
typedef SmallVector<uint64_t, 64> RecordData;
|
|
enum LoadResult { Failure = 1, Success = 0 };
|
|
enum StreamResult { Read_EndOfStream,
|
|
Read_BlockBegin,
|
|
Read_Failure,
|
|
Read_Record,
|
|
Read_BlockEnd };
|
|
|
|
namespace {
|
|
class DiagLoader {
|
|
enum CXLoadDiag_Error *error;
|
|
CXString *errorString;
|
|
|
|
void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) {
|
|
if (error)
|
|
*error = code;
|
|
if (errorString)
|
|
*errorString = cxstring::createDup(err);
|
|
}
|
|
|
|
void reportInvalidFile(llvm::StringRef err) {
|
|
return reportBad(CXLoadDiag_InvalidFile, err);
|
|
}
|
|
|
|
LoadResult readMetaBlock(llvm::BitstreamCursor &Stream);
|
|
|
|
LoadResult readDiagnosticBlock(llvm::BitstreamCursor &Stream,
|
|
CXDiagnosticSetImpl &Diags,
|
|
CXLoadedDiagnosticSetImpl &TopDiags);
|
|
|
|
StreamResult readToNextRecordOrBlock(llvm::BitstreamCursor &Stream,
|
|
llvm::StringRef errorContext,
|
|
unsigned &BlockOrRecordID,
|
|
bool atTopLevel = false);
|
|
|
|
|
|
LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
Strings &strings, llvm::StringRef errorContext,
|
|
RecordData &Record,
|
|
StringRef Blob,
|
|
bool allowEmptyString = false);
|
|
|
|
LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
const char *&RetStr,
|
|
llvm::StringRef errorContext,
|
|
RecordData &Record,
|
|
StringRef Blob,
|
|
bool allowEmptyString = false);
|
|
|
|
LoadResult readRange(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
RecordData &Record, unsigned RecStartIdx,
|
|
CXSourceRange &SR);
|
|
|
|
LoadResult readLocation(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
RecordData &Record, unsigned &offset,
|
|
CXLoadedDiagnostic::Location &Loc);
|
|
|
|
public:
|
|
DiagLoader(enum CXLoadDiag_Error *e, CXString *es)
|
|
: error(e), errorString(es) {
|
|
if (error)
|
|
*error = CXLoadDiag_None;
|
|
if (errorString)
|
|
*errorString = cxstring::createEmpty();
|
|
}
|
|
|
|
CXDiagnosticSet load(const char *file);
|
|
};
|
|
}
|
|
|
|
CXDiagnosticSet DiagLoader::load(const char *file) {
|
|
// Open the diagnostics file.
|
|
std::string ErrStr;
|
|
FileSystemOptions FO;
|
|
FileManager FileMgr(FO);
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buffer;
|
|
Buffer.reset(FileMgr.getBufferForFile(file));
|
|
|
|
if (!Buffer) {
|
|
reportBad(CXLoadDiag_CannotLoad, ErrStr);
|
|
return 0;
|
|
}
|
|
|
|
llvm::BitstreamReader StreamFile;
|
|
StreamFile.init((const unsigned char *)Buffer->getBufferStart(),
|
|
(const unsigned char *)Buffer->getBufferEnd());
|
|
|
|
llvm::BitstreamCursor Stream;
|
|
Stream.init(StreamFile);
|
|
|
|
// Sniff for the signature.
|
|
if (Stream.Read(8) != 'D' ||
|
|
Stream.Read(8) != 'I' ||
|
|
Stream.Read(8) != 'A' ||
|
|
Stream.Read(8) != 'G') {
|
|
reportBad(CXLoadDiag_InvalidFile,
|
|
"Bad header in diagnostics file");
|
|
return 0;
|
|
}
|
|
|
|
std::unique_ptr<CXLoadedDiagnosticSetImpl> Diags(
|
|
new CXLoadedDiagnosticSetImpl());
|
|
|
|
while (true) {
|
|
unsigned BlockID = 0;
|
|
StreamResult Res = readToNextRecordOrBlock(Stream, "Top-level",
|
|
BlockID, true);
|
|
switch (Res) {
|
|
case Read_EndOfStream:
|
|
return (CXDiagnosticSet)Diags.release();
|
|
case Read_Failure:
|
|
return 0;
|
|
case Read_Record:
|
|
llvm_unreachable("Top-level does not have records");
|
|
case Read_BlockEnd:
|
|
continue;
|
|
case Read_BlockBegin:
|
|
break;
|
|
}
|
|
|
|
switch (BlockID) {
|
|
case serialized_diags::BLOCK_META:
|
|
if (readMetaBlock(Stream))
|
|
return 0;
|
|
break;
|
|
case serialized_diags::BLOCK_DIAG:
|
|
if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get()))
|
|
return 0;
|
|
break;
|
|
default:
|
|
if (!Stream.SkipBlock()) {
|
|
reportInvalidFile("Malformed block at top-level of diagnostics file");
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream,
|
|
llvm::StringRef errorContext,
|
|
unsigned &blockOrRecordID,
|
|
bool atTopLevel) {
|
|
|
|
blockOrRecordID = 0;
|
|
|
|
while (!Stream.AtEndOfStream()) {
|
|
unsigned Code = Stream.ReadCode();
|
|
|
|
// Handle the top-level specially.
|
|
if (atTopLevel) {
|
|
if (Code == llvm::bitc::ENTER_SUBBLOCK) {
|
|
unsigned BlockID = Stream.ReadSubBlockID();
|
|
if (BlockID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
|
|
if (Stream.ReadBlockInfoBlock()) {
|
|
reportInvalidFile("Malformed BlockInfoBlock in diagnostics file");
|
|
return Read_Failure;
|
|
}
|
|
continue;
|
|
}
|
|
blockOrRecordID = BlockID;
|
|
return Read_BlockBegin;
|
|
}
|
|
reportInvalidFile("Only blocks can appear at the top of a "
|
|
"diagnostic file");
|
|
return Read_Failure;
|
|
}
|
|
|
|
switch ((llvm::bitc::FixedAbbrevIDs)Code) {
|
|
case llvm::bitc::ENTER_SUBBLOCK:
|
|
blockOrRecordID = Stream.ReadSubBlockID();
|
|
return Read_BlockBegin;
|
|
|
|
case llvm::bitc::END_BLOCK:
|
|
if (Stream.ReadBlockEnd()) {
|
|
reportInvalidFile("Cannot read end of block");
|
|
return Read_Failure;
|
|
}
|
|
return Read_BlockEnd;
|
|
|
|
case llvm::bitc::DEFINE_ABBREV:
|
|
Stream.ReadAbbrevRecord();
|
|
continue;
|
|
|
|
case llvm::bitc::UNABBREV_RECORD:
|
|
reportInvalidFile("Diagnostics file should have no unabbreviated "
|
|
"records");
|
|
return Read_Failure;
|
|
|
|
default:
|
|
// We found a record.
|
|
blockOrRecordID = Code;
|
|
return Read_Record;
|
|
}
|
|
}
|
|
|
|
if (atTopLevel)
|
|
return Read_EndOfStream;
|
|
|
|
reportInvalidFile(Twine("Premature end of diagnostics file within ").str() +
|
|
errorContext.str());
|
|
return Read_Failure;
|
|
}
|
|
|
|
LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) {
|
|
if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {
|
|
reportInvalidFile("Malformed metadata block");
|
|
return Failure;
|
|
}
|
|
|
|
bool versionChecked = false;
|
|
|
|
while (true) {
|
|
unsigned blockOrCode = 0;
|
|
StreamResult Res = readToNextRecordOrBlock(Stream, "Metadata Block",
|
|
blockOrCode);
|
|
|
|
switch(Res) {
|
|
case Read_EndOfStream:
|
|
llvm_unreachable("EndOfStream handled by readToNextRecordOrBlock");
|
|
case Read_Failure:
|
|
return Failure;
|
|
case Read_Record:
|
|
break;
|
|
case Read_BlockBegin:
|
|
if (Stream.SkipBlock()) {
|
|
reportInvalidFile("Malformed metadata block");
|
|
return Failure;
|
|
}
|
|
case Read_BlockEnd:
|
|
if (!versionChecked) {
|
|
reportInvalidFile("Diagnostics file does not contain version"
|
|
" information");
|
|
return Failure;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
RecordData Record;
|
|
unsigned recordID = Stream.readRecord(blockOrCode, Record);
|
|
|
|
if (recordID == serialized_diags::RECORD_VERSION) {
|
|
if (Record.size() < 1) {
|
|
reportInvalidFile("malformed VERSION identifier in diagnostics file");
|
|
return Failure;
|
|
}
|
|
if (Record[0] > MaxSupportedVersion) {
|
|
reportInvalidFile("diagnostics file is a newer version than the one "
|
|
"supported");
|
|
return Failure;
|
|
}
|
|
versionChecked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
const char *&RetStr,
|
|
llvm::StringRef errorContext,
|
|
RecordData &Record,
|
|
StringRef Blob,
|
|
bool allowEmptyString) {
|
|
|
|
// Basic buffer overflow check.
|
|
if (Blob.size() > 65536) {
|
|
reportInvalidFile(std::string("Out-of-bounds string in ") +
|
|
std::string(errorContext));
|
|
return Failure;
|
|
}
|
|
|
|
if (allowEmptyString && Record.size() >= 1 && Blob.size() == 0) {
|
|
RetStr = "";
|
|
return Success;
|
|
}
|
|
|
|
if (Record.size() < 1 || Blob.size() == 0) {
|
|
reportInvalidFile(std::string("Corrupted ") + std::string(errorContext)
|
|
+ std::string(" entry"));
|
|
return Failure;
|
|
}
|
|
|
|
RetStr = TopDiags.copyString(Blob);
|
|
return Success;
|
|
}
|
|
|
|
LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
Strings &strings,
|
|
llvm::StringRef errorContext,
|
|
RecordData &Record,
|
|
StringRef Blob,
|
|
bool allowEmptyString) {
|
|
const char *RetStr;
|
|
if (readString(TopDiags, RetStr, errorContext, Record, Blob,
|
|
allowEmptyString))
|
|
return Failure;
|
|
strings[Record[0]] = RetStr;
|
|
return Success;
|
|
}
|
|
|
|
LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
RecordData &Record, unsigned &offset,
|
|
CXLoadedDiagnostic::Location &Loc) {
|
|
if (Record.size() < offset + 3) {
|
|
reportInvalidFile("Corrupted source location");
|
|
return Failure;
|
|
}
|
|
|
|
unsigned fileID = Record[offset++];
|
|
if (fileID == 0) {
|
|
// Sentinel value.
|
|
Loc.file = 0;
|
|
Loc.line = 0;
|
|
Loc.column = 0;
|
|
Loc.offset = 0;
|
|
return Success;
|
|
}
|
|
|
|
const FileEntry *FE = TopDiags.Files[fileID];
|
|
if (!FE) {
|
|
reportInvalidFile("Corrupted file entry in source location");
|
|
return Failure;
|
|
}
|
|
Loc.file = const_cast<FileEntry *>(FE);
|
|
Loc.line = Record[offset++];
|
|
Loc.column = Record[offset++];
|
|
Loc.offset = Record[offset++];
|
|
return Success;
|
|
}
|
|
|
|
LoadResult DiagLoader::readRange(CXLoadedDiagnosticSetImpl &TopDiags,
|
|
RecordData &Record,
|
|
unsigned int RecStartIdx,
|
|
CXSourceRange &SR) {
|
|
CXLoadedDiagnostic::Location *Start, *End;
|
|
Start = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>();
|
|
End = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>();
|
|
|
|
if (readLocation(TopDiags, Record, RecStartIdx, *Start))
|
|
return Failure;
|
|
if (readLocation(TopDiags, Record, RecStartIdx, *End))
|
|
return Failure;
|
|
|
|
CXSourceLocation startLoc = makeLocation(Start);
|
|
CXSourceLocation endLoc = makeLocation(End);
|
|
SR = clang_getRange(startLoc, endLoc);
|
|
return Success;
|
|
}
|
|
|
|
LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream,
|
|
CXDiagnosticSetImpl &Diags,
|
|
CXLoadedDiagnosticSetImpl &TopDiags){
|
|
|
|
if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
|
|
reportInvalidFile("malformed diagnostic block");
|
|
return Failure;
|
|
}
|
|
|
|
std::unique_ptr<CXLoadedDiagnostic> D(new CXLoadedDiagnostic());
|
|
RecordData Record;
|
|
|
|
while (true) {
|
|
unsigned blockOrCode = 0;
|
|
StreamResult Res = readToNextRecordOrBlock(Stream, "Diagnostic Block",
|
|
blockOrCode);
|
|
switch (Res) {
|
|
case Read_EndOfStream:
|
|
llvm_unreachable("EndOfStream handled in readToNextRecordOrBlock");
|
|
case Read_Failure:
|
|
return Failure;
|
|
case Read_BlockBegin: {
|
|
// The only blocks we care about are subdiagnostics.
|
|
if (blockOrCode != serialized_diags::BLOCK_DIAG) {
|
|
if (!Stream.SkipBlock()) {
|
|
reportInvalidFile("Invalid subblock in Diagnostics block");
|
|
return Failure;
|
|
}
|
|
} else if (readDiagnosticBlock(Stream, D->getChildDiagnostics(),
|
|
TopDiags)) {
|
|
return Failure;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
case Read_BlockEnd:
|
|
Diags.appendDiagnostic(D.release());
|
|
return Success;
|
|
case Read_Record:
|
|
break;
|
|
}
|
|
|
|
// Read the record.
|
|
Record.clear();
|
|
StringRef Blob;
|
|
unsigned recID = Stream.readRecord(blockOrCode, Record, &Blob);
|
|
|
|
if (recID < serialized_diags::RECORD_FIRST ||
|
|
recID > serialized_diags::RECORD_LAST)
|
|
continue;
|
|
|
|
switch ((serialized_diags::RecordIDs)recID) {
|
|
case serialized_diags::RECORD_VERSION:
|
|
continue;
|
|
case serialized_diags::RECORD_CATEGORY:
|
|
if (readString(TopDiags, TopDiags.Categories, "category", Record,
|
|
Blob, /* allowEmptyString */ true))
|
|
return Failure;
|
|
continue;
|
|
|
|
case serialized_diags::RECORD_DIAG_FLAG:
|
|
if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record,
|
|
Blob))
|
|
return Failure;
|
|
continue;
|
|
|
|
case serialized_diags::RECORD_FILENAME: {
|
|
if (readString(TopDiags, TopDiags.FileNames, "filename", Record,
|
|
Blob))
|
|
return Failure;
|
|
|
|
if (Record.size() < 3) {
|
|
reportInvalidFile("Invalid file entry");
|
|
return Failure;
|
|
}
|
|
|
|
const FileEntry *FE =
|
|
TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]],
|
|
/* size */ Record[1],
|
|
/* time */ Record[2]);
|
|
|
|
TopDiags.Files[Record[0]] = FE;
|
|
continue;
|
|
}
|
|
|
|
case serialized_diags::RECORD_SOURCE_RANGE: {
|
|
CXSourceRange SR;
|
|
if (readRange(TopDiags, Record, 0, SR))
|
|
return Failure;
|
|
D->Ranges.push_back(SR);
|
|
continue;
|
|
}
|
|
|
|
case serialized_diags::RECORD_FIXIT: {
|
|
CXSourceRange SR;
|
|
if (readRange(TopDiags, Record, 0, SR))
|
|
return Failure;
|
|
const char *RetStr;
|
|
if (readString(TopDiags, RetStr, "FIXIT", Record, Blob,
|
|
/* allowEmptyString */ true))
|
|
return Failure;
|
|
D->FixIts.push_back(std::make_pair(SR, RetStr));
|
|
continue;
|
|
}
|
|
|
|
case serialized_diags::RECORD_DIAG: {
|
|
D->severity = Record[0];
|
|
unsigned offset = 1;
|
|
if (readLocation(TopDiags, Record, offset, D->DiagLoc))
|
|
return Failure;
|
|
D->category = Record[offset++];
|
|
unsigned diagFlag = Record[offset++];
|
|
D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : "";
|
|
D->CategoryText = D->category ? TopDiags.Categories[D->category] : "";
|
|
D->Spelling = TopDiags.copyString(Blob);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
CXDiagnosticSet clang_loadDiagnostics(const char *file,
|
|
enum CXLoadDiag_Error *error,
|
|
CXString *errorString) {
|
|
DiagLoader L(error, errorString);
|
|
return L.load(file);
|
|
}
|
|
} // end extern 'C'.
|