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