2018-03-27 05:45:04 +08:00
|
|
|
//===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===//
|
2014-10-14 14:30:31 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Frontend/SerializedDiagnosticReader.h"
|
|
|
|
#include "clang/Basic/FileManager.h"
|
2018-03-27 05:45:04 +08:00
|
|
|
#include "clang/Basic/FileSystemOptions.h"
|
2015-01-14 19:29:14 +08:00
|
|
|
#include "clang/Frontend/SerializedDiagnostics.h"
|
2018-03-27 05:45:04 +08:00
|
|
|
#include "llvm/ADT/Optional.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/Bitcode/BitCodes.h"
|
|
|
|
#include "llvm/Bitcode/BitstreamReader.h"
|
|
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/ErrorOr.h"
|
2014-10-14 14:30:31 +08:00
|
|
|
#include "llvm/Support/ManagedStatic.h"
|
2018-03-27 05:45:04 +08:00
|
|
|
#include <cstdint>
|
|
|
|
#include <system_error>
|
2014-10-14 14:30:31 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2018-03-27 05:45:04 +08:00
|
|
|
using namespace serialized_diags;
|
2014-10-14 14:30:31 +08:00
|
|
|
|
|
|
|
std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
|
|
|
|
// Open the diagnostics file.
|
|
|
|
FileSystemOptions FO;
|
|
|
|
FileManager FileMgr(FO);
|
|
|
|
|
2014-10-27 06:44:13 +08:00
|
|
|
auto Buffer = FileMgr.getBufferForFile(File);
|
2014-10-14 14:30:31 +08:00
|
|
|
if (!Buffer)
|
|
|
|
return SDError::CouldNotLoad;
|
|
|
|
|
2016-11-08 12:17:11 +08:00
|
|
|
llvm::BitstreamCursor Stream(**Buffer);
|
|
|
|
Optional<llvm::BitstreamBlockInfo> BlockInfo;
|
2017-07-07 18:25:12 +08:00
|
|
|
|
|
|
|
if (Stream.AtEndOfStream())
|
|
|
|
return SDError::InvalidSignature;
|
2014-10-14 14:30:31 +08:00
|
|
|
|
|
|
|
// Sniff for the signature.
|
|
|
|
if (Stream.Read(8) != 'D' ||
|
|
|
|
Stream.Read(8) != 'I' ||
|
|
|
|
Stream.Read(8) != 'A' ||
|
|
|
|
Stream.Read(8) != 'G')
|
|
|
|
return SDError::InvalidSignature;
|
|
|
|
|
|
|
|
// Read the top level blocks.
|
|
|
|
while (!Stream.AtEndOfStream()) {
|
|
|
|
if (Stream.ReadCode() != llvm::bitc::ENTER_SUBBLOCK)
|
|
|
|
return SDError::InvalidDiagnostics;
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
switch (Stream.ReadSubBlockID()) {
|
2018-03-27 05:45:04 +08:00
|
|
|
case llvm::bitc::BLOCKINFO_BLOCK_ID:
|
2016-11-08 12:17:11 +08:00
|
|
|
BlockInfo = Stream.ReadBlockInfoBlock();
|
|
|
|
if (!BlockInfo)
|
2014-10-14 14:30:31 +08:00
|
|
|
return SDError::MalformedBlockInfoBlock;
|
2016-11-08 12:17:11 +08:00
|
|
|
Stream.setBlockInfo(&*BlockInfo);
|
2014-10-14 14:30:31 +08:00
|
|
|
continue;
|
|
|
|
case BLOCK_META:
|
|
|
|
if ((EC = readMetaBlock(Stream)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case BLOCK_DIAG:
|
|
|
|
if ((EC = readDiagnosticBlock(Stream)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
if (!Stream.SkipBlock())
|
|
|
|
return SDError::MalformedTopLevelBlock;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2018-03-27 05:45:04 +08:00
|
|
|
return {};
|
2014-10-14 14:30:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
enum class SerializedDiagnosticReader::Cursor {
|
|
|
|
Record = 1,
|
|
|
|
BlockEnd,
|
|
|
|
BlockBegin
|
|
|
|
};
|
|
|
|
|
|
|
|
llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
|
|
|
|
SerializedDiagnosticReader::skipUntilRecordOrBlock(
|
|
|
|
llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
|
|
|
|
BlockOrRecordID = 0;
|
|
|
|
|
|
|
|
while (!Stream.AtEndOfStream()) {
|
|
|
|
unsigned Code = Stream.ReadCode();
|
|
|
|
|
|
|
|
switch ((llvm::bitc::FixedAbbrevIDs)Code) {
|
|
|
|
case llvm::bitc::ENTER_SUBBLOCK:
|
|
|
|
BlockOrRecordID = Stream.ReadSubBlockID();
|
|
|
|
return Cursor::BlockBegin;
|
|
|
|
|
|
|
|
case llvm::bitc::END_BLOCK:
|
|
|
|
if (Stream.ReadBlockEnd())
|
|
|
|
return SDError::InvalidDiagnostics;
|
|
|
|
return Cursor::BlockEnd;
|
|
|
|
|
|
|
|
case llvm::bitc::DEFINE_ABBREV:
|
|
|
|
Stream.ReadAbbrevRecord();
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case llvm::bitc::UNABBREV_RECORD:
|
|
|
|
return SDError::UnsupportedConstruct;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// We found a record.
|
|
|
|
BlockOrRecordID = Code;
|
|
|
|
return Cursor::Record;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SDError::InvalidDiagnostics;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code
|
|
|
|
SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
|
|
|
|
if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META))
|
|
|
|
return SDError::MalformedMetadataBlock;
|
|
|
|
|
|
|
|
bool VersionChecked = false;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
unsigned BlockOrCode = 0;
|
|
|
|
llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
|
|
|
|
if (!Res)
|
|
|
|
Res.getError();
|
|
|
|
|
|
|
|
switch (Res.get()) {
|
|
|
|
case Cursor::Record:
|
|
|
|
break;
|
|
|
|
case Cursor::BlockBegin:
|
|
|
|
if (Stream.SkipBlock())
|
|
|
|
return SDError::MalformedMetadataBlock;
|
2017-06-03 14:27:16 +08:00
|
|
|
LLVM_FALLTHROUGH;
|
2014-10-14 14:30:31 +08:00
|
|
|
case Cursor::BlockEnd:
|
|
|
|
if (!VersionChecked)
|
|
|
|
return SDError::MissingVersion;
|
2018-03-27 05:45:04 +08:00
|
|
|
return {};
|
2014-10-14 14:30:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SmallVector<uint64_t, 1> Record;
|
|
|
|
unsigned RecordID = Stream.readRecord(BlockOrCode, Record);
|
|
|
|
|
|
|
|
if (RecordID == RECORD_VERSION) {
|
|
|
|
if (Record.size() < 1)
|
|
|
|
return SDError::MissingVersion;
|
|
|
|
if (Record[0] > VersionNumber)
|
|
|
|
return SDError::VersionMismatch;
|
|
|
|
VersionChecked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code
|
|
|
|
SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
|
|
|
|
if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG))
|
|
|
|
return SDError::MalformedDiagnosticBlock;
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
if ((EC = visitStartOfDiagnostic()))
|
|
|
|
return EC;
|
|
|
|
|
|
|
|
SmallVector<uint64_t, 16> Record;
|
|
|
|
while (true) {
|
|
|
|
unsigned BlockOrCode = 0;
|
|
|
|
llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
|
|
|
|
if (!Res)
|
|
|
|
Res.getError();
|
|
|
|
|
|
|
|
switch (Res.get()) {
|
|
|
|
case Cursor::BlockBegin:
|
|
|
|
// The only blocks we care about are subdiagnostics.
|
|
|
|
if (BlockOrCode == serialized_diags::BLOCK_DIAG) {
|
|
|
|
if ((EC = readDiagnosticBlock(Stream)))
|
|
|
|
return EC;
|
|
|
|
} else if (!Stream.SkipBlock())
|
|
|
|
return SDError::MalformedSubBlock;
|
|
|
|
continue;
|
|
|
|
case Cursor::BlockEnd:
|
|
|
|
if ((EC = visitEndOfDiagnostic()))
|
|
|
|
return EC;
|
2018-03-27 05:45:04 +08:00
|
|
|
return {};
|
2014-10-14 14:30:31 +08:00
|
|
|
case Cursor::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 ((RecordIDs)RecID) {
|
|
|
|
case RECORD_CATEGORY:
|
|
|
|
// A category has ID and name size.
|
|
|
|
if (Record.size() != 2)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitCategoryRecord(Record[0], Blob)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_DIAG:
|
|
|
|
// A diagnostic has severity, location (4), category, flag, and message
|
|
|
|
// size.
|
|
|
|
if (Record.size() != 8)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitDiagnosticRecord(
|
|
|
|
Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
|
|
|
|
Record[5], Record[6], Blob)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_DIAG_FLAG:
|
|
|
|
// A diagnostic flag has ID and name size.
|
|
|
|
if (Record.size() != 2)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitDiagFlagRecord(Record[0], Blob)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_FILENAME:
|
|
|
|
// A filename has ID, size, timestamp, and name size. The size and
|
|
|
|
// timestamp are legacy fields that are always zero these days.
|
|
|
|
if (Record.size() != 4)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_FIXIT:
|
|
|
|
// A fixit has two locations (4 each) and message size.
|
|
|
|
if (Record.size() != 9)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitFixitRecord(
|
|
|
|
Location(Record[0], Record[1], Record[2], Record[3]),
|
|
|
|
Location(Record[4], Record[5], Record[6], Record[7]), Blob)))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_SOURCE_RANGE:
|
|
|
|
// A source range is two locations (4 each).
|
|
|
|
if (Record.size() != 8)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitSourceRangeRecord(
|
|
|
|
Location(Record[0], Record[1], Record[2], Record[3]),
|
|
|
|
Location(Record[4], Record[5], Record[6], Record[7]))))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
case RECORD_VERSION:
|
|
|
|
// A version is just a number.
|
|
|
|
if (Record.size() != 1)
|
|
|
|
return SDError::MalformedDiagnosticRecord;
|
|
|
|
if ((EC = visitVersionRecord(Record[0])))
|
|
|
|
return EC;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2018-03-27 05:45:04 +08:00
|
|
|
|
2014-10-14 14:30:31 +08:00
|
|
|
class SDErrorCategoryType final : public std::error_category {
|
2016-10-20 07:39:55 +08:00
|
|
|
const char *name() const noexcept override {
|
2014-10-14 14:30:31 +08:00
|
|
|
return "clang.serialized_diags";
|
|
|
|
}
|
2018-03-27 05:45:04 +08:00
|
|
|
|
2014-10-14 14:30:31 +08:00
|
|
|
std::string message(int IE) const override {
|
2018-03-27 05:45:04 +08:00
|
|
|
auto E = static_cast<SDError>(IE);
|
2014-10-14 14:30:31 +08:00
|
|
|
switch (E) {
|
|
|
|
case SDError::CouldNotLoad:
|
|
|
|
return "Failed to open diagnostics file";
|
|
|
|
case SDError::InvalidSignature:
|
|
|
|
return "Invalid diagnostics signature";
|
|
|
|
case SDError::InvalidDiagnostics:
|
|
|
|
return "Parse error reading diagnostics";
|
|
|
|
case SDError::MalformedTopLevelBlock:
|
|
|
|
return "Malformed block at top-level of diagnostics";
|
|
|
|
case SDError::MalformedSubBlock:
|
|
|
|
return "Malformed sub-block in a diagnostic";
|
|
|
|
case SDError::MalformedBlockInfoBlock:
|
|
|
|
return "Malformed BlockInfo block";
|
|
|
|
case SDError::MalformedMetadataBlock:
|
|
|
|
return "Malformed Metadata block";
|
|
|
|
case SDError::MalformedDiagnosticBlock:
|
|
|
|
return "Malformed Diagnostic block";
|
|
|
|
case SDError::MalformedDiagnosticRecord:
|
|
|
|
return "Malformed Diagnostic record";
|
|
|
|
case SDError::MissingVersion:
|
|
|
|
return "No version provided in diagnostics";
|
|
|
|
case SDError::VersionMismatch:
|
|
|
|
return "Unsupported diagnostics version";
|
|
|
|
case SDError::UnsupportedConstruct:
|
|
|
|
return "Bitcode constructs that are not supported in diagnostics appear";
|
|
|
|
case SDError::HandlerFailed:
|
|
|
|
return "Generic error occurred while handling a record";
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown error type!");
|
|
|
|
}
|
|
|
|
};
|
2018-03-27 05:45:04 +08:00
|
|
|
|
|
|
|
} // namespace
|
2014-10-14 14:30:31 +08:00
|
|
|
|
|
|
|
static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
|
|
|
|
const std::error_category &clang::serialized_diags::SDErrorCategory() {
|
|
|
|
return *ErrorCategory;
|
|
|
|
}
|