forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			656 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			656 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- CXLoadedDiagnostic.cpp - Handling of persisent diags ----*- C++ -*-===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // Implements handling of persisent diagnostics.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "CXLoadedDiagnostic.h"
 | |
| #include "CXString.h"
 | |
| #include "clang/Basic/Diagnostic.h"
 | |
| #include "clang/Basic/FileManager.h"
 | |
| #include "clang/Frontend/SerializedDiagnosticPrinter.h"
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| #include "llvm/ADT/Twine.h"
 | |
| #include "llvm/ADT/Optional.h"
 | |
| #include "clang/Basic/LLVM.h"
 | |
| #include "llvm/Support/ErrorHandling.h"
 | |
| #include "llvm/Bitcode/BitstreamReader.h"
 | |
| #include "llvm/Support/MemoryBuffer.h"
 | |
| using namespace clang;
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Extend CXDiagnosticSetImpl which contains strings for diagnostics.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| typedef llvm::DenseMap<unsigned, const char *> Strings;
 | |
| 
 | |
| namespace {
 | |
| class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl {
 | |
| public:
 | |
|   CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {}
 | |
|   virtual ~CXLoadedDiagnosticSetImpl() {}  
 | |
| 
 | |
|   llvm::BumpPtrAllocator Alloc;
 | |
|   Strings Categories;
 | |
|   Strings WarningFlags;
 | |
|   Strings FileNames;
 | |
|   
 | |
|   FileSystemOptions FO;
 | |
|   FileManager FakeFiles;
 | |
|   llvm::DenseMap<unsigned, const FileEntry *> Files;
 | |
| 
 | |
|   /// \brief Copy the string into our own allocator.
 | |
|   const char *copyString(StringRef Blob) {
 | |
|     char *mem = Alloc.Allocate<char>(Blob.size() + 1);
 | |
|     memcpy(mem, Blob.data(), Blob.size());
 | |
|     mem[Blob.size()] = '\0';
 | |
|     return mem;
 | |
|   }
 | |
| };
 | |
| }
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Cleanup.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| CXLoadedDiagnostic::~CXLoadedDiagnostic() {}
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Public CXLoadedDiagnostic methods.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const {
 | |
|   // FIXME: possibly refactor with logic in CXStoredDiagnostic.
 | |
|   switch (severity) {
 | |
|     case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored;
 | |
|     case DiagnosticsEngine::Note:    return CXDiagnostic_Note;
 | |
|     case DiagnosticsEngine::Warning: return CXDiagnostic_Warning;
 | |
|     case DiagnosticsEngine::Error:   return CXDiagnostic_Error;
 | |
|     case DiagnosticsEngine::Fatal:   return CXDiagnostic_Fatal;
 | |
|   }
 | |
|   
 | |
|   llvm_unreachable("Invalid diagnostic level");
 | |
| }
 | |
| 
 | |
| static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) {
 | |
|   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
 | |
|   // is a persistent diagnostic.
 | |
|   uintptr_t V = (uintptr_t) DLoc;
 | |
|   V |= 0x1;
 | |
|   CXSourceLocation Loc = { {  (void*) V, 0 }, 0 };
 | |
|   return Loc;
 | |
| }  
 | |
| 
 | |
| CXSourceLocation CXLoadedDiagnostic::getLocation() const {
 | |
|   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
 | |
|   // is a persistent diagnostic.
 | |
|   return makeLocation(&DiagLoc);
 | |
| }
 | |
| 
 | |
| CXString CXLoadedDiagnostic::getSpelling() const {
 | |
|   return cxstring::createRef(Spelling);
 | |
| }
 | |
| 
 | |
| CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const {
 | |
|   if (DiagOption.empty())
 | |
|     return cxstring::createEmpty();
 | |
| 
 | |
|   // FIXME: possibly refactor with logic in CXStoredDiagnostic.
 | |
|   if (Disable)
 | |
|     *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str());
 | |
|   return cxstring::createDup((Twine("-W") + DiagOption).str());
 | |
| }
 | |
| 
 | |
| unsigned CXLoadedDiagnostic::getCategory() const {
 | |
|   return category;
 | |
| }
 | |
| 
 | |
| CXString CXLoadedDiagnostic::getCategoryText() const {
 | |
|   return cxstring::createDup(CategoryText);
 | |
| }
 | |
| 
 | |
| unsigned CXLoadedDiagnostic::getNumRanges() const {
 | |
|   return Ranges.size();
 | |
| }
 | |
| 
 | |
| CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const {
 | |
|   assert(Range < Ranges.size());
 | |
|   return Ranges[Range];
 | |
| }
 | |
| 
 | |
| unsigned CXLoadedDiagnostic::getNumFixIts() const {
 | |
|   return FixIts.size();
 | |
| }
 | |
| 
 | |
| CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt,
 | |
|                                       CXSourceRange *ReplacementRange) const {
 | |
|   assert(FixIt < FixIts.size());
 | |
|   if (ReplacementRange)
 | |
|     *ReplacementRange = FixIts[FixIt].first;
 | |
|   return cxstring::createRef(FixIts[FixIt].second);
 | |
| }
 | |
| 
 | |
| void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location,
 | |
|                                         CXFile *file,
 | |
|                                         unsigned int *line,
 | |
|                                         unsigned int *column,
 | |
|                                         unsigned int *offset) {
 | |
|   
 | |
|   
 | |
|   // CXSourceLocation consists of the following fields:
 | |
|   //
 | |
|   //   void *ptr_data[2];
 | |
|   //   unsigned int_data;
 | |
|   //
 | |
|   // The lowest bit of ptr_data[0] is always set to 1 to indicate this
 | |
|   // is a persistent diagnostic.
 | |
|   //
 | |
|   // For now, do the unoptimized approach and store the data in a side
 | |
|   // data structure.  We can optimize this case later.
 | |
|   
 | |
|   uintptr_t V = (uintptr_t) location.ptr_data[0];
 | |
|   assert((V & 0x1) == 1);
 | |
|   V &= ~(uintptr_t)1;
 | |
|   
 | |
|   const Location &Loc = *((Location*)V);
 | |
|   
 | |
|   if (file)
 | |
|     *file = Loc.file;  
 | |
|   if (line)
 | |
|     *line = Loc.line;
 | |
|   if (column)
 | |
|     *column = Loc.column;
 | |
|   if (offset)
 | |
|     *offset = Loc.offset;
 | |
| }
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Deserialize diagnostics.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| enum { MaxSupportedVersion = 1 };
 | |
| 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 {
 | |
|   enum CXLoadDiag_Error *error;
 | |
|   CXString *errorString;
 | |
|   
 | |
|   void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) {
 | |
|     if (error)
 | |
|       *error = code;
 | |
|     if (errorString)
 | |
|       *errorString = cxstring::createDup(err);
 | |
|   }
 | |
|   
 | |
|   void 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);
 | |
|                        
 | |
| public:
 | |
|   DiagLoader(enum CXLoadDiag_Error *e, CXString *es)
 | |
|     : 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);
 | |
| 
 | |
|   OwningPtr<llvm::MemoryBuffer> Buffer;
 | |
|   Buffer.reset(FileMgr.getBufferForFile(file));
 | |
| 
 | |
|   if (!Buffer) {
 | |
|     reportBad(CXLoadDiag_CannotLoad, ErrStr);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   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 0;
 | |
|   }
 | |
| 
 | |
|   OwningPtr<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.take();
 | |
|       case Read_Failure:
 | |
|         return 0;
 | |
|       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 0;
 | |
|         break;
 | |
|       case serialized_diags::BLOCK_DIAG:
 | |
|         if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get()))
 | |
|           return 0;
 | |
|         break;
 | |
|       default:
 | |
|         if (!Stream.SkipBlock()) {
 | |
|           reportInvalidFile("Malformed block at top-level of diagnostics file");
 | |
|           return 0;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags,
 | |
|                                   const char *&RetStr,
 | |
|                                   llvm::StringRef errorContext,
 | |
|                                   RecordData &Record,
 | |
|                                   StringRef Blob,
 | |
|                                   bool allowEmptyString) {
 | |
|   
 | |
|   // Basic buffer overflow check.
 | |
|   if (Blob.size() > 65536) {
 | |
|     reportInvalidFile(std::string("Out-of-bounds string in ") +
 | |
|                       std::string(errorContext));
 | |
|     return Failure;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags,
 | |
|                                     RecordData &Record, unsigned &offset,
 | |
|                                     CXLoadedDiagnostic::Location &Loc) {
 | |
|   if (Record.size() < offset + 3) {
 | |
|     reportInvalidFile("Corrupted source location");
 | |
|     return Failure;
 | |
|   }
 | |
|   
 | |
|   unsigned fileID = Record[offset++];
 | |
|   if (fileID == 0) {
 | |
|     // Sentinel value.
 | |
|     Loc.file = 0;
 | |
|     Loc.line = 0;
 | |
|     Loc.column = 0;
 | |
|     Loc.offset = 0;
 | |
|     return Success;
 | |
|   }
 | |
| 
 | |
|   const FileEntry *FE = TopDiags.Files[fileID];
 | |
|   if (!FE) {
 | |
|     reportInvalidFile("Corrupted file entry in source location");
 | |
|     return Failure;
 | |
|   }
 | |
|   Loc.file = const_cast<FileEntry *>(FE);
 | |
|   Loc.line = Record[offset++];
 | |
|   Loc.column = Record[offset++];
 | |
|   Loc.offset = Record[offset++];
 | |
|   return Success;
 | |
| }
 | |
| 
 | |
| 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;  
 | |
| }
 | |
| 
 | |
| LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream,
 | |
|                                            CXDiagnosticSetImpl &Diags,
 | |
|                                            CXLoadedDiagnosticSetImpl &TopDiags){
 | |
| 
 | |
|   if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
 | |
|     reportInvalidFile("malformed diagnostic block");
 | |
|     return Failure;
 | |
|   }
 | |
|   
 | |
|   OwningPtr<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(D.take());        
 | |
|         return Success;
 | |
|       case Read_Record:
 | |
|         break;
 | |
|     }
 | |
|     
 | |
|     // Read the record.
 | |
|     Record.clear();
 | |
|     StringRef Blob;
 | |
|     unsigned recID = Stream.readRecord(blockOrCode, Record, &Blob);
 | |
|     
 | |
|     if (recID < serialized_diags::RECORD_FIRST ||
 | |
|         recID > serialized_diags::RECORD_LAST)
 | |
|       continue;
 | |
|     
 | |
|     switch ((serialized_diags::RecordIDs)recID) {  
 | |
|       case serialized_diags::RECORD_VERSION:
 | |
|         continue;
 | |
|       case serialized_diags::RECORD_CATEGORY:
 | |
|         if (readString(TopDiags, TopDiags.Categories, "category", Record,
 | |
|                        Blob, /* allowEmptyString */ true))
 | |
|           return Failure;
 | |
|         continue;
 | |
|       
 | |
|       case serialized_diags::RECORD_DIAG_FLAG:
 | |
|         if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record,
 | |
|                        Blob))
 | |
|           return Failure;
 | |
|         continue;
 | |
|         
 | |
|       case serialized_diags::RECORD_FILENAME: {
 | |
|         if (readString(TopDiags, TopDiags.FileNames, "filename", Record,
 | |
|                        Blob))
 | |
|           return Failure;
 | |
| 
 | |
|         if (Record.size() < 3) {
 | |
|           reportInvalidFile("Invalid file entry");
 | |
|           return Failure;
 | |
|         }
 | |
|         
 | |
|         const FileEntry *FE =
 | |
|           TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]],
 | |
|                                             /* size */ Record[1],
 | |
|                                             /* time */ Record[2]);
 | |
|         
 | |
|         TopDiags.Files[Record[0]] = FE;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       case serialized_diags::RECORD_SOURCE_RANGE: {
 | |
|         CXSourceRange SR;
 | |
|         if (readRange(TopDiags, Record, 0, SR))
 | |
|           return Failure;
 | |
|         D->Ranges.push_back(SR);
 | |
|         continue;
 | |
|       }
 | |
|       
 | |
|       case serialized_diags::RECORD_FIXIT: {
 | |
|         CXSourceRange SR;
 | |
|         if (readRange(TopDiags, Record, 0, SR))
 | |
|           return Failure;
 | |
|         const char *RetStr;
 | |
|         if (readString(TopDiags, RetStr, "FIXIT", Record, Blob,
 | |
|                        /* allowEmptyString */ true))
 | |
|           return Failure;
 | |
|         D->FixIts.push_back(std::make_pair(SR, RetStr));
 | |
|         continue;
 | |
|       }
 | |
|         
 | |
|       case serialized_diags::RECORD_DIAG: {
 | |
|         D->severity = Record[0];
 | |
|         unsigned offset = 1;
 | |
|         if (readLocation(TopDiags, Record, offset, D->DiagLoc))
 | |
|           return Failure;
 | |
|         D->category = Record[offset++];
 | |
|         unsigned diagFlag = Record[offset++];
 | |
|         D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : "";
 | |
|         D->CategoryText = D->category ? TopDiags.Categories[D->category] : "";
 | |
|         D->Spelling = TopDiags.copyString(Blob);
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| extern "C" {
 | |
| CXDiagnosticSet clang_loadDiagnostics(const char *file,
 | |
|                                       enum CXLoadDiag_Error *error,
 | |
|                                       CXString *errorString) {
 | |
|   DiagLoader L(error, errorString);
 | |
|   return L.load(file);
 | |
| }
 | |
| } // end extern 'C'.
 |