]> granicus.if.org Git - clang/commitdiff
Frontend: Extract SerializedDiagnosticReader out of CXLoadedDiagnostic (NFC)
authorJustin Bogner <mail@justinbogner.com>
Tue, 14 Oct 2014 00:40:55 +0000 (00:40 +0000)
committerJustin Bogner <mail@justinbogner.com>
Tue, 14 Oct 2014 00:40:55 +0000 (00:40 +0000)
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

include/clang/Frontend/SerializedDiagnosticPrinter.h
include/clang/Frontend/SerializedDiagnosticReader.h [new file with mode: 0644]
include/clang/Frontend/SerializedDiagnostics.h [new file with mode: 0644]
lib/Frontend/CMakeLists.txt
lib/Frontend/SerializedDiagnosticPrinter.cpp
lib/Frontend/SerializedDiagnosticReader.cpp [new file with mode: 0644]
tools/libclang/CXLoadedDiagnostic.cpp

index 207f9f198466f9f80976f5556eb85b6693204aaa..f00a68bb8942592afb6cfbfcafc34e62044d1da8 100644 (file)
@@ -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 (file)
index 0000000..92e99d3
--- /dev/null
@@ -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<int>(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<Cursor> 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<clang::serialized_diags::SDError> : std::true_type {};
+}
+
+#endif
diff --git a/include/clang/Frontend/SerializedDiagnostics.h b/include/clang/Frontend/SerializedDiagnostics.h
new file mode 100644 (file)
index 0000000..d36ba48
--- /dev/null
@@ -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
index 403cc42927a63f1f750031ef3e1f7292696832f7..cca0b5163b1825f02f4a8c4e6065e8608e85f7fd 100644 (file)
@@ -31,6 +31,7 @@ add_clang_library(clangFrontend
   MultiplexConsumer.cpp
   PrintPreprocessedOutput.cpp
   SerializedDiagnosticPrinter.cpp
+  SerializedDiagnosticReader.cpp
   TextDiagnostic.cpp
   TextDiagnosticBuffer.cpp
   TextDiagnosticPrinter.cpp
index 3d6e7578c78d805c3f1dba1ed088c4874b27b48f..133d86c1938e44d47eb2136e4a4d340e967a51c8 100644 (file)
@@ -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 (file)
index 0000000..6d692e4
--- /dev/null
@@ -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<llvm::MemoryBuffer> 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::Cursor>
+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<Cursor> 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<uint64_t, 1> 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<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 (!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<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!");
+  }
+};
+}
+
+static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
+const std::error_category &clang::serialized_diags::SDErrorCategory() {
+  return *ErrorCategory;
+}
index 0e0075fc387ef8c779d6cf1d750c9580b95ac00a..c923b26be9cee2bccffad3066028ad0bc0b92e7a 100644 (file)
@@ -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<uint64_t, 64> 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<CXLoadedDiagnosticSetImpl> TopDiags;
+  SmallVector<std::unique_ptr<CXLoadedDiagnostic>, 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<llvm::MemoryBuffer> 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<CXLoadedDiagnosticSetImpl> 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<CXLoadedDiagnosticSetImpl>();
+
+  std::error_code EC = readDiagnostics(file);
+  if (EC) {
+    switch (EC.value()) {
+    case static_cast<int>(serialized_diags::SDError::HandlerFailed):
+      // We've already reported the problem.
+      break;
+    case static_cast<int>(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<FileEntry *>(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<CXLoadedDiagnostic::Location>();
+  End = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>();
+
+  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<CXLoadedDiagnostic>());
+  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<FileEntry *>(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<CXLoadedDiagnostic::Location>();
-  End = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>();
-  
-  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<CXLoadedDiagnostic> 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" {