From: Justin Bogner Date: Tue, 14 Oct 2014 00:40:55 +0000 (+0000) Subject: Frontend: Extract SerializedDiagnosticReader out of CXLoadedDiagnostic (NFC) X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2835c8acb3745a6ac6cebdfe5800017b61c02486;p=clang Frontend: Extract SerializedDiagnosticReader out of CXLoadedDiagnostic (NFC) We currently read serialized diagnostics directly in the C API, which makes it difficult to reuse this logic elsewhere. This extracts the core of the serialized diagnostic parsing logic into a base class that can be subclassed using a visitor pattern. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@219647 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Frontend/SerializedDiagnosticPrinter.h b/include/clang/Frontend/SerializedDiagnosticPrinter.h index 207f9f1984..f00a68bb89 100644 --- a/include/clang/Frontend/SerializedDiagnosticPrinter.h +++ b/include/clang/Frontend/SerializedDiagnosticPrinter.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_FRONTEND_SERIALIZEDDIAGNOSTICPRINTER_H #include "clang/Basic/LLVM.h" +#include "clang/Frontend/SerializedDiagnostics.h" #include "llvm/Bitcode/BitstreamWriter.h" namespace llvm { @@ -23,41 +24,6 @@ class DiagnosticsEngine; class DiagnosticOptions; namespace serialized_diags { - -enum BlockIDs { - /// \brief A top-level block which represents any meta data associated - /// with the diagostics, including versioning of the format. - BLOCK_META = llvm::bitc::FIRST_APPLICATION_BLOCKID, - - /// \brief The this block acts as a container for all the information - /// for a specific diagnostic. - BLOCK_DIAG -}; - -enum RecordIDs { - RECORD_VERSION = 1, - RECORD_DIAG, - RECORD_SOURCE_RANGE, - RECORD_DIAG_FLAG, - RECORD_CATEGORY, - RECORD_FILENAME, - RECORD_FIXIT, - RECORD_FIRST = RECORD_VERSION, - RECORD_LAST = RECORD_FIXIT -}; - -/// A stable version of DiagnosticIDs::Level. -/// -/// Do not change the order of values in this enum, and please increment the -/// serialized diagnostics version number when you add to it. -enum Level { - Ignored = 0, - Note, - Warning, - Error, - Fatal, - Remark -}; /// \brief Returns a DiagnosticConsumer that serializes diagnostics to /// a bitcode file. diff --git a/include/clang/Frontend/SerializedDiagnosticReader.h b/include/clang/Frontend/SerializedDiagnosticReader.h new file mode 100644 index 0000000000..92e99d305d --- /dev/null +++ b/include/clang/Frontend/SerializedDiagnosticReader.h @@ -0,0 +1,131 @@ +//===--- SerializedDiagnosticReader.h - Reads diagnostics -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_SERIALIZED_DIAGNOSTIC_READER_H_ +#define LLVM_CLANG_FRONTEND_SERIALIZED_DIAGNOSTIC_READER_H_ + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/ErrorOr.h" + +namespace clang { +namespace serialized_diags { + +enum class SDError { + CouldNotLoad = 1, + InvalidSignature, + InvalidDiagnostics, + MalformedTopLevelBlock, + MalformedSubBlock, + MalformedBlockInfoBlock, + MalformedMetadataBlock, + MalformedDiagnosticBlock, + MalformedDiagnosticRecord, + MissingVersion, + VersionMismatch, + UnsupportedConstruct, + /// A generic error for subclass handlers that don't want or need to define + /// their own error_category. + HandlerFailed +}; + +const std::error_category &SDErrorCategory(); + +inline std::error_code make_error_code(SDError E) { + return std::error_code(static_cast(E), SDErrorCategory()); +} + +/// \brief A location that is represented in the serialized diagnostics. +struct Location { + unsigned FileID; + unsigned Line; + unsigned Col; + unsigned Offset; + Location(unsigned FileID, unsigned Line, unsigned Col, unsigned Offset) + : FileID(FileID), Line(Line), Col(Col), Offset(Offset) {} +}; + +/// \brief A base class that handles reading serialized diagnostics from a file. +/// +/// Subclasses should override the visit* methods with their logic for handling +/// the various constructs that are found in serialized diagnostics. +class SerializedDiagnosticReader { +public: + SerializedDiagnosticReader() {} + virtual ~SerializedDiagnosticReader() {} + + /// \brief Read the diagnostics in \c File + std::error_code readDiagnostics(StringRef File); + +private: + enum class Cursor; + + /// \brief Read to the next record or block to process. + llvm::ErrorOr skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream, + unsigned &BlockOrRecordId); + + /// \brief Read a metadata block from \c Stream. + std::error_code readMetaBlock(llvm::BitstreamCursor &Stream); + + /// \brief Read a diagnostic block from \c Stream. + std::error_code readDiagnosticBlock(llvm::BitstreamCursor &Stream); + +protected: + /// \brief Visit the start of a diagnostic block. + virtual std::error_code visitStartOfDiagnostic() { + return std::error_code(); + }; + /// \brief Visit the end of a diagnostic block. + virtual std::error_code visitEndOfDiagnostic() { return std::error_code(); }; + /// \brief Visit a category. This associates the category \c ID to a \c Name. + virtual std::error_code visitCategoryRecord(unsigned ID, StringRef Name) { + return std::error_code(); + }; + /// \brief Visit a flag. This associates the flag's \c ID to a \c Name. + virtual std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) { + return std::error_code(); + }; + /// \brief Visit a diagnostic. + virtual std::error_code + visitDiagnosticRecord(unsigned Severity, const Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + return std::error_code(); + }; + /// \brief Visit a filename. This associates the file's \c ID to a \c Name. + virtual std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + return std::error_code(); + }; + /// \brief Visit a fixit hint. + virtual std::error_code + visitFixitRecord(const Location &Start, const Location &End, StringRef Text) { + return std::error_code(); + }; + /// \brief Visit a source range. + virtual std::error_code visitSourceRangeRecord(const Location &Start, + const Location &End) { + return std::error_code(); + }; + /// \brief Visit the version of the set of diagnostics. + virtual std::error_code visitVersionRecord(unsigned Version) { + return std::error_code(); + }; +}; + +} // end serialized_diags namespace +} // end clang namespace + +namespace std { +template <> +struct is_error_code_enum : std::true_type {}; +} + +#endif diff --git a/include/clang/Frontend/SerializedDiagnostics.h b/include/clang/Frontend/SerializedDiagnostics.h new file mode 100644 index 0000000000..d36ba487a7 --- /dev/null +++ b/include/clang/Frontend/SerializedDiagnostics.h @@ -0,0 +1,59 @@ +//===--- SerializedDiagnostics.h - Common data for serialized diagnostics -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_SERIALIZE_DIAGNOSTICS_H_ +#define LLVM_CLANG_FRONTEND_SERIALIZE_DIAGNOSTICS_H_ + +#include "llvm/BitCode/BitCodes.h" + +namespace clang { +namespace serialized_diags { + +enum BlockIDs { + /// \brief A top-level block which represents any meta data associated + /// with the diagostics, including versioning of the format. + BLOCK_META = llvm::bitc::FIRST_APPLICATION_BLOCKID, + + /// \brief The this block acts as a container for all the information + /// for a specific diagnostic. + BLOCK_DIAG +}; + +enum RecordIDs { + RECORD_VERSION = 1, + RECORD_DIAG, + RECORD_SOURCE_RANGE, + RECORD_DIAG_FLAG, + RECORD_CATEGORY, + RECORD_FILENAME, + RECORD_FIXIT, + RECORD_FIRST = RECORD_VERSION, + RECORD_LAST = RECORD_FIXIT +}; + +/// \brief A stable version of DiagnosticIDs::Level. +/// +/// Do not change the order of values in this enum, and please increment the +/// serialized diagnostics version number when you add to it. +enum Level { + Ignored = 0, + Note, + Warning, + Error, + Fatal, + Remark +}; + +/// \brief The serialized diagnostics version number. +enum { VersionNumber = 2 }; + +} // end serialized_diags namespace +} // end clang namespace + +#endif diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 403cc42927..cca0b5163b 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangFrontend MultiplexConsumer.cpp PrintPreprocessedOutput.cpp SerializedDiagnosticPrinter.cpp + SerializedDiagnosticReader.cpp TextDiagnostic.cpp TextDiagnosticBuffer.cpp TextDiagnosticPrinter.cpp diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp index 3d6e7578c7..133d86c193 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnostics.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -172,9 +173,6 @@ private: void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, const SourceManager &SM); - /// \brief The version of the diagnostics file. - enum { Version = 2 }; - /// \brief Language options, which can differ from one clone of this client /// to another. const LangOptions *LangOpts; @@ -466,7 +464,7 @@ void SDiagsWriter::EmitMetaBlock() { Stream.EnterSubblock(BLOCK_META, 3); Record.clear(); Record.push_back(RECORD_VERSION); - Record.push_back(Version); + Record.push_back(VersionNumber); Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); Stream.ExitBlock(); } diff --git a/lib/Frontend/SerializedDiagnosticReader.cpp b/lib/Frontend/SerializedDiagnosticReader.cpp new file mode 100644 index 0000000000..6d692e4059 --- /dev/null +++ b/lib/Frontend/SerializedDiagnosticReader.cpp @@ -0,0 +1,296 @@ +//===--- SerializedDiagnosticReader.cpp - Reads diagnostics ---------------===// +// +// 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/Frontend/SerializedDiagnostics.h" +#include "clang/Basic/FileManager.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; +using namespace clang::serialized_diags; + +std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { + // Open the diagnostics file. + FileSystemOptions FO; + FileManager FileMgr(FO); + + std::unique_ptr Buffer = FileMgr.getBufferForFile(File); + if (!Buffer) + return SDError::CouldNotLoad; + + 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') + 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()) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (Stream.ReadBlockInfoBlock()) + return SDError::MalformedBlockInfoBlock; + 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; + } + } + return std::error_code(); +} + +enum class SerializedDiagnosticReader::Cursor { + Record = 1, + BlockEnd, + BlockBegin +}; + +llvm::ErrorOr +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 Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + if (!Res) + Res.getError(); + + switch (Res.get()) { + case Cursor::Record: + break; + case Cursor::BlockBegin: + if (Stream.SkipBlock()) + return SDError::MalformedMetadataBlock; + case Cursor::BlockEnd: + if (!VersionChecked) + return SDError::MissingVersion; + return std::error_code(); + } + + SmallVector 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 Record; + while (true) { + unsigned BlockOrCode = 0; + llvm::ErrorOr 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; + return std::error_code(); + 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 { +class SDErrorCategoryType final : public std::error_category { + const char *name() const LLVM_NOEXCEPT override { + return "clang.serialized_diags"; + } + std::string message(int IE) const override { + SDError E = static_cast(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!"); + } +}; +} + +static llvm::ManagedStatic ErrorCategory; +const std::error_category &clang::serialized_diags::SDErrorCategory() { + return *ErrorCategory; +} diff --git a/tools/libclang/CXLoadedDiagnostic.cpp b/tools/libclang/CXLoadedDiagnostic.cpp index 0e0075fc38..c923b26be9 100644 --- a/tools/libclang/CXLoadedDiagnostic.cpp +++ b/tools/libclang/CXLoadedDiagnostic.cpp @@ -16,7 +16,8 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" -#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "clang/Frontend/SerializedDiagnosticReader.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -183,475 +184,207 @@ void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, // Deserialize diagnostics. //===----------------------------------------------------------------------===// -enum { MaxSupportedVersion = 2 }; -typedef SmallVector RecordData; -enum LoadResult { Failure = 1, Success = 0 }; -enum StreamResult { Read_EndOfStream, - Read_BlockBegin, - Read_Failure, - Read_Record, - Read_BlockEnd }; - namespace { -class DiagLoader { +class DiagLoader : serialized_diags::SerializedDiagnosticReader { enum CXLoadDiag_Error *error; CXString *errorString; - - void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { + std::unique_ptr TopDiags; + SmallVector, 8> CurrentDiags; + + std::error_code reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { if (error) *error = code; if (errorString) *errorString = cxstring::createDup(err); + return serialized_diags::SDError::HandlerFailed; } - void reportInvalidFile(llvm::StringRef err) { + std::error_code 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); - + std::error_code readRange(const serialized_diags::Location &SDStart, + const serialized_diags::Location &SDEnd, + CXSourceRange &SR); + + std::error_code readLocation(const serialized_diags::Location &SDLoc, + CXLoadedDiagnostic::Location &LoadedLoc); + +protected: + std::error_code visitStartOfDiagnostic() override; + std::error_code visitEndOfDiagnostic() override; + + std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; + + std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; + + std::error_code visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) override; + + std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) override; + + std::error_code visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) override; + + std::error_code + visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) override; + public: DiagLoader(enum CXLoadDiag_Error *e, CXString *es) - : error(e), errorString(es) { - if (error) - *error = CXLoadDiag_None; - if (errorString) - *errorString = cxstring::createEmpty(); - } + : SerializedDiagnosticReader(), 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 Buffer = FileMgr.getBufferForFile(file); - if (!Buffer) { - reportBad(CXLoadDiag_CannotLoad, ErrStr); - return nullptr; - } - - 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 nullptr; - } - - std::unique_ptr 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 nullptr; - 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 nullptr; - break; - case serialized_diags::BLOCK_DIAG: - if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get())) - return nullptr; - break; - default: - if (!Stream.SkipBlock()) { - reportInvalidFile("Malformed block at top-level of diagnostics file"); - return nullptr; - } - break; + TopDiags = llvm::make_unique(); + + std::error_code EC = readDiagnostics(file); + if (EC) { + switch (EC.value()) { + case static_cast(serialized_diags::SDError::HandlerFailed): + // We've already reported the problem. + break; + case static_cast(serialized_diags::SDError::CouldNotLoad): + reportBad(CXLoadDiag_CannotLoad, EC.message()); + break; + default: + reportInvalidFile(EC.message()); + break; } + return 0; } -} -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; + return (CXDiagnosticSet)TopDiags.release(); } -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; - } +std::error_code +DiagLoader::readLocation(const serialized_diags::Location &SDLoc, + CXLoadedDiagnostic::Location &LoadedLoc) { + unsigned FileID = SDLoc.FileID; + if (FileID == 0) + LoadedLoc.file = nullptr; + else { + LoadedLoc.file = const_cast(TopDiags->Files[FileID]); + if (!LoadedLoc.file) + return reportInvalidFile("Corrupted file entry in source location"); } + LoadedLoc.line = SDLoc.Line; + LoadedLoc.column = SDLoc.Col; + LoadedLoc.offset = SDLoc.Offset; + return std::error_code(); } -LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, - const char *&RetStr, - llvm::StringRef errorContext, - RecordData &Record, - StringRef Blob, - bool allowEmptyString) { +std::error_code +DiagLoader::readRange(const serialized_diags::Location &SDStart, + const serialized_diags::Location &SDEnd, + CXSourceRange &SR) { + CXLoadedDiagnostic::Location *Start, *End; + Start = TopDiags->Alloc.Allocate(); + End = TopDiags->Alloc.Allocate(); + + std::error_code EC; + if ((EC = readLocation(SDStart, *Start))) + return EC; + if ((EC = readLocation(SDEnd, *End))) + return EC; - // Basic buffer overflow check. - if (Blob.size() > 65536) { - reportInvalidFile(std::string("Out-of-bounds string in ") + - std::string(errorContext)); - return Failure; - } + CXSourceLocation startLoc = makeLocation(Start); + CXSourceLocation endLoc = makeLocation(End); + SR = clang_getRange(startLoc, endLoc); + return std::error_code(); +} - 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; +std::error_code DiagLoader::visitStartOfDiagnostic() { + CurrentDiags.push_back(llvm::make_unique()); + return std::error_code(); } -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; +std::error_code DiagLoader::visitEndOfDiagnostic() { + auto D = CurrentDiags.pop_back_val(); + if (CurrentDiags.empty()) + TopDiags->appendDiagnostic(std::move(D)); + else + CurrentDiags.back()->getChildDiagnostics().appendDiagnostic(std::move(D)); + return std::error_code(); } -LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags, - RecordData &Record, unsigned &offset, - CXLoadedDiagnostic::Location &Loc) { - if (Record.size() < offset + 4) { - reportInvalidFile("Corrupted source location"); - return Failure; - } - auto Fields = makeArrayRef(Record).slice(offset); - offset += 4; - - unsigned fileID = Fields[0]; - if (fileID == 0) { - // Sentinel value. - Loc.file = nullptr; - Loc.line = 0; - Loc.column = 0; - Loc.offset = 0; - return Success; - } +std::error_code DiagLoader::visitCategoryRecord(unsigned ID, StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in category"); + TopDiags->Categories[ID] = TopDiags->copyString(Name); + return std::error_code(); +} - const FileEntry *FE = TopDiags.Files[fileID]; - if (!FE) { - reportInvalidFile("Corrupted file entry in source location"); - return Failure; - } - Loc.file = const_cast(FE); - Loc.line = Fields[1]; - Loc.column = Fields[2]; - Loc.offset = Fields[3]; - return Success; +std::error_code DiagLoader::visitDiagFlagRecord(unsigned ID, StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in warning flag"); + TopDiags->WarningFlags[ID] = TopDiags->copyString(Name); + return std::error_code(); } -LoadResult DiagLoader::readRange(CXLoadedDiagnosticSetImpl &TopDiags, - RecordData &Record, - unsigned int RecStartIdx, - CXSourceRange &SR) { - CXLoadedDiagnostic::Location *Start, *End; - Start = TopDiags.Alloc.Allocate(); - End = TopDiags.Alloc.Allocate(); - - 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; +std::error_code DiagLoader::visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in filename"); + TopDiags->FileNames[ID] = TopDiags->copyString(Name); + TopDiags->Files[ID] = + TopDiags->FakeFiles.getVirtualFile(Name, Size, Timestamp); + return std::error_code(); } -LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream, - CXDiagnosticSetImpl &Diags, - CXLoadedDiagnosticSetImpl &TopDiags){ +std::error_code +DiagLoader::visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) { + CXSourceRange SR; + if (std::error_code EC = readRange(Start, End, SR)) + return EC; + CurrentDiags.back()->Ranges.push_back(SR); + return std::error_code(); +} - if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { - reportInvalidFile("malformed diagnostic block"); - return Failure; - } +std::error_code +DiagLoader::visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) { + CXSourceRange SR; + if (std::error_code EC = readRange(Start, End, SR)) + return EC; + // FIXME: Why do we care about long strings? + if (CodeToInsert.size() > 65536) + return reportInvalidFile("Out-of-bounds string in FIXIT"); + CurrentDiags.back()->FixIts.push_back( + std::make_pair(SR, TopDiags->copyString(CodeToInsert))); + return std::error_code(); +} - std::unique_ptr 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(std::move(D)); - 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; - } - } - } +std::error_code DiagLoader::visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + CXLoadedDiagnostic &D = *CurrentDiags.back(); + D.severity = Severity; + if (std::error_code EC = readLocation(Location, D.DiagLoc)) + return EC; + D.category = Category; + D.DiagOption = Flag ? TopDiags->WarningFlags[Flag] : ""; + D.CategoryText = Category ? TopDiags->Categories[Category] : ""; + D.Spelling = TopDiags->copyString(Message); + return std::error_code(); } extern "C" {