From: Justin Bogner Date: Tue, 14 Oct 2014 00:57:34 +0000 (+0000) Subject: Revert "Frontend: Extract SerializedDiagnosticReader out of CXLoadedDiagnostic (NFC)" X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bd272ece3ffe5fbe65596643aa2d7d6adf6f98d5;p=clang Revert "Frontend: Extract SerializedDiagnosticReader out of CXLoadedDiagnostic (NFC)" The bots can't seem to find an include file. Reverting for now and I'll look into it in a bit. This reverts commits r219647 and r219648. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@219649 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Frontend/SerializedDiagnosticPrinter.h b/include/clang/Frontend/SerializedDiagnosticPrinter.h index f00a68bb89..207f9f1984 100644 --- a/include/clang/Frontend/SerializedDiagnosticPrinter.h +++ b/include/clang/Frontend/SerializedDiagnosticPrinter.h @@ -11,7 +11,6 @@ #define LLVM_CLANG_FRONTEND_SERIALIZEDDIAGNOSTICPRINTER_H #include "clang/Basic/LLVM.h" -#include "clang/Frontend/SerializedDiagnostics.h" #include "llvm/Bitcode/BitstreamWriter.h" namespace llvm { @@ -24,6 +23,41 @@ 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 deleted file mode 100644 index 92e99d305d..0000000000 --- a/include/clang/Frontend/SerializedDiagnosticReader.h +++ /dev/null @@ -1,131 +0,0 @@ -//===--- 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 deleted file mode 100644 index d36ba487a7..0000000000 --- a/include/clang/Frontend/SerializedDiagnostics.h +++ /dev/null @@ -1,59 +0,0 @@ -//===--- 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 228bd8915c..403cc42927 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(Rewrite) set(LLVM_LINK_COMPONENTS - BitReader Option Support ) @@ -32,7 +31,6 @@ 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 133d86c193..3d6e7578c7 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #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" @@ -173,6 +172,9 @@ 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; @@ -464,7 +466,7 @@ void SDiagsWriter::EmitMetaBlock() { Stream.EnterSubblock(BLOCK_META, 3); Record.clear(); Record.push_back(RECORD_VERSION); - Record.push_back(VersionNumber); + Record.push_back(Version); Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); Stream.ExitBlock(); } diff --git a/lib/Frontend/SerializedDiagnosticReader.cpp b/lib/Frontend/SerializedDiagnosticReader.cpp deleted file mode 100644 index 6d692e4059..0000000000 --- a/lib/Frontend/SerializedDiagnosticReader.cpp +++ /dev/null @@ -1,296 +0,0 @@ -//===--- 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 c923b26be9..0e0075fc38 100644 --- a/tools/libclang/CXLoadedDiagnostic.cpp +++ b/tools/libclang/CXLoadedDiagnostic.cpp @@ -16,8 +16,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" -#include "clang/Frontend/SerializedDiagnostics.h" -#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -184,207 +183,475 @@ 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 : serialized_diags::SerializedDiagnosticReader { +class DiagLoader { enum CXLoadDiag_Error *error; CXString *errorString; - std::unique_ptr TopDiags; - SmallVector, 8> CurrentDiags; - - std::error_code reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { + + void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { if (error) *error = code; if (errorString) *errorString = cxstring::createDup(err); - return serialized_diags::SDError::HandlerFailed; } - std::error_code reportInvalidFile(llvm::StringRef err) { + void reportInvalidFile(llvm::StringRef err) { return reportBad(CXLoadDiag_InvalidFile, err); } - 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; - + 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); + public: DiagLoader(enum CXLoadDiag_Error *e, CXString *es) - : SerializedDiagnosticReader(), error(e), errorString(es) { - if (error) - *error = CXLoadDiag_None; - if (errorString) - *errorString = cxstring::createEmpty(); - } + : 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) { - 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; + // 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; } - return (CXDiagnosticSet)TopDiags.release(); -} + 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::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"); + 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; + } } - LoadedLoc.line = SDLoc.Line; - LoadedLoc.column = SDLoc.Col; - LoadedLoc.offset = SDLoc.Offset; - return std::error_code(); } -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; +StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, + llvm::StringRef errorContext, + unsigned &blockOrRecordID, + bool atTopLevel) { - CXSourceLocation startLoc = makeLocation(Start); - CXSourceLocation endLoc = makeLocation(End); - SR = clang_getRange(startLoc, endLoc); - return std::error_code(); + 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; } -std::error_code DiagLoader::visitStartOfDiagnostic() { - CurrentDiags.push_back(llvm::make_unique()); - return std::error_code(); -} +LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { + reportInvalidFile("Malformed metadata block"); + return Failure; + } -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(); + 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::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(); -} +LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, + const char *&RetStr, + llvm::StringRef errorContext, + RecordData &Record, + StringRef Blob, + bool allowEmptyString) { + + // Basic buffer overflow check. + if (Blob.size() > 65536) { + reportInvalidFile(std::string("Out-of-bounds string in ") + + std::string(errorContext)); + return Failure; + } -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(); + 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::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::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::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(); +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; + } + + 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::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(); +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::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(); +LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream, + CXDiagnosticSetImpl &Diags, + CXLoadedDiagnosticSetImpl &TopDiags){ + + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { + reportInvalidFile("malformed diagnostic block"); + return Failure; + } + + 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; + } + } + } } extern "C" {