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