Home | History | Annotate | Download | only in libclang
      1 /*===-- CXLoadedDiagnostic.cpp - Handling of persisent diags -*- C++ -*-===*\
      2 |*                                                                            *|
      3 |*                     The LLVM Compiler Infrastructure                       *|
      4 |*                                                                            *|
      5 |* This file is distributed under the University of Illinois Open Source      *|
      6 |* License. See LICENSE.TXT for details.                                      *|
      7 |*                                                                            *|
      8 |*===----------------------------------------------------------------------===*|
      9 |*                                                                            *|
     10 |* Implements handling of persisent diagnostics.                              *|
     11 |*                                                                            *|
     12 \*===----------------------------------------------------------------------===*/
     13 
     14 #include "CXLoadedDiagnostic.h"
     15 #include "CXString.h"
     16 #include "clang/Basic/Diagnostic.h"
     17 #include "clang/Basic/FileManager.h"
     18 #include "clang/Frontend/SerializedDiagnosticPrinter.h"
     19 #include "llvm/ADT/StringRef.h"
     20 #include "llvm/ADT/Twine.h"
     21 #include "llvm/ADT/Optional.h"
     22 #include "clang/Basic/LLVM.h"
     23 #include "llvm/Support/ErrorHandling.h"
     24 #include "llvm/Bitcode/BitstreamReader.h"
     25 #include "llvm/Support/MemoryBuffer.h"
     26 #include <assert.h>
     27 
     28 using namespace clang;
     29 using namespace clang::cxstring;
     30 
     31 //===----------------------------------------------------------------------===//
     32 // Extend CXDiagnosticSetImpl which contains strings for diagnostics.
     33 //===----------------------------------------------------------------------===//
     34 
     35 typedef llvm::DenseMap<unsigned, llvm::StringRef> Strings;
     36 
     37 namespace {
     38 class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl {
     39 public:
     40   CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {}
     41   virtual ~CXLoadedDiagnosticSetImpl() {}
     42 
     43   llvm::StringRef makeString(const char *blob, unsigned blobLen);
     44 
     45   llvm::BumpPtrAllocator Alloc;
     46   Strings Categories;
     47   Strings WarningFlags;
     48   Strings FileNames;
     49 
     50   FileSystemOptions FO;
     51   FileManager FakeFiles;
     52   llvm::DenseMap<unsigned, const FileEntry *> Files;
     53 };
     54 }
     55 
     56 llvm::StringRef CXLoadedDiagnosticSetImpl::makeString(const char *blob,
     57                                                       unsigned bloblen) {
     58   char *mem = Alloc.Allocate<char>(bloblen + 1);
     59   memcpy(mem, blob, bloblen);
     60   // Add a null terminator for those clients accessing the buffer
     61   // like a c-string.
     62   mem[bloblen] = '\0';
     63   return llvm::StringRef(mem, bloblen);
     64 }
     65 
     66 //===----------------------------------------------------------------------===//
     67 // Cleanup.
     68 //===----------------------------------------------------------------------===//
     69 
     70 CXLoadedDiagnostic::~CXLoadedDiagnostic() {}
     71 
     72 //===----------------------------------------------------------------------===//
     73 // Public CXLoadedDiagnostic methods.
     74 //===----------------------------------------------------------------------===//
     75 
     76 CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const {
     77   // FIXME: possibly refactor with logic in CXStoredDiagnostic.
     78   switch (severity) {
     79     case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored;
     80     case DiagnosticsEngine::Note:    return CXDiagnostic_Note;
     81     case DiagnosticsEngine::Warning: return CXDiagnostic_Warning;
     82     case DiagnosticsEngine::Error:   return CXDiagnostic_Error;
     83     case DiagnosticsEngine::Fatal:   return CXDiagnostic_Fatal;
     84   }
     85 
     86   llvm_unreachable("Invalid diagnostic level");
     87 }
     88 
     89 static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) {
     90   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
     91   // is a persistent diagnostic.
     92   uintptr_t V = (uintptr_t) DLoc;
     93   V |= 0x1;
     94   CXSourceLocation Loc = { {  (void*) V, 0 }, 0 };
     95   return Loc;
     96 }
     97 
     98 CXSourceLocation CXLoadedDiagnostic::getLocation() const {
     99   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
    100   // is a persistent diagnostic.
    101   return makeLocation(&DiagLoc);
    102 }
    103 
    104 CXString CXLoadedDiagnostic::getSpelling() const {
    105   return cxstring::createCXString(Spelling, false);
    106 }
    107 
    108 CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const {
    109   if (DiagOption.empty())
    110     return createCXString("");
    111 
    112   // FIXME: possibly refactor with logic in CXStoredDiagnostic.
    113   if (Disable)
    114     *Disable = createCXString((Twine("-Wno-") + DiagOption).str());
    115   return createCXString((Twine("-W") + DiagOption).str());
    116 }
    117 
    118 unsigned CXLoadedDiagnostic::getCategory() const {
    119   return category;
    120 }
    121 
    122 CXString CXLoadedDiagnostic::getCategoryText() const {
    123   return cxstring::createCXString(CategoryText);
    124 }
    125 
    126 unsigned CXLoadedDiagnostic::getNumRanges() const {
    127   return Ranges.size();
    128 }
    129 
    130 CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const {
    131   assert(Range < Ranges.size());
    132   return Ranges[Range];
    133 }
    134 
    135 unsigned CXLoadedDiagnostic::getNumFixIts() const {
    136   return FixIts.size();
    137 }
    138 
    139 CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt,
    140                                       CXSourceRange *ReplacementRange) const {
    141   assert(FixIt < FixIts.size());
    142   if (ReplacementRange)
    143     *ReplacementRange = FixIts[FixIt].first;
    144   return FixIts[FixIt].second;
    145 }
    146 
    147 void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location,
    148                                         CXFile *file,
    149                                         unsigned int *line,
    150                                         unsigned int *column,
    151                                         unsigned int *offset) {
    152 
    153 
    154   // CXSourceLocation consists of the following fields:
    155   //
    156   //   void *ptr_data[2];
    157   //   unsigned int_data;
    158   //
    159   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
    160   // is a persistent diagnostic.
    161   //
    162   // For now, do the unoptimized approach and store the data in a side
    163   // data structure.  We can optimize this case later.
    164 
    165   uintptr_t V = (uintptr_t) location.ptr_data[0];
    166   assert((V & 0x1) == 1);
    167   V &= ~(uintptr_t)1;
    168 
    169   const Location &Loc = *((Location*)V);
    170 
    171   if (file)
    172     *file = Loc.file;
    173   if (line)
    174     *line = Loc.line;
    175   if (column)
    176     *column = Loc.column;
    177   if (offset)
    178     *offset = Loc.offset;
    179 }
    180 
    181 //===----------------------------------------------------------------------===//
    182 // Deserialize diagnostics.
    183 //===----------------------------------------------------------------------===//
    184 
    185 enum { MaxSupportedVersion = 1 };
    186 typedef SmallVector<uint64_t, 64> RecordData;
    187 enum LoadResult { Failure = 1, Success = 0 };
    188 enum StreamResult { Read_EndOfStream,
    189                     Read_BlockBegin,
    190                     Read_Failure,
    191                     Read_Record,
    192                     Read_BlockEnd };
    193 
    194 namespace {
    195 class DiagLoader {
    196   enum CXLoadDiag_Error *error;
    197   CXString *errorString;
    198 
    199   void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) {
    200     if (error)
    201       *error = code;
    202     if (errorString)
    203       *errorString = createCXString(err);
    204   }
    205 
    206   void reportInvalidFile(llvm::StringRef err) {
    207     return reportBad(CXLoadDiag_InvalidFile, err);
    208   }
    209 
    210   LoadResult readMetaBlock(llvm::BitstreamCursor &Stream);
    211 
    212   LoadResult readDiagnosticBlock(llvm::BitstreamCursor &Stream,
    213                                  CXDiagnosticSetImpl &Diags,
    214                                  CXLoadedDiagnosticSetImpl &TopDiags);
    215 
    216   StreamResult readToNextRecordOrBlock(llvm::BitstreamCursor &Stream,
    217                                        llvm::StringRef errorContext,
    218                                        unsigned &BlockOrRecordID,
    219                                        const bool atTopLevel = false);
    220 
    221 
    222   LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags,
    223                         Strings &strings, llvm::StringRef errorContext,
    224                         RecordData &Record,
    225                         const char *BlobStart,
    226                         unsigned BlobLen,
    227                         bool allowEmptyString = false);
    228 
    229   LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags,
    230                         llvm::StringRef &RetStr,
    231                         llvm::StringRef errorContext,
    232                         RecordData &Record,
    233                         const char *BlobStart,
    234                         unsigned BlobLen,
    235                         bool allowEmptyString = false);
    236 
    237   LoadResult readRange(CXLoadedDiagnosticSetImpl &TopDiags,
    238                        RecordData &Record, unsigned RecStartIdx,
    239                        CXSourceRange &SR);
    240 
    241   LoadResult readLocation(CXLoadedDiagnosticSetImpl &TopDiags,
    242                           RecordData &Record, unsigned &offset,
    243                           CXLoadedDiagnostic::Location &Loc);
    244 
    245 public:
    246   DiagLoader(enum CXLoadDiag_Error *e, CXString *es)
    247     : error(e), errorString(es) {
    248       if (error)
    249         *error = CXLoadDiag_None;
    250       if (errorString)
    251         *errorString = createCXString("");
    252     }
    253 
    254   CXDiagnosticSet load(const char *file);
    255 };
    256 }
    257 
    258 CXDiagnosticSet DiagLoader::load(const char *file) {
    259   // Open the diagnostics file.
    260   std::string ErrStr;
    261   FileSystemOptions FO;
    262   FileManager FileMgr(FO);
    263 
    264   OwningPtr<llvm::MemoryBuffer> Buffer;
    265   Buffer.reset(FileMgr.getBufferForFile(file));
    266 
    267   if (!Buffer) {
    268     reportBad(CXLoadDiag_CannotLoad, ErrStr);
    269     return 0;
    270   }
    271 
    272   llvm::BitstreamReader StreamFile;
    273   StreamFile.init((const unsigned char *)Buffer->getBufferStart(),
    274                   (const unsigned char *)Buffer->getBufferEnd());
    275 
    276   llvm::BitstreamCursor Stream;
    277   Stream.init(StreamFile);
    278 
    279   // Sniff for the signature.
    280   if (Stream.Read(8) != 'D' ||
    281       Stream.Read(8) != 'I' ||
    282       Stream.Read(8) != 'A' ||
    283       Stream.Read(8) != 'G') {
    284     reportBad(CXLoadDiag_InvalidFile,
    285               "Bad header in diagnostics file");
    286     return 0;
    287   }
    288 
    289   OwningPtr<CXLoadedDiagnosticSetImpl>
    290     Diags(new CXLoadedDiagnosticSetImpl());
    291 
    292   while (true) {
    293     unsigned BlockID = 0;
    294     StreamResult Res = readToNextRecordOrBlock(Stream, "Top-level",
    295                                                BlockID, true);
    296     switch (Res) {
    297       case Read_EndOfStream:
    298         return (CXDiagnosticSet) Diags.take();
    299       case Read_Failure:
    300         return 0;
    301       case Read_Record:
    302         llvm_unreachable("Top-level does not have records");
    303       case Read_BlockEnd:
    304         continue;
    305       case Read_BlockBegin:
    306         break;
    307     }
    308 
    309     switch (BlockID) {
    310       case serialized_diags::BLOCK_META:
    311         if (readMetaBlock(Stream))
    312           return 0;
    313         break;
    314       case serialized_diags::BLOCK_DIAG:
    315         if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get()))
    316           return 0;
    317         break;
    318       default:
    319         if (!Stream.SkipBlock()) {
    320           reportInvalidFile("Malformed block at top-level of diagnostics file");
    321           return 0;
    322         }
    323         break;
    324     }
    325   }
    326 }
    327 
    328 StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream,
    329                                                  llvm::StringRef errorContext,
    330                                                  unsigned &blockOrRecordID,
    331                                                  const bool atTopLevel) {
    332 
    333   blockOrRecordID = 0;
    334 
    335   while (!Stream.AtEndOfStream()) {
    336     unsigned Code = Stream.ReadCode();
    337 
    338     // Handle the top-level specially.
    339     if (atTopLevel) {
    340       if (Code == llvm::bitc::ENTER_SUBBLOCK) {
    341         unsigned BlockID = Stream.ReadSubBlockID();
    342         if (BlockID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
    343           if (Stream.ReadBlockInfoBlock()) {
    344             reportInvalidFile("Malformed BlockInfoBlock in diagnostics file");
    345             return Read_Failure;
    346           }
    347           continue;
    348         }
    349         blockOrRecordID = BlockID;
    350         return Read_BlockBegin;
    351       }
    352       reportInvalidFile("Only blocks can appear at the top of a "
    353                         "diagnostic file");
    354       return Read_Failure;
    355     }
    356 
    357     switch ((llvm::bitc::FixedAbbrevIDs)Code) {
    358       case llvm::bitc::ENTER_SUBBLOCK:
    359         blockOrRecordID = Stream.ReadSubBlockID();
    360         return Read_BlockBegin;
    361 
    362       case llvm::bitc::END_BLOCK:
    363         if (Stream.ReadBlockEnd()) {
    364           reportInvalidFile("Cannot read end of block");
    365           return Read_Failure;
    366         }
    367         return Read_BlockEnd;
    368 
    369       case llvm::bitc::DEFINE_ABBREV:
    370         Stream.ReadAbbrevRecord();
    371         continue;
    372 
    373       case llvm::bitc::UNABBREV_RECORD:
    374         reportInvalidFile("Diagnostics file should have no unabbreviated "
    375                           "records");
    376         return Read_Failure;
    377 
    378       default:
    379         // We found a record.
    380         blockOrRecordID = Code;
    381         return Read_Record;
    382     }
    383   }
    384 
    385   if (atTopLevel)
    386     return Read_EndOfStream;
    387 
    388   reportInvalidFile(Twine("Premature end of diagnostics file within ").str() +
    389                     errorContext.str());
    390   return Read_Failure;
    391 }
    392 
    393 LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) {
    394   if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {
    395     reportInvalidFile("Malformed metadata block");
    396     return Failure;
    397   }
    398 
    399   bool versionChecked = false;
    400 
    401   while (true) {
    402     unsigned blockOrCode = 0;
    403     StreamResult Res = readToNextRecordOrBlock(Stream, "Metadata Block",
    404                                                blockOrCode);
    405 
    406     switch(Res) {
    407       case Read_EndOfStream:
    408         llvm_unreachable("EndOfStream handled by readToNextRecordOrBlock");
    409       case Read_Failure:
    410         return Failure;
    411       case Read_Record:
    412         break;
    413       case Read_BlockBegin:
    414         if (Stream.SkipBlock()) {
    415           reportInvalidFile("Malformed metadata block");
    416           return Failure;
    417         }
    418       case Read_BlockEnd:
    419         if (!versionChecked) {
    420           reportInvalidFile("Diagnostics file does not contain version"
    421                             " information");
    422           return Failure;
    423         }
    424         return Success;
    425     }
    426 
    427     RecordData Record;
    428     const char *Blob;
    429     unsigned BlobLen;
    430     unsigned recordID = Stream.ReadRecord(blockOrCode, Record, &Blob, &BlobLen);
    431 
    432     if (recordID == serialized_diags::RECORD_VERSION) {
    433       if (Record.size() < 1) {
    434         reportInvalidFile("malformed VERSION identifier in diagnostics file");
    435         return Failure;
    436       }
    437       if (Record[0] > MaxSupportedVersion) {
    438         reportInvalidFile("diagnosics file is a newer version than the one "
    439                           "supported");
    440         return Failure;
    441       }
    442       versionChecked = true;
    443     }
    444   }
    445 }
    446 
    447 LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags,
    448                                   llvm::StringRef &RetStr,
    449                                   llvm::StringRef errorContext,
    450                                   RecordData &Record,
    451                                   const char *BlobStart,
    452                                   unsigned BlobLen,
    453                                   bool allowEmptyString) {
    454 
    455   // Basic buffer overflow check.
    456   if (BlobLen > 65536) {
    457     reportInvalidFile(std::string("Out-of-bounds string in ") +
    458                       std::string(errorContext));
    459     return Failure;
    460   }
    461 
    462   if (allowEmptyString && Record.size() >= 1 && BlobLen == 0) {
    463     RetStr = "";
    464     return Success;
    465   }
    466 
    467   if (Record.size() < 1 || BlobLen == 0) {
    468     reportInvalidFile(std::string("Corrupted ") + std::string(errorContext)
    469                       + std::string(" entry"));
    470     return Failure;
    471   }
    472 
    473   RetStr = TopDiags.makeString(BlobStart, BlobLen);
    474   return Success;
    475 }
    476 
    477 LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags,
    478                                   Strings &strings,
    479                                   llvm::StringRef errorContext,
    480                                   RecordData &Record,
    481                                   const char *BlobStart,
    482                                   unsigned BlobLen,
    483                                   bool allowEmptyString) {
    484   llvm::StringRef RetStr;
    485   if (readString(TopDiags, RetStr, errorContext, Record, BlobStart, BlobLen,
    486                  allowEmptyString))
    487     return Failure;
    488   strings[Record[0]] = RetStr;
    489   return Success;
    490 }
    491 
    492 LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags,
    493                                     RecordData &Record, unsigned &offset,
    494                                     CXLoadedDiagnostic::Location &Loc) {
    495   if (Record.size() < offset + 3) {
    496     reportInvalidFile("Corrupted source location");
    497     return Failure;
    498   }
    499 
    500   unsigned fileID = Record[offset++];
    501   if (fileID == 0) {
    502     // Sentinel value.
    503     Loc.file = 0;
    504     Loc.line = 0;
    505     Loc.column = 0;
    506     Loc.offset = 0;
    507     return Success;
    508   }
    509 
    510   const FileEntry *FE = TopDiags.Files[fileID];
    511   if (!FE) {
    512     reportInvalidFile("Corrupted file entry in source location");
    513     return Failure;
    514   }
    515   Loc.file = (void*) FE;
    516   Loc.line = Record[offset++];
    517   Loc.column = Record[offset++];
    518   Loc.offset = Record[offset++];
    519   return Success;
    520 }
    521 
    522 LoadResult DiagLoader::readRange(CXLoadedDiagnosticSetImpl &TopDiags,
    523                                  RecordData &Record,
    524                                  unsigned int RecStartIdx,
    525                                  CXSourceRange &SR) {
    526   CXLoadedDiagnostic::Location *Start, *End;
    527   Start = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>();
    528   End = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>();
    529 
    530   if (readLocation(TopDiags, Record, RecStartIdx, *Start))
    531     return Failure;
    532   if (readLocation(TopDiags, Record, RecStartIdx, *End))
    533     return Failure;
    534 
    535   CXSourceLocation startLoc = makeLocation(Start);
    536   CXSourceLocation endLoc = makeLocation(End);
    537   SR = clang_getRange(startLoc, endLoc);
    538   return Success;
    539 }
    540 
    541 LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream,
    542                                            CXDiagnosticSetImpl &Diags,
    543                                            CXLoadedDiagnosticSetImpl &TopDiags){
    544 
    545   if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
    546     reportInvalidFile("malformed diagnostic block");
    547     return Failure;
    548   }
    549 
    550   OwningPtr<CXLoadedDiagnostic> D(new CXLoadedDiagnostic());
    551   RecordData Record;
    552 
    553   while (true) {
    554     unsigned blockOrCode = 0;
    555     StreamResult Res = readToNextRecordOrBlock(Stream, "Diagnostic Block",
    556                                                blockOrCode);
    557     switch (Res) {
    558       case Read_EndOfStream:
    559         llvm_unreachable("EndOfStream handled in readToNextRecordOrBlock");
    560       case Read_Failure:
    561         return Failure;
    562       case Read_BlockBegin: {
    563         // The only blocks we care about are subdiagnostics.
    564         if (blockOrCode != serialized_diags::BLOCK_DIAG) {
    565           if (!Stream.SkipBlock()) {
    566             reportInvalidFile("Invalid subblock in Diagnostics block");
    567             return Failure;
    568           }
    569         } else if (readDiagnosticBlock(Stream, D->getChildDiagnostics(),
    570                                        TopDiags)) {
    571           return Failure;
    572         }
    573 
    574         continue;
    575       }
    576       case Read_BlockEnd:
    577         Diags.appendDiagnostic(D.take());
    578         return Success;
    579       case Read_Record:
    580         break;
    581     }
    582 
    583     // Read the record.
    584     Record.clear();
    585     const char *BlobStart = 0;
    586     unsigned BlobLen = 0;
    587     unsigned recID = Stream.ReadRecord(blockOrCode, Record,
    588                                        BlobStart, BlobLen);
    589 
    590     if (recID < serialized_diags::RECORD_FIRST ||
    591         recID > serialized_diags::RECORD_LAST)
    592       continue;
    593 
    594     switch ((serialized_diags::RecordIDs)recID) {
    595       case serialized_diags::RECORD_VERSION:
    596         continue;
    597       case serialized_diags::RECORD_CATEGORY:
    598         if (readString(TopDiags, TopDiags.Categories, "category", Record,
    599                        BlobStart, BlobLen,
    600                        /* allowEmptyString */ true))
    601           return Failure;
    602         continue;
    603 
    604       case serialized_diags::RECORD_DIAG_FLAG:
    605         if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record,
    606                        BlobStart, BlobLen))
    607           return Failure;
    608         continue;
    609 
    610       case serialized_diags::RECORD_FILENAME: {
    611         if (readString(TopDiags, TopDiags.FileNames, "filename", Record,
    612                        BlobStart, BlobLen))
    613           return Failure;
    614 
    615         if (Record.size() < 3) {
    616           reportInvalidFile("Invalid file entry");
    617           return Failure;
    618         }
    619 
    620         const FileEntry *FE =
    621           TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]],
    622                                             /* size */ Record[1],
    623                                             /* time */ Record[2]);
    624 
    625         TopDiags.Files[Record[0]] = FE;
    626         continue;
    627       }
    628 
    629       case serialized_diags::RECORD_SOURCE_RANGE: {
    630         CXSourceRange SR;
    631         if (readRange(TopDiags, Record, 0, SR))
    632           return Failure;
    633         D->Ranges.push_back(SR);
    634         continue;
    635       }
    636 
    637       case serialized_diags::RECORD_FIXIT: {
    638         CXSourceRange SR;
    639         if (readRange(TopDiags, Record, 0, SR))
    640           return Failure;
    641         llvm::StringRef RetStr;
    642         if (readString(TopDiags, RetStr, "FIXIT", Record, BlobStart, BlobLen,
    643                        /* allowEmptyString */ true))
    644           return Failure;
    645         D->FixIts.push_back(std::make_pair(SR, createCXString(RetStr, false)));
    646         continue;
    647       }
    648 
    649       case serialized_diags::RECORD_DIAG: {
    650         D->severity = Record[0];
    651         unsigned offset = 1;
    652         if (readLocation(TopDiags, Record, offset, D->DiagLoc))
    653           return Failure;
    654         D->category = Record[offset++];
    655         unsigned diagFlag = Record[offset++];
    656         D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : "";
    657         D->CategoryText = D->category ? TopDiags.Categories[D->category] : "";
    658         D->Spelling = TopDiags.makeString(BlobStart, BlobLen);
    659         continue;
    660       }
    661     }
    662   }
    663 }
    664 
    665 extern "C" {
    666 CXDiagnosticSet clang_loadDiagnostics(const char *file,
    667                                       enum CXLoadDiag_Error *error,
    668                                       CXString *errorString) {
    669   DiagLoader L(error, errorString);
    670   return L.load(file);
    671 }
    672 } // end extern 'C'.
    673