forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			372 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- SerializedDiagnosticReader.cpp - Reads 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/SerializedDiagnosticReader.h"
 | |
| #include "clang/Basic/FileManager.h"
 | |
| #include "clang/Basic/FileSystemOptions.h"
 | |
| #include "clang/Frontend/SerializedDiagnostics.h"
 | |
| #include "llvm/ADT/Optional.h"
 | |
| #include "llvm/ADT/SmallVector.h"
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| #include "llvm/Bitstream/BitCodes.h"
 | |
| #include "llvm/Bitstream/BitstreamReader.h"
 | |
| #include "llvm/Support/Compiler.h"
 | |
| #include "llvm/Support/ErrorHandling.h"
 | |
| #include "llvm/Support/ErrorOr.h"
 | |
| #include "llvm/Support/ManagedStatic.h"
 | |
| #include <cstdint>
 | |
| #include <system_error>
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace serialized_diags;
 | |
| 
 | |
| std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
 | |
|   // Open the diagnostics file.
 | |
|   FileSystemOptions FO;
 | |
|   FileManager FileMgr(FO);
 | |
| 
 | |
|   auto Buffer = FileMgr.getBufferForFile(File);
 | |
|   if (!Buffer)
 | |
|     return SDError::CouldNotLoad;
 | |
| 
 | |
|   llvm::BitstreamCursor Stream(**Buffer);
 | |
|   Optional<llvm::BitstreamBlockInfo> BlockInfo;
 | |
| 
 | |
|   if (Stream.AtEndOfStream())
 | |
|     return SDError::InvalidSignature;
 | |
| 
 | |
|   // Sniff for the signature.
 | |
|   for (unsigned char C : {'D', 'I', 'A', 'G'}) {
 | |
|     if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) {
 | |
|       if (Res.get() == C)
 | |
|         continue;
 | |
|     } else {
 | |
|       // FIXME this drops the error on the floor.
 | |
|       consumeError(Res.takeError());
 | |
|     }
 | |
|     return SDError::InvalidSignature;
 | |
|   }
 | |
| 
 | |
|   // Read the top level blocks.
 | |
|   while (!Stream.AtEndOfStream()) {
 | |
|     if (Expected<unsigned> Res = Stream.ReadCode()) {
 | |
|       if (Res.get() != llvm::bitc::ENTER_SUBBLOCK)
 | |
|         return SDError::InvalidDiagnostics;
 | |
|     } else {
 | |
|       // FIXME this drops the error on the floor.
 | |
|       consumeError(Res.takeError());
 | |
|       return SDError::InvalidDiagnostics;
 | |
|     }
 | |
| 
 | |
|     std::error_code EC;
 | |
|     Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID();
 | |
|     if (!MaybeSubBlockID) {
 | |
|       // FIXME this drops the error on the floor.
 | |
|       consumeError(MaybeSubBlockID.takeError());
 | |
|       return SDError::InvalidDiagnostics;
 | |
|     }
 | |
| 
 | |
|     switch (MaybeSubBlockID.get()) {
 | |
|     case llvm::bitc::BLOCKINFO_BLOCK_ID: {
 | |
|       Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =
 | |
|           Stream.ReadBlockInfoBlock();
 | |
|       if (!MaybeBlockInfo) {
 | |
|         // FIXME this drops the error on the floor.
 | |
|         consumeError(MaybeBlockInfo.takeError());
 | |
|         return SDError::InvalidDiagnostics;
 | |
|       }
 | |
|       BlockInfo = std::move(MaybeBlockInfo.get());
 | |
|     }
 | |
|       if (!BlockInfo)
 | |
|         return SDError::MalformedBlockInfoBlock;
 | |
|       Stream.setBlockInfo(&*BlockInfo);
 | |
|       continue;
 | |
|     case BLOCK_META:
 | |
|       if ((EC = readMetaBlock(Stream)))
 | |
|         return EC;
 | |
|       continue;
 | |
|     case BLOCK_DIAG:
 | |
|       if ((EC = readDiagnosticBlock(Stream)))
 | |
|         return EC;
 | |
|       continue;
 | |
|     default:
 | |
|       if (llvm::Error Err = Stream.SkipBlock()) {
 | |
|         // FIXME this drops the error on the floor.
 | |
|         consumeError(std::move(Err));
 | |
|         return SDError::MalformedTopLevelBlock;
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
|   }
 | |
|   return {};
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     if (Expected<unsigned> Res = Stream.ReadCode())
 | |
|       Code = Res.get();
 | |
|     else
 | |
|       return llvm::errorToErrorCode(Res.takeError());
 | |
| 
 | |
|     if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) {
 | |
|       // We found a record.
 | |
|       BlockOrRecordID = Code;
 | |
|       return Cursor::Record;
 | |
|     }
 | |
|     switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) {
 | |
|     case llvm::bitc::ENTER_SUBBLOCK:
 | |
|       if (Expected<unsigned> Res = Stream.ReadSubBlockID())
 | |
|         BlockOrRecordID = Res.get();
 | |
|       else
 | |
|         return llvm::errorToErrorCode(Res.takeError());
 | |
|       return Cursor::BlockBegin;
 | |
| 
 | |
|     case llvm::bitc::END_BLOCK:
 | |
|       if (Stream.ReadBlockEnd())
 | |
|         return SDError::InvalidDiagnostics;
 | |
|       return Cursor::BlockEnd;
 | |
| 
 | |
|     case llvm::bitc::DEFINE_ABBREV:
 | |
|       if (llvm::Error Err = Stream.ReadAbbrevRecord())
 | |
|         return llvm::errorToErrorCode(std::move(Err));
 | |
|       continue;
 | |
| 
 | |
|     case llvm::bitc::UNABBREV_RECORD:
 | |
|       return SDError::UnsupportedConstruct;
 | |
| 
 | |
|     case llvm::bitc::FIRST_APPLICATION_ABBREV:
 | |
|       llvm_unreachable("Unexpected abbrev id.");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return SDError::InvalidDiagnostics;
 | |
| }
 | |
| 
 | |
| std::error_code
 | |
| SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
 | |
|   if (llvm::Error Err =
 | |
|           Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {
 | |
|     // FIXME this drops the error on the floor.
 | |
|     consumeError(std::move(Err));
 | |
|     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 (llvm::Error Err = Stream.SkipBlock()) {
 | |
|         // FIXME this drops the error on the floor.
 | |
|         consumeError(std::move(Err));
 | |
|         return SDError::MalformedMetadataBlock;
 | |
|       }
 | |
|       LLVM_FALLTHROUGH;
 | |
|     case Cursor::BlockEnd:
 | |
|       if (!VersionChecked)
 | |
|         return SDError::MissingVersion;
 | |
|       return {};
 | |
|     }
 | |
| 
 | |
|     SmallVector<uint64_t, 1> Record;
 | |
|     Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record);
 | |
|     if (!MaybeRecordID)
 | |
|       return errorToErrorCode(MaybeRecordID.takeError());
 | |
|     unsigned RecordID = MaybeRecordID.get();
 | |
| 
 | |
|     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 (llvm::Error Err =
 | |
|           Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
 | |
|     // FIXME this drops the error on the floor.
 | |
|     consumeError(std::move(Err));
 | |
|     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 (llvm::Error Err = Stream.SkipBlock()) {
 | |
|         // FIXME this drops the error on the floor.
 | |
|         consumeError(std::move(Err));
 | |
|         return SDError::MalformedSubBlock;
 | |
|       }
 | |
|       continue;
 | |
|     case Cursor::BlockEnd:
 | |
|       if ((EC = visitEndOfDiagnostic()))
 | |
|         return EC;
 | |
|       return {};
 | |
|     case Cursor::Record:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // Read the record.
 | |
|     Record.clear();
 | |
|     StringRef Blob;
 | |
|     Expected<unsigned> MaybeRecID =
 | |
|         Stream.readRecord(BlockOrCode, Record, &Blob);
 | |
|     if (!MaybeRecID)
 | |
|       return errorToErrorCode(MaybeRecID.takeError());
 | |
|     unsigned RecID = MaybeRecID.get();
 | |
| 
 | |
|     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 {
 | |
| 
 | |
| class SDErrorCategoryType final : public std::error_category {
 | |
|   const char *name() const noexcept override {
 | |
|     return "clang.serialized_diags";
 | |
|   }
 | |
| 
 | |
|   std::string message(int IE) const override {
 | |
|     auto E = static_cast<SDError>(IE);
 | |
|     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!");
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
 | |
| const std::error_category &clang::serialized_diags::SDErrorCategory() {
 | |
|   return *ErrorCategory;
 | |
| }
 |