]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/libclang/CXLoadedDiagnostic.cpp
Vendor import of clang release_32 branch r168974 (effectively, 3.2 RC2):
[FreeBSD/FreeBSD.git] / tools / libclang / CXLoadedDiagnostic.cpp
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'.