370 lines · cpp
1//===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Frontend/SerializedDiagnosticReader.h"10#include "clang/Basic/FileManager.h"11#include "clang/Basic/FileSystemOptions.h"12#include "clang/Frontend/SerializedDiagnostics.h"13#include "llvm/ADT/SmallVector.h"14#include "llvm/Bitstream/BitstreamReader.h"15#include "llvm/Support/Compiler.h"16#include "llvm/Support/ErrorHandling.h"17#include "llvm/Support/ErrorOr.h"18#include "llvm/Support/ManagedStatic.h"19#include <cstdint>20#include <optional>21#include <system_error>22 23using namespace clang;24using namespace serialized_diags;25 26std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {27 // Open the diagnostics file.28 FileSystemOptions FO;29 FileManager FileMgr(FO);30 31 auto Buffer = FileMgr.getBufferForFile(File);32 if (!Buffer)33 return SDError::CouldNotLoad;34 35 llvm::BitstreamCursor Stream(**Buffer);36 std::optional<llvm::BitstreamBlockInfo> BlockInfo;37 38 if (Stream.AtEndOfStream())39 return SDError::InvalidSignature;40 41 // Sniff for the signature.42 for (unsigned char C : {'D', 'I', 'A', 'G'}) {43 if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) {44 if (Res.get() == C)45 continue;46 } else {47 // FIXME this drops the error on the floor.48 consumeError(Res.takeError());49 }50 return SDError::InvalidSignature;51 }52 53 // Read the top level blocks.54 while (!Stream.AtEndOfStream()) {55 if (Expected<unsigned> Res = Stream.ReadCode()) {56 if (Res.get() != llvm::bitc::ENTER_SUBBLOCK)57 return SDError::InvalidDiagnostics;58 } else {59 // FIXME this drops the error on the floor.60 consumeError(Res.takeError());61 return SDError::InvalidDiagnostics;62 }63 64 std::error_code EC;65 Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID();66 if (!MaybeSubBlockID) {67 // FIXME this drops the error on the floor.68 consumeError(MaybeSubBlockID.takeError());69 return SDError::InvalidDiagnostics;70 }71 72 switch (MaybeSubBlockID.get()) {73 case llvm::bitc::BLOCKINFO_BLOCK_ID: {74 Expected<std::optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =75 Stream.ReadBlockInfoBlock();76 if (!MaybeBlockInfo) {77 // FIXME this drops the error on the floor.78 consumeError(MaybeBlockInfo.takeError());79 return SDError::InvalidDiagnostics;80 }81 BlockInfo = std::move(MaybeBlockInfo.get());82 }83 if (!BlockInfo)84 return SDError::MalformedBlockInfoBlock;85 Stream.setBlockInfo(&*BlockInfo);86 continue;87 case BLOCK_META:88 if ((EC = readMetaBlock(Stream)))89 return EC;90 continue;91 case BLOCK_DIAG:92 if ((EC = readDiagnosticBlock(Stream)))93 return EC;94 continue;95 default:96 if (llvm::Error Err = Stream.SkipBlock()) {97 // FIXME this drops the error on the floor.98 consumeError(std::move(Err));99 return SDError::MalformedTopLevelBlock;100 }101 continue;102 }103 }104 return {};105}106 107enum class SerializedDiagnosticReader::Cursor {108 Record = 1,109 BlockEnd,110 BlockBegin111};112 113llvm::ErrorOr<SerializedDiagnosticReader::Cursor>114SerializedDiagnosticReader::skipUntilRecordOrBlock(115 llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {116 BlockOrRecordID = 0;117 118 while (!Stream.AtEndOfStream()) {119 unsigned Code;120 if (Expected<unsigned> Res = Stream.ReadCode())121 Code = Res.get();122 else123 return llvm::errorToErrorCode(Res.takeError());124 125 if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) {126 // We found a record.127 BlockOrRecordID = Code;128 return Cursor::Record;129 }130 switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) {131 case llvm::bitc::ENTER_SUBBLOCK:132 if (Expected<unsigned> Res = Stream.ReadSubBlockID())133 BlockOrRecordID = Res.get();134 else135 return llvm::errorToErrorCode(Res.takeError());136 return Cursor::BlockBegin;137 138 case llvm::bitc::END_BLOCK:139 if (Stream.ReadBlockEnd())140 return SDError::InvalidDiagnostics;141 return Cursor::BlockEnd;142 143 case llvm::bitc::DEFINE_ABBREV:144 if (llvm::Error Err = Stream.ReadAbbrevRecord())145 return llvm::errorToErrorCode(std::move(Err));146 continue;147 148 case llvm::bitc::UNABBREV_RECORD:149 return SDError::UnsupportedConstruct;150 151 case llvm::bitc::FIRST_APPLICATION_ABBREV:152 llvm_unreachable("Unexpected abbrev id.");153 }154 }155 156 return SDError::InvalidDiagnostics;157}158 159std::error_code160SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {161 if (llvm::Error Err =162 Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {163 // FIXME this drops the error on the floor.164 consumeError(std::move(Err));165 return SDError::MalformedMetadataBlock;166 }167 168 bool VersionChecked = false;169 170 while (true) {171 unsigned BlockOrCode = 0;172 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);173 if (!Res)174 Res.getError();175 176 switch (Res.get()) {177 case Cursor::Record:178 break;179 case Cursor::BlockBegin:180 if (llvm::Error Err = Stream.SkipBlock()) {181 // FIXME this drops the error on the floor.182 consumeError(std::move(Err));183 return SDError::MalformedMetadataBlock;184 }185 [[fallthrough]];186 case Cursor::BlockEnd:187 if (!VersionChecked)188 return SDError::MissingVersion;189 return {};190 }191 192 SmallVector<uint64_t, 1> Record;193 Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record);194 if (!MaybeRecordID)195 return errorToErrorCode(MaybeRecordID.takeError());196 unsigned RecordID = MaybeRecordID.get();197 198 if (RecordID == RECORD_VERSION) {199 if (Record.size() < 1)200 return SDError::MissingVersion;201 if (Record[0] > VersionNumber)202 return SDError::VersionMismatch;203 VersionChecked = true;204 }205 }206}207 208std::error_code209SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {210 if (llvm::Error Err =211 Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {212 // FIXME this drops the error on the floor.213 consumeError(std::move(Err));214 return SDError::MalformedDiagnosticBlock;215 }216 217 std::error_code EC;218 if ((EC = visitStartOfDiagnostic()))219 return EC;220 221 SmallVector<uint64_t, 16> Record;222 while (true) {223 unsigned BlockOrCode = 0;224 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);225 if (!Res)226 Res.getError();227 228 switch (Res.get()) {229 case Cursor::BlockBegin:230 // The only blocks we care about are subdiagnostics.231 if (BlockOrCode == serialized_diags::BLOCK_DIAG) {232 if ((EC = readDiagnosticBlock(Stream)))233 return EC;234 } else if (llvm::Error Err = Stream.SkipBlock()) {235 // FIXME this drops the error on the floor.236 consumeError(std::move(Err));237 return SDError::MalformedSubBlock;238 }239 continue;240 case Cursor::BlockEnd:241 if ((EC = visitEndOfDiagnostic()))242 return EC;243 return {};244 case Cursor::Record:245 break;246 }247 248 // Read the record.249 Record.clear();250 StringRef Blob;251 Expected<unsigned> MaybeRecID =252 Stream.readRecord(BlockOrCode, Record, &Blob);253 if (!MaybeRecID)254 return errorToErrorCode(MaybeRecID.takeError());255 unsigned RecID = MaybeRecID.get();256 257 if (RecID < serialized_diags::RECORD_FIRST ||258 RecID > serialized_diags::RECORD_LAST)259 continue;260 261 switch ((RecordIDs)RecID) {262 case RECORD_CATEGORY:263 // A category has ID and name size.264 if (Record.size() != 2)265 return SDError::MalformedDiagnosticRecord;266 if ((EC = visitCategoryRecord(Record[0], Blob)))267 return EC;268 continue;269 case RECORD_DIAG:270 // A diagnostic has severity, location (4), category, flag, and message271 // size.272 if (Record.size() != 8)273 return SDError::MalformedDiagnosticRecord;274 if ((EC = visitDiagnosticRecord(275 Record[0], Location(Record[1], Record[2], Record[3], Record[4]),276 Record[5], Record[6], Blob)))277 return EC;278 continue;279 case RECORD_DIAG_FLAG:280 // A diagnostic flag has ID and name size.281 if (Record.size() != 2)282 return SDError::MalformedDiagnosticRecord;283 if ((EC = visitDiagFlagRecord(Record[0], Blob)))284 return EC;285 continue;286 case RECORD_FILENAME:287 // A filename has ID, size, timestamp, and name size. The size and288 // timestamp are legacy fields that are always zero these days.289 if (Record.size() != 4)290 return SDError::MalformedDiagnosticRecord;291 if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))292 return EC;293 continue;294 case RECORD_FIXIT:295 // A fixit has two locations (4 each) and message size.296 if (Record.size() != 9)297 return SDError::MalformedDiagnosticRecord;298 if ((EC = visitFixitRecord(299 Location(Record[0], Record[1], Record[2], Record[3]),300 Location(Record[4], Record[5], Record[6], Record[7]), Blob)))301 return EC;302 continue;303 case RECORD_SOURCE_RANGE:304 // A source range is two locations (4 each).305 if (Record.size() != 8)306 return SDError::MalformedDiagnosticRecord;307 if ((EC = visitSourceRangeRecord(308 Location(Record[0], Record[1], Record[2], Record[3]),309 Location(Record[4], Record[5], Record[6], Record[7]))))310 return EC;311 continue;312 case RECORD_VERSION:313 // A version is just a number.314 if (Record.size() != 1)315 return SDError::MalformedDiagnosticRecord;316 if ((EC = visitVersionRecord(Record[0])))317 return EC;318 continue;319 }320 }321}322 323namespace {324 325class SDErrorCategoryType final : public std::error_category {326 const char *name() const noexcept override {327 return "clang.serialized_diags";328 }329 330 std::string message(int IE) const override {331 auto E = static_cast<SDError>(IE);332 switch (E) {333 case SDError::CouldNotLoad:334 return "Failed to open diagnostics file";335 case SDError::InvalidSignature:336 return "Invalid diagnostics signature";337 case SDError::InvalidDiagnostics:338 return "Parse error reading diagnostics";339 case SDError::MalformedTopLevelBlock:340 return "Malformed block at top-level of diagnostics";341 case SDError::MalformedSubBlock:342 return "Malformed sub-block in a diagnostic";343 case SDError::MalformedBlockInfoBlock:344 return "Malformed BlockInfo block";345 case SDError::MalformedMetadataBlock:346 return "Malformed Metadata block";347 case SDError::MalformedDiagnosticBlock:348 return "Malformed Diagnostic block";349 case SDError::MalformedDiagnosticRecord:350 return "Malformed Diagnostic record";351 case SDError::MissingVersion:352 return "No version provided in diagnostics";353 case SDError::VersionMismatch:354 return "Unsupported diagnostics version";355 case SDError::UnsupportedConstruct:356 return "Bitcode constructs that are not supported in diagnostics appear";357 case SDError::HandlerFailed:358 return "Generic error occurred while handling a record";359 }360 llvm_unreachable("Unknown error type!");361 }362};363 364} // namespace365 366static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;367const std::error_category &clang::serialized_diags::SDErrorCategory() {368 return *ErrorCategory;369}370