| Chris Lattner | 2815bc9 | 2013-01-19 18:47:35 +0000 | [diff] [blame] | 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 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 13 |  | 
 | 14 | #include "CXLoadedDiagnostic.h" | 
 | 15 | #include "CXString.h" | 
 | 16 | #include "clang/Basic/Diagnostic.h" | 
 | 17 | #include "clang/Basic/FileManager.h" | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 18 | #include "clang/Basic/LLVM.h" | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 19 | #include "clang/Frontend/SerializedDiagnosticPrinter.h" | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 20 | #include "llvm/ADT/Optional.h" | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 21 | #include "llvm/ADT/StringRef.h" | 
 | 22 | #include "llvm/ADT/Twine.h" | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 23 | #include "llvm/Bitcode/BitstreamReader.h" | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 24 | #include "llvm/Support/ErrorHandling.h" | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 25 | #include "llvm/Support/MemoryBuffer.h" | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 26 | using namespace clang; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 27 |  | 
 | 28 | //===----------------------------------------------------------------------===// | 
 | 29 | // Extend CXDiagnosticSetImpl which contains strings for diagnostics. | 
 | 30 | //===----------------------------------------------------------------------===// | 
 | 31 |  | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 32 | typedef llvm::DenseMap<unsigned, const char *> Strings; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 33 |  | 
 | 34 | namespace { | 
 | 35 | class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { | 
 | 36 | public: | 
 | 37 |   CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {} | 
 | 38 |   virtual ~CXLoadedDiagnosticSetImpl() {}   | 
 | 39 |  | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 48 |  | 
 | 49 |   /// \brief Copy the string into our own allocator. | 
 | 50 |   const char *copyString(StringRef Blob) { | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 51 |     char *mem = Alloc.Allocate<char>(Blob.size() + 1); | 
 | 52 |     memcpy(mem, Blob.data(), Blob.size()); | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 53 |     mem[Blob.size()] = '\0'; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 54 |     return mem; | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 55 |   } | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 56 | }; | 
 | 57 | } | 
 | 58 |  | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 59 | //===----------------------------------------------------------------------===// | 
 | 60 | // Cleanup. | 
 | 61 | //===----------------------------------------------------------------------===// | 
 | 62 |  | 
 | 63 | CXLoadedDiagnostic::~CXLoadedDiagnostic() {} | 
 | 64 |  | 
 | 65 | //===----------------------------------------------------------------------===// | 
 | 66 | // Public CXLoadedDiagnostic methods. | 
 | 67 | //===----------------------------------------------------------------------===// | 
 | 68 |  | 
 | 69 | CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 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) | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 82 | #undef CASE | 
| Stephen Hines | 6bcf27b | 2014-05-29 04:14:42 -0700 | [diff] [blame] | 83 |   // The 'Remark' level isn't represented in the stable API. | 
 | 84 |   case serialized_diags::Remark: return CXDiagnostic_Warning; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 85 |   } | 
 | 86 |    | 
 | 87 |   llvm_unreachable("Invalid diagnostic level"); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 88 | } | 
 | 89 |  | 
 | 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; | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 95 |   CXSourceLocation Loc = { {  (void*) V, nullptr }, 0 }; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 96 |   return Loc; | 
 | 97 | }   | 
 | 98 |  | 
 | 99 | 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 |  | 
 | 105 | CXString CXLoadedDiagnostic::getSpelling() const { | 
| Dmitri Gribenko | 5595ded | 2013-02-02 02:19:29 +0000 | [diff] [blame] | 106 |   return cxstring::createRef(Spelling); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 107 | } | 
 | 108 |  | 
 | 109 | CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { | 
 | 110 |   if (DiagOption.empty()) | 
| Dmitri Gribenko | dc66adb | 2013-02-01 14:21:22 +0000 | [diff] [blame] | 111 |     return cxstring::createEmpty(); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 112 |  | 
 | 113 |   // FIXME: possibly refactor with logic in CXStoredDiagnostic. | 
 | 114 |   if (Disable) | 
| Dmitri Gribenko | 5595ded | 2013-02-02 02:19:29 +0000 | [diff] [blame] | 115 |     *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str()); | 
 | 116 |   return cxstring::createDup((Twine("-W") + DiagOption).str()); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 117 | } | 
 | 118 |  | 
 | 119 | unsigned CXLoadedDiagnostic::getCategory() const { | 
 | 120 |   return category; | 
 | 121 | } | 
 | 122 |  | 
| Ted Kremenek | 78d5d3b | 2012-04-12 00:03:31 +0000 | [diff] [blame] | 123 | CXString CXLoadedDiagnostic::getCategoryText() const { | 
| Dmitri Gribenko | 5595ded | 2013-02-02 02:19:29 +0000 | [diff] [blame] | 124 |   return cxstring::createDup(CategoryText); | 
| Ted Kremenek | 78d5d3b | 2012-04-12 00:03:31 +0000 | [diff] [blame] | 125 | } | 
 | 126 |  | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 127 | unsigned CXLoadedDiagnostic::getNumRanges() const { | 
 | 128 |   return Ranges.size(); | 
 | 129 | } | 
 | 130 |  | 
 | 131 | CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { | 
 | 132 |   assert(Range < Ranges.size()); | 
 | 133 |   return Ranges[Range]; | 
 | 134 | } | 
 | 135 |  | 
 | 136 | unsigned CXLoadedDiagnostic::getNumFixIts() const { | 
 | 137 |   return FixIts.size(); | 
 | 138 | } | 
 | 139 |  | 
 | 140 | CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt, | 
 | 141 |                                       CXSourceRange *ReplacementRange) const { | 
 | 142 |   assert(FixIt < FixIts.size()); | 
 | 143 |   if (ReplacementRange) | 
 | 144 |     *ReplacementRange = FixIts[FixIt].first; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 145 |   return cxstring::createRef(FixIts[FixIt].second); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 146 | } | 
 | 147 |  | 
 | 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 |  | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 186 | enum { MaxSupportedVersion = 2 }; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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 |    | 
 | 200 |   void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { | 
 | 201 |     if (error) | 
 | 202 |       *error = code; | 
 | 203 |     if (errorString) | 
| Dmitri Gribenko | 5595ded | 2013-02-02 02:19:29 +0000 | [diff] [blame] | 204 |       *errorString = cxstring::createDup(err); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 205 |   } | 
 | 206 |    | 
 | 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, | 
| Chris Lattner | e485f07 | 2013-01-19 21:35:35 +0000 | [diff] [blame] | 220 |                                        bool atTopLevel = false); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 221 |    | 
 | 222 |    | 
 | 223 |   LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, | 
 | 224 |                         Strings &strings, llvm::StringRef errorContext, | 
 | 225 |                         RecordData &Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 226 |                         StringRef Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 227 |                         bool allowEmptyString = false); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 228 |  | 
 | 229 |   LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 230 |                         const char *&RetStr, | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 231 |                         llvm::StringRef errorContext, | 
 | 232 |                         RecordData &Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 233 |                         StringRef Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 234 |                         bool allowEmptyString = false); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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: | 
 | 245 |   DiagLoader(enum CXLoadDiag_Error *e, CXString *es) | 
 | 246 |     : error(e), errorString(es) { | 
 | 247 |       if (error) | 
 | 248 |         *error = CXLoadDiag_None; | 
 | 249 |       if (errorString) | 
| Dmitri Gribenko | dc66adb | 2013-02-01 14:21:22 +0000 | [diff] [blame] | 250 |         *errorString = cxstring::createEmpty(); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 251 |     } | 
| Ted Kremenek | e97ac9e | 2011-11-11 15:19:48 +0000 | [diff] [blame] | 252 |  | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 253 |   CXDiagnosticSet load(const char *file); | 
 | 254 | }; | 
 | 255 | } | 
 | 256 |  | 
 | 257 | CXDiagnosticSet DiagLoader::load(const char *file) { | 
 | 258 |   // Open the diagnostics file. | 
 | 259 |   std::string ErrStr; | 
 | 260 |   FileSystemOptions FO; | 
 | 261 |   FileManager FileMgr(FO); | 
 | 262 |  | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 263 |   std::unique_ptr<llvm::MemoryBuffer> Buffer; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 264 |   Buffer.reset(FileMgr.getBufferForFile(file)); | 
 | 265 |  | 
 | 266 |   if (!Buffer) { | 
 | 267 |     reportBad(CXLoadDiag_CannotLoad, ErrStr); | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 268 |     return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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"); | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 285 |     return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 286 |   } | 
 | 287 |  | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 288 |   std::unique_ptr<CXLoadedDiagnosticSetImpl> Diags( | 
 | 289 |       new CXLoadedDiagnosticSetImpl()); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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: | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 297 |         return (CXDiagnosticSet)Diags.release(); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 298 |       case Read_Failure: | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 299 |         return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 300 |       case Read_Record: | 
 | 301 |         llvm_unreachable("Top-level does not have records"); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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)) | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 311 |           return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 312 |         break; | 
 | 313 |       case serialized_diags::BLOCK_DIAG: | 
 | 314 |         if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get())) | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 315 |           return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 316 |         break; | 
 | 317 |       default: | 
 | 318 |         if (!Stream.SkipBlock()) { | 
 | 319 |           reportInvalidFile("Malformed block at top-level of diagnostics file"); | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 320 |           return nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 321 |         } | 
 | 322 |         break; | 
 | 323 |     } | 
 | 324 |   } | 
 | 325 | } | 
 | 326 |  | 
 | 327 | StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, | 
 | 328 |                                                  llvm::StringRef errorContext, | 
 | 329 |                                                  unsigned &blockOrRecordID, | 
| Chris Lattner | e485f07 | 2013-01-19 21:35:35 +0000 | [diff] [blame] | 330 |                                                  bool atTopLevel) { | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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 |  | 
 | 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; | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 427 |     unsigned recordID = Stream.readRecord(blockOrCode, Record); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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) { | 
| Stefanus Du Toit | fc09336 | 2013-03-01 21:41:22 +0000 | [diff] [blame] | 435 |         reportInvalidFile("diagnostics file is a newer version than the one " | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 436 |                           "supported"); | 
 | 437 |         return Failure; | 
 | 438 |       } | 
 | 439 |       versionChecked = true; | 
 | 440 |     } | 
 | 441 |   } | 
 | 442 | } | 
 | 443 |  | 
 | 444 | LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 445 |                                   const char *&RetStr, | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 446 |                                   llvm::StringRef errorContext, | 
 | 447 |                                   RecordData &Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 448 |                                   StringRef Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 449 |                                   bool allowEmptyString) { | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 450 |    | 
 | 451 |   // Basic buffer overflow check. | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 452 |   if (Blob.size() > 65536) { | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 453 |     reportInvalidFile(std::string("Out-of-bounds string in ") + | 
 | 454 |                       std::string(errorContext)); | 
 | 455 |     return Failure; | 
 | 456 |   } | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 457 |  | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 458 |   if (allowEmptyString && Record.size() >= 1 && Blob.size() == 0) { | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 459 |     RetStr = ""; | 
 | 460 |     return Success; | 
 | 461 |   } | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 462 |    | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 463 |   if (Record.size() < 1 || Blob.size() == 0) { | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 464 |     reportInvalidFile(std::string("Corrupted ") + std::string(errorContext) | 
 | 465 |                       + std::string(" entry")); | 
 | 466 |     return Failure; | 
 | 467 |   } | 
 | 468 |    | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 469 |   RetStr = TopDiags.copyString(Blob); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 470 |   return Success; | 
 | 471 | } | 
 | 472 |  | 
 | 473 | LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, | 
 | 474 |                                   Strings &strings, | 
 | 475 |                                   llvm::StringRef errorContext, | 
 | 476 |                                   RecordData &Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 477 |                                   StringRef Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 478 |                                   bool allowEmptyString) { | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 479 |   const char *RetStr; | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 480 |   if (readString(TopDiags, RetStr, errorContext, Record, Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 481 |                  allowEmptyString)) | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 482 |     return Failure; | 
 | 483 |   strings[Record[0]] = RetStr; | 
 | 484 |   return Success; | 
 | 485 | } | 
 | 486 |  | 
 | 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. | 
| Stephen Hines | ef82254 | 2014-07-21 00:47:37 -0700 | [diff] [blame^] | 498 |     Loc.file = nullptr; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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 |   } | 
| David Greene | 527ce9f | 2013-01-15 22:09:48 +0000 | [diff] [blame] | 510 |   Loc.file = const_cast<FileEntry *>(FE); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 511 |   Loc.line = Record[offset++]; | 
 | 512 |   Loc.column = Record[offset++]; | 
 | 513 |   Loc.offset = Record[offset++]; | 
 | 514 |   return Success; | 
 | 515 | } | 
 | 516 |  | 
 | 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 |  | 
 | 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 |   } | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 544 |  | 
 | 545 |   std::unique_ptr<CXLoadedDiagnostic> D(new CXLoadedDiagnostic()); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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"); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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: | 
| Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 572 |         Diags.appendDiagnostic(D.release()); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 573 |         return Success; | 
 | 574 |       case Read_Record: | 
 | 575 |         break; | 
 | 576 |     } | 
 | 577 |      | 
 | 578 |     // Read the record. | 
 | 579 |     Record.clear(); | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 580 |     StringRef Blob; | 
 | 581 |     unsigned recID = Stream.readRecord(blockOrCode, Record, &Blob); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 592 |                        Blob, /* allowEmptyString */ true)) | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 593 |           return Failure; | 
 | 594 |         continue; | 
 | 595 |        | 
 | 596 |       case serialized_diags::RECORD_DIAG_FLAG: | 
 | 597 |         if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 598 |                        Blob)) | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 599 |           return Failure; | 
 | 600 |         continue; | 
 | 601 |          | 
 | 602 |       case serialized_diags::RECORD_FILENAME: { | 
 | 603 |         if (readString(TopDiags, TopDiags.FileNames, "filename", Record, | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 604 |                        Blob)) | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 633 |         const char *RetStr; | 
| Chris Lattner | b3ce357 | 2013-01-20 02:38:54 +0000 | [diff] [blame] | 634 |         if (readString(TopDiags, RetStr, "FIXIT", Record, Blob, | 
| Ted Kremenek | 952538d | 2011-11-29 00:30:52 +0000 | [diff] [blame] | 635 |                        /* allowEmptyString */ true)) | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 636 |           return Failure; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 637 |         D->FixIts.push_back(std::make_pair(SR, RetStr)); | 
| Ted Kremenek | e97ac9e | 2011-11-11 15:19:48 +0000 | [diff] [blame] | 638 |         continue; | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 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] : ""; | 
| Ted Kremenek | 78d5d3b | 2012-04-12 00:03:31 +0000 | [diff] [blame] | 649 |         D->CategoryText = D->category ? TopDiags.Categories[D->category] : ""; | 
| Dmitri Gribenko | 9ba7627 | 2013-02-18 19:50:38 +0000 | [diff] [blame] | 650 |         D->Spelling = TopDiags.copyString(Blob); | 
| Ted Kremenek | 1532217 | 2011-11-10 08:43:12 +0000 | [diff] [blame] | 651 |         continue; | 
 | 652 |       } | 
 | 653 |     } | 
 | 654 |   } | 
 | 655 | } | 
 | 656 |  | 
 | 657 | extern "C" { | 
 | 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'. |