10b57cec5SDimitry Andric //===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "clang/Frontend/SerializedDiagnosticReader.h"
100b57cec5SDimitry Andric #include "clang/Basic/FileManager.h"
110b57cec5SDimitry Andric #include "clang/Basic/FileSystemOptions.h"
120b57cec5SDimitry Andric #include "clang/Frontend/SerializedDiagnostics.h"
130b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
140b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
150b57cec5SDimitry Andric #include "llvm/Bitstream/BitCodes.h"
160b57cec5SDimitry Andric #include "llvm/Bitstream/BitstreamReader.h"
170b57cec5SDimitry Andric #include "llvm/Support/Compiler.h"
180b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
190b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
200b57cec5SDimitry Andric #include "llvm/Support/ManagedStatic.h"
210b57cec5SDimitry Andric #include <cstdint>
22*bdd1243dSDimitry Andric #include <optional>
230b57cec5SDimitry Andric #include <system_error>
240b57cec5SDimitry Andric
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace serialized_diags;
270b57cec5SDimitry Andric
readDiagnostics(StringRef File)280b57cec5SDimitry Andric std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
290b57cec5SDimitry Andric // Open the diagnostics file.
300b57cec5SDimitry Andric FileSystemOptions FO;
310b57cec5SDimitry Andric FileManager FileMgr(FO);
320b57cec5SDimitry Andric
330b57cec5SDimitry Andric auto Buffer = FileMgr.getBufferForFile(File);
340b57cec5SDimitry Andric if (!Buffer)
350b57cec5SDimitry Andric return SDError::CouldNotLoad;
360b57cec5SDimitry Andric
370b57cec5SDimitry Andric llvm::BitstreamCursor Stream(**Buffer);
38*bdd1243dSDimitry Andric std::optional<llvm::BitstreamBlockInfo> BlockInfo;
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric if (Stream.AtEndOfStream())
410b57cec5SDimitry Andric return SDError::InvalidSignature;
420b57cec5SDimitry Andric
430b57cec5SDimitry Andric // Sniff for the signature.
440b57cec5SDimitry Andric for (unsigned char C : {'D', 'I', 'A', 'G'}) {
450b57cec5SDimitry Andric if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) {
460b57cec5SDimitry Andric if (Res.get() == C)
470b57cec5SDimitry Andric continue;
480b57cec5SDimitry Andric } else {
490b57cec5SDimitry Andric // FIXME this drops the error on the floor.
500b57cec5SDimitry Andric consumeError(Res.takeError());
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric return SDError::InvalidSignature;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric // Read the top level blocks.
560b57cec5SDimitry Andric while (!Stream.AtEndOfStream()) {
570b57cec5SDimitry Andric if (Expected<unsigned> Res = Stream.ReadCode()) {
580b57cec5SDimitry Andric if (Res.get() != llvm::bitc::ENTER_SUBBLOCK)
590b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
600b57cec5SDimitry Andric } else {
610b57cec5SDimitry Andric // FIXME this drops the error on the floor.
620b57cec5SDimitry Andric consumeError(Res.takeError());
630b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric std::error_code EC;
670b57cec5SDimitry Andric Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID();
680b57cec5SDimitry Andric if (!MaybeSubBlockID) {
690b57cec5SDimitry Andric // FIXME this drops the error on the floor.
700b57cec5SDimitry Andric consumeError(MaybeSubBlockID.takeError());
710b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
720b57cec5SDimitry Andric }
730b57cec5SDimitry Andric
740b57cec5SDimitry Andric switch (MaybeSubBlockID.get()) {
750b57cec5SDimitry Andric case llvm::bitc::BLOCKINFO_BLOCK_ID: {
76*bdd1243dSDimitry Andric Expected<std::optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =
770b57cec5SDimitry Andric Stream.ReadBlockInfoBlock();
780b57cec5SDimitry Andric if (!MaybeBlockInfo) {
790b57cec5SDimitry Andric // FIXME this drops the error on the floor.
800b57cec5SDimitry Andric consumeError(MaybeBlockInfo.takeError());
810b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric BlockInfo = std::move(MaybeBlockInfo.get());
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric if (!BlockInfo)
860b57cec5SDimitry Andric return SDError::MalformedBlockInfoBlock;
870b57cec5SDimitry Andric Stream.setBlockInfo(&*BlockInfo);
880b57cec5SDimitry Andric continue;
890b57cec5SDimitry Andric case BLOCK_META:
900b57cec5SDimitry Andric if ((EC = readMetaBlock(Stream)))
910b57cec5SDimitry Andric return EC;
920b57cec5SDimitry Andric continue;
930b57cec5SDimitry Andric case BLOCK_DIAG:
940b57cec5SDimitry Andric if ((EC = readDiagnosticBlock(Stream)))
950b57cec5SDimitry Andric return EC;
960b57cec5SDimitry Andric continue;
970b57cec5SDimitry Andric default:
980b57cec5SDimitry Andric if (llvm::Error Err = Stream.SkipBlock()) {
990b57cec5SDimitry Andric // FIXME this drops the error on the floor.
1000b57cec5SDimitry Andric consumeError(std::move(Err));
1010b57cec5SDimitry Andric return SDError::MalformedTopLevelBlock;
1020b57cec5SDimitry Andric }
1030b57cec5SDimitry Andric continue;
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric }
1060b57cec5SDimitry Andric return {};
1070b57cec5SDimitry Andric }
1080b57cec5SDimitry Andric
1090b57cec5SDimitry Andric enum class SerializedDiagnosticReader::Cursor {
1100b57cec5SDimitry Andric Record = 1,
1110b57cec5SDimitry Andric BlockEnd,
1120b57cec5SDimitry Andric BlockBegin
1130b57cec5SDimitry Andric };
1140b57cec5SDimitry Andric
1150b57cec5SDimitry Andric llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
skipUntilRecordOrBlock(llvm::BitstreamCursor & Stream,unsigned & BlockOrRecordID)1160b57cec5SDimitry Andric SerializedDiagnosticReader::skipUntilRecordOrBlock(
1170b57cec5SDimitry Andric llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
1180b57cec5SDimitry Andric BlockOrRecordID = 0;
1190b57cec5SDimitry Andric
1200b57cec5SDimitry Andric while (!Stream.AtEndOfStream()) {
1210b57cec5SDimitry Andric unsigned Code;
1220b57cec5SDimitry Andric if (Expected<unsigned> Res = Stream.ReadCode())
1230b57cec5SDimitry Andric Code = Res.get();
1240b57cec5SDimitry Andric else
1250b57cec5SDimitry Andric return llvm::errorToErrorCode(Res.takeError());
1260b57cec5SDimitry Andric
1270b57cec5SDimitry Andric if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) {
1280b57cec5SDimitry Andric // We found a record.
1290b57cec5SDimitry Andric BlockOrRecordID = Code;
1300b57cec5SDimitry Andric return Cursor::Record;
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) {
1330b57cec5SDimitry Andric case llvm::bitc::ENTER_SUBBLOCK:
1340b57cec5SDimitry Andric if (Expected<unsigned> Res = Stream.ReadSubBlockID())
1350b57cec5SDimitry Andric BlockOrRecordID = Res.get();
1360b57cec5SDimitry Andric else
1370b57cec5SDimitry Andric return llvm::errorToErrorCode(Res.takeError());
1380b57cec5SDimitry Andric return Cursor::BlockBegin;
1390b57cec5SDimitry Andric
1400b57cec5SDimitry Andric case llvm::bitc::END_BLOCK:
1410b57cec5SDimitry Andric if (Stream.ReadBlockEnd())
1420b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
1430b57cec5SDimitry Andric return Cursor::BlockEnd;
1440b57cec5SDimitry Andric
1450b57cec5SDimitry Andric case llvm::bitc::DEFINE_ABBREV:
1460b57cec5SDimitry Andric if (llvm::Error Err = Stream.ReadAbbrevRecord())
1470b57cec5SDimitry Andric return llvm::errorToErrorCode(std::move(Err));
1480b57cec5SDimitry Andric continue;
1490b57cec5SDimitry Andric
1500b57cec5SDimitry Andric case llvm::bitc::UNABBREV_RECORD:
1510b57cec5SDimitry Andric return SDError::UnsupportedConstruct;
1520b57cec5SDimitry Andric
1530b57cec5SDimitry Andric case llvm::bitc::FIRST_APPLICATION_ABBREV:
1540b57cec5SDimitry Andric llvm_unreachable("Unexpected abbrev id.");
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric }
1570b57cec5SDimitry Andric
1580b57cec5SDimitry Andric return SDError::InvalidDiagnostics;
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric std::error_code
readMetaBlock(llvm::BitstreamCursor & Stream)1620b57cec5SDimitry Andric SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
1630b57cec5SDimitry Andric if (llvm::Error Err =
1640b57cec5SDimitry Andric Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {
1650b57cec5SDimitry Andric // FIXME this drops the error on the floor.
1660b57cec5SDimitry Andric consumeError(std::move(Err));
1670b57cec5SDimitry Andric return SDError::MalformedMetadataBlock;
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric
1700b57cec5SDimitry Andric bool VersionChecked = false;
1710b57cec5SDimitry Andric
1720b57cec5SDimitry Andric while (true) {
1730b57cec5SDimitry Andric unsigned BlockOrCode = 0;
1740b57cec5SDimitry Andric llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
1750b57cec5SDimitry Andric if (!Res)
1760b57cec5SDimitry Andric Res.getError();
1770b57cec5SDimitry Andric
1780b57cec5SDimitry Andric switch (Res.get()) {
1790b57cec5SDimitry Andric case Cursor::Record:
1800b57cec5SDimitry Andric break;
1810b57cec5SDimitry Andric case Cursor::BlockBegin:
1820b57cec5SDimitry Andric if (llvm::Error Err = Stream.SkipBlock()) {
1830b57cec5SDimitry Andric // FIXME this drops the error on the floor.
1840b57cec5SDimitry Andric consumeError(std::move(Err));
1850b57cec5SDimitry Andric return SDError::MalformedMetadataBlock;
1860b57cec5SDimitry Andric }
187*bdd1243dSDimitry Andric [[fallthrough]];
1880b57cec5SDimitry Andric case Cursor::BlockEnd:
1890b57cec5SDimitry Andric if (!VersionChecked)
1900b57cec5SDimitry Andric return SDError::MissingVersion;
1910b57cec5SDimitry Andric return {};
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric
1940b57cec5SDimitry Andric SmallVector<uint64_t, 1> Record;
1950b57cec5SDimitry Andric Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record);
1960b57cec5SDimitry Andric if (!MaybeRecordID)
1970b57cec5SDimitry Andric return errorToErrorCode(MaybeRecordID.takeError());
1980b57cec5SDimitry Andric unsigned RecordID = MaybeRecordID.get();
1990b57cec5SDimitry Andric
2000b57cec5SDimitry Andric if (RecordID == RECORD_VERSION) {
2010b57cec5SDimitry Andric if (Record.size() < 1)
2020b57cec5SDimitry Andric return SDError::MissingVersion;
2030b57cec5SDimitry Andric if (Record[0] > VersionNumber)
2040b57cec5SDimitry Andric return SDError::VersionMismatch;
2050b57cec5SDimitry Andric VersionChecked = true;
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric }
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric
2100b57cec5SDimitry Andric std::error_code
readDiagnosticBlock(llvm::BitstreamCursor & Stream)2110b57cec5SDimitry Andric SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
2120b57cec5SDimitry Andric if (llvm::Error Err =
2130b57cec5SDimitry Andric Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
2140b57cec5SDimitry Andric // FIXME this drops the error on the floor.
2150b57cec5SDimitry Andric consumeError(std::move(Err));
2160b57cec5SDimitry Andric return SDError::MalformedDiagnosticBlock;
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric
2190b57cec5SDimitry Andric std::error_code EC;
2200b57cec5SDimitry Andric if ((EC = visitStartOfDiagnostic()))
2210b57cec5SDimitry Andric return EC;
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric SmallVector<uint64_t, 16> Record;
2240b57cec5SDimitry Andric while (true) {
2250b57cec5SDimitry Andric unsigned BlockOrCode = 0;
2260b57cec5SDimitry Andric llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
2270b57cec5SDimitry Andric if (!Res)
2280b57cec5SDimitry Andric Res.getError();
2290b57cec5SDimitry Andric
2300b57cec5SDimitry Andric switch (Res.get()) {
2310b57cec5SDimitry Andric case Cursor::BlockBegin:
2320b57cec5SDimitry Andric // The only blocks we care about are subdiagnostics.
2330b57cec5SDimitry Andric if (BlockOrCode == serialized_diags::BLOCK_DIAG) {
2340b57cec5SDimitry Andric if ((EC = readDiagnosticBlock(Stream)))
2350b57cec5SDimitry Andric return EC;
2360b57cec5SDimitry Andric } else if (llvm::Error Err = Stream.SkipBlock()) {
2370b57cec5SDimitry Andric // FIXME this drops the error on the floor.
2380b57cec5SDimitry Andric consumeError(std::move(Err));
2390b57cec5SDimitry Andric return SDError::MalformedSubBlock;
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric continue;
2420b57cec5SDimitry Andric case Cursor::BlockEnd:
2430b57cec5SDimitry Andric if ((EC = visitEndOfDiagnostic()))
2440b57cec5SDimitry Andric return EC;
2450b57cec5SDimitry Andric return {};
2460b57cec5SDimitry Andric case Cursor::Record:
2470b57cec5SDimitry Andric break;
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric
2500b57cec5SDimitry Andric // Read the record.
2510b57cec5SDimitry Andric Record.clear();
2520b57cec5SDimitry Andric StringRef Blob;
2530b57cec5SDimitry Andric Expected<unsigned> MaybeRecID =
2540b57cec5SDimitry Andric Stream.readRecord(BlockOrCode, Record, &Blob);
2550b57cec5SDimitry Andric if (!MaybeRecID)
2560b57cec5SDimitry Andric return errorToErrorCode(MaybeRecID.takeError());
2570b57cec5SDimitry Andric unsigned RecID = MaybeRecID.get();
2580b57cec5SDimitry Andric
2590b57cec5SDimitry Andric if (RecID < serialized_diags::RECORD_FIRST ||
2600b57cec5SDimitry Andric RecID > serialized_diags::RECORD_LAST)
2610b57cec5SDimitry Andric continue;
2620b57cec5SDimitry Andric
2630b57cec5SDimitry Andric switch ((RecordIDs)RecID) {
2640b57cec5SDimitry Andric case RECORD_CATEGORY:
2650b57cec5SDimitry Andric // A category has ID and name size.
2660b57cec5SDimitry Andric if (Record.size() != 2)
2670b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
2680b57cec5SDimitry Andric if ((EC = visitCategoryRecord(Record[0], Blob)))
2690b57cec5SDimitry Andric return EC;
2700b57cec5SDimitry Andric continue;
2710b57cec5SDimitry Andric case RECORD_DIAG:
2720b57cec5SDimitry Andric // A diagnostic has severity, location (4), category, flag, and message
2730b57cec5SDimitry Andric // size.
2740b57cec5SDimitry Andric if (Record.size() != 8)
2750b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
2760b57cec5SDimitry Andric if ((EC = visitDiagnosticRecord(
2770b57cec5SDimitry Andric Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
2780b57cec5SDimitry Andric Record[5], Record[6], Blob)))
2790b57cec5SDimitry Andric return EC;
2800b57cec5SDimitry Andric continue;
2810b57cec5SDimitry Andric case RECORD_DIAG_FLAG:
2820b57cec5SDimitry Andric // A diagnostic flag has ID and name size.
2830b57cec5SDimitry Andric if (Record.size() != 2)
2840b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
2850b57cec5SDimitry Andric if ((EC = visitDiagFlagRecord(Record[0], Blob)))
2860b57cec5SDimitry Andric return EC;
2870b57cec5SDimitry Andric continue;
2880b57cec5SDimitry Andric case RECORD_FILENAME:
2890b57cec5SDimitry Andric // A filename has ID, size, timestamp, and name size. The size and
2900b57cec5SDimitry Andric // timestamp are legacy fields that are always zero these days.
2910b57cec5SDimitry Andric if (Record.size() != 4)
2920b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
2930b57cec5SDimitry Andric if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))
2940b57cec5SDimitry Andric return EC;
2950b57cec5SDimitry Andric continue;
2960b57cec5SDimitry Andric case RECORD_FIXIT:
2970b57cec5SDimitry Andric // A fixit has two locations (4 each) and message size.
2980b57cec5SDimitry Andric if (Record.size() != 9)
2990b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
3000b57cec5SDimitry Andric if ((EC = visitFixitRecord(
3010b57cec5SDimitry Andric Location(Record[0], Record[1], Record[2], Record[3]),
3020b57cec5SDimitry Andric Location(Record[4], Record[5], Record[6], Record[7]), Blob)))
3030b57cec5SDimitry Andric return EC;
3040b57cec5SDimitry Andric continue;
3050b57cec5SDimitry Andric case RECORD_SOURCE_RANGE:
3060b57cec5SDimitry Andric // A source range is two locations (4 each).
3070b57cec5SDimitry Andric if (Record.size() != 8)
3080b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
3090b57cec5SDimitry Andric if ((EC = visitSourceRangeRecord(
3100b57cec5SDimitry Andric Location(Record[0], Record[1], Record[2], Record[3]),
3110b57cec5SDimitry Andric Location(Record[4], Record[5], Record[6], Record[7]))))
3120b57cec5SDimitry Andric return EC;
3130b57cec5SDimitry Andric continue;
3140b57cec5SDimitry Andric case RECORD_VERSION:
3150b57cec5SDimitry Andric // A version is just a number.
3160b57cec5SDimitry Andric if (Record.size() != 1)
3170b57cec5SDimitry Andric return SDError::MalformedDiagnosticRecord;
3180b57cec5SDimitry Andric if ((EC = visitVersionRecord(Record[0])))
3190b57cec5SDimitry Andric return EC;
3200b57cec5SDimitry Andric continue;
3210b57cec5SDimitry Andric }
3220b57cec5SDimitry Andric }
3230b57cec5SDimitry Andric }
3240b57cec5SDimitry Andric
3250b57cec5SDimitry Andric namespace {
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric class SDErrorCategoryType final : public std::error_category {
name() const3280b57cec5SDimitry Andric const char *name() const noexcept override {
3290b57cec5SDimitry Andric return "clang.serialized_diags";
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric
message(int IE) const3320b57cec5SDimitry Andric std::string message(int IE) const override {
3330b57cec5SDimitry Andric auto E = static_cast<SDError>(IE);
3340b57cec5SDimitry Andric switch (E) {
3350b57cec5SDimitry Andric case SDError::CouldNotLoad:
3360b57cec5SDimitry Andric return "Failed to open diagnostics file";
3370b57cec5SDimitry Andric case SDError::InvalidSignature:
3380b57cec5SDimitry Andric return "Invalid diagnostics signature";
3390b57cec5SDimitry Andric case SDError::InvalidDiagnostics:
3400b57cec5SDimitry Andric return "Parse error reading diagnostics";
3410b57cec5SDimitry Andric case SDError::MalformedTopLevelBlock:
3420b57cec5SDimitry Andric return "Malformed block at top-level of diagnostics";
3430b57cec5SDimitry Andric case SDError::MalformedSubBlock:
3440b57cec5SDimitry Andric return "Malformed sub-block in a diagnostic";
3450b57cec5SDimitry Andric case SDError::MalformedBlockInfoBlock:
3460b57cec5SDimitry Andric return "Malformed BlockInfo block";
3470b57cec5SDimitry Andric case SDError::MalformedMetadataBlock:
3480b57cec5SDimitry Andric return "Malformed Metadata block";
3490b57cec5SDimitry Andric case SDError::MalformedDiagnosticBlock:
3500b57cec5SDimitry Andric return "Malformed Diagnostic block";
3510b57cec5SDimitry Andric case SDError::MalformedDiagnosticRecord:
3520b57cec5SDimitry Andric return "Malformed Diagnostic record";
3530b57cec5SDimitry Andric case SDError::MissingVersion:
3540b57cec5SDimitry Andric return "No version provided in diagnostics";
3550b57cec5SDimitry Andric case SDError::VersionMismatch:
3560b57cec5SDimitry Andric return "Unsupported diagnostics version";
3570b57cec5SDimitry Andric case SDError::UnsupportedConstruct:
3580b57cec5SDimitry Andric return "Bitcode constructs that are not supported in diagnostics appear";
3590b57cec5SDimitry Andric case SDError::HandlerFailed:
3600b57cec5SDimitry Andric return "Generic error occurred while handling a record";
3610b57cec5SDimitry Andric }
3620b57cec5SDimitry Andric llvm_unreachable("Unknown error type!");
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric };
3650b57cec5SDimitry Andric
3660b57cec5SDimitry Andric } // namespace
3670b57cec5SDimitry Andric
3680b57cec5SDimitry Andric static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
SDErrorCategory()3690b57cec5SDimitry Andric const std::error_category &clang::serialized_diags::SDErrorCategory() {
3700b57cec5SDimitry Andric return *ErrorCategory;
3710b57cec5SDimitry Andric }
372