SerializedDiagnosticPrinter.cpp revision 314564
1//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// 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#include "clang/Frontend/SerializedDiagnosticPrinter.h" 11#include "clang/Basic/Diagnostic.h" 12#include "clang/Basic/DiagnosticOptions.h" 13#include "clang/Basic/SourceManager.h" 14#include "clang/Frontend/DiagnosticRenderer.h" 15#include "clang/Frontend/FrontendDiagnostic.h" 16#include "clang/Frontend/SerializedDiagnosticReader.h" 17#include "clang/Frontend/SerializedDiagnostics.h" 18#include "clang/Frontend/TextDiagnosticPrinter.h" 19#include "clang/Lex/Lexer.h" 20#include "llvm/ADT/DenseSet.h" 21#include "llvm/ADT/STLExtras.h" 22#include "llvm/ADT/SmallString.h" 23#include "llvm/ADT/StringRef.h" 24#include "llvm/Support/raw_ostream.h" 25#include <utility> 26 27using namespace clang; 28using namespace clang::serialized_diags; 29 30namespace { 31 32class AbbreviationMap { 33 llvm::DenseMap<unsigned, unsigned> Abbrevs; 34public: 35 AbbreviationMap() {} 36 37 void set(unsigned recordID, unsigned abbrevID) { 38 assert(Abbrevs.find(recordID) == Abbrevs.end() 39 && "Abbreviation already set."); 40 Abbrevs[recordID] = abbrevID; 41 } 42 43 unsigned get(unsigned recordID) { 44 assert(Abbrevs.find(recordID) != Abbrevs.end() && 45 "Abbreviation not set."); 46 return Abbrevs[recordID]; 47 } 48}; 49 50typedef SmallVector<uint64_t, 64> RecordData; 51typedef SmallVectorImpl<uint64_t> RecordDataImpl; 52typedef ArrayRef<uint64_t> RecordDataRef; 53 54class SDiagsWriter; 55 56class SDiagsRenderer : public DiagnosticNoteRenderer { 57 SDiagsWriter &Writer; 58public: 59 SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts, 60 DiagnosticOptions *DiagOpts) 61 : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {} 62 63 ~SDiagsRenderer() override {} 64 65protected: 66 void emitDiagnosticMessage(SourceLocation Loc, 67 PresumedLoc PLoc, 68 DiagnosticsEngine::Level Level, 69 StringRef Message, 70 ArrayRef<CharSourceRange> Ranges, 71 const SourceManager *SM, 72 DiagOrStoredDiag D) override; 73 74 void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, 75 DiagnosticsEngine::Level Level, 76 ArrayRef<CharSourceRange> Ranges, 77 const SourceManager &SM) override {} 78 79 void emitNote(SourceLocation Loc, StringRef Message, 80 const SourceManager *SM) override; 81 82 void emitCodeContext(SourceLocation Loc, 83 DiagnosticsEngine::Level Level, 84 SmallVectorImpl<CharSourceRange>& Ranges, 85 ArrayRef<FixItHint> Hints, 86 const SourceManager &SM) override; 87 88 void beginDiagnostic(DiagOrStoredDiag D, 89 DiagnosticsEngine::Level Level) override; 90 void endDiagnostic(DiagOrStoredDiag D, 91 DiagnosticsEngine::Level Level) override; 92}; 93 94typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup; 95 96class SDiagsMerger : SerializedDiagnosticReader { 97 SDiagsWriter &Writer; 98 AbbrevLookup FileLookup; 99 AbbrevLookup CategoryLookup; 100 AbbrevLookup DiagFlagLookup; 101 102public: 103 SDiagsMerger(SDiagsWriter &Writer) 104 : SerializedDiagnosticReader(), Writer(Writer) {} 105 106 std::error_code mergeRecordsFromFile(const char *File) { 107 return readDiagnostics(File); 108 } 109 110protected: 111 std::error_code visitStartOfDiagnostic() override; 112 std::error_code visitEndOfDiagnostic() override; 113 std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; 114 std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; 115 std::error_code visitDiagnosticRecord( 116 unsigned Severity, const serialized_diags::Location &Location, 117 unsigned Category, unsigned Flag, StringRef Message) override; 118 std::error_code visitFilenameRecord(unsigned ID, unsigned Size, 119 unsigned Timestamp, 120 StringRef Name) override; 121 std::error_code visitFixitRecord(const serialized_diags::Location &Start, 122 const serialized_diags::Location &End, 123 StringRef CodeToInsert) override; 124 std::error_code 125 visitSourceRangeRecord(const serialized_diags::Location &Start, 126 const serialized_diags::Location &End) override; 127 128private: 129 std::error_code adjustSourceLocFilename(RecordData &Record, 130 unsigned int offset); 131 132 void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup, 133 unsigned NewAbbrev); 134 135 void writeRecordWithAbbrev(unsigned ID, RecordData &Record); 136 137 void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob); 138}; 139 140class SDiagsWriter : public DiagnosticConsumer { 141 friend class SDiagsRenderer; 142 friend class SDiagsMerger; 143 144 struct SharedState; 145 146 explicit SDiagsWriter(std::shared_ptr<SharedState> State) 147 : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false), 148 State(std::move(State)) {} 149 150public: 151 SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) 152 : LangOpts(nullptr), OriginalInstance(true), 153 MergeChildRecords(MergeChildRecords), 154 State(std::make_shared<SharedState>(File, Diags)) { 155 if (MergeChildRecords) 156 RemoveOldDiagnostics(); 157 EmitPreamble(); 158 } 159 160 ~SDiagsWriter() override {} 161 162 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 163 const Diagnostic &Info) override; 164 165 void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override { 166 LangOpts = &LO; 167 } 168 169 void finish() override; 170 171private: 172 /// \brief Build a DiagnosticsEngine to emit diagnostics about the diagnostics 173 DiagnosticsEngine *getMetaDiags(); 174 175 /// \brief Remove old copies of the serialized diagnostics. This is necessary 176 /// so that we can detect when subprocesses write diagnostics that we should 177 /// merge into our own. 178 void RemoveOldDiagnostics(); 179 180 /// \brief Emit the preamble for the serialized diagnostics. 181 void EmitPreamble(); 182 183 /// \brief Emit the BLOCKINFO block. 184 void EmitBlockInfoBlock(); 185 186 /// \brief Emit the META data block. 187 void EmitMetaBlock(); 188 189 /// \brief Start a DIAG block. 190 void EnterDiagBlock(); 191 192 /// \brief End a DIAG block. 193 void ExitDiagBlock(); 194 195 /// \brief Emit a DIAG record. 196 void EmitDiagnosticMessage(SourceLocation Loc, 197 PresumedLoc PLoc, 198 DiagnosticsEngine::Level Level, 199 StringRef Message, 200 const SourceManager *SM, 201 DiagOrStoredDiag D); 202 203 /// \brief Emit FIXIT and SOURCE_RANGE records for a diagnostic. 204 void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, 205 ArrayRef<FixItHint> Hints, 206 const SourceManager &SM); 207 208 /// \brief Emit a record for a CharSourceRange. 209 void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); 210 211 /// \brief Emit the string information for the category. 212 unsigned getEmitCategory(unsigned category = 0); 213 214 /// \brief Emit the string information for diagnostic flags. 215 unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 216 unsigned DiagID = 0); 217 218 unsigned getEmitDiagnosticFlag(StringRef DiagName); 219 220 /// \brief Emit (lazily) the file string and retrieved the file identifier. 221 unsigned getEmitFile(const char *Filename); 222 223 /// \brief Add SourceLocation information the specified record. 224 void AddLocToRecord(SourceLocation Loc, const SourceManager *SM, 225 PresumedLoc PLoc, RecordDataImpl &Record, 226 unsigned TokSize = 0); 227 228 /// \brief Add SourceLocation information the specified record. 229 void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, 230 const SourceManager *SM, 231 unsigned TokSize = 0) { 232 AddLocToRecord(Loc, SM, SM ? SM->getPresumedLoc(Loc) : PresumedLoc(), 233 Record, TokSize); 234 } 235 236 /// \brief Add CharSourceRange information the specified record. 237 void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, 238 const SourceManager &SM); 239 240 /// \brief Language options, which can differ from one clone of this client 241 /// to another. 242 const LangOptions *LangOpts; 243 244 /// \brief Whether this is the original instance (rather than one of its 245 /// clones), responsible for writing the file at the end. 246 bool OriginalInstance; 247 248 /// \brief Whether this instance should aggregate diagnostics that are 249 /// generated from child processes. 250 bool MergeChildRecords; 251 252 /// \brief State that is shared among the various clones of this diagnostic 253 /// consumer. 254 struct SharedState { 255 SharedState(StringRef File, DiagnosticOptions *Diags) 256 : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), 257 EmittedAnyDiagBlocks(false) {} 258 259 /// \brief Diagnostic options. 260 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 261 262 /// \brief The byte buffer for the serialized content. 263 SmallString<1024> Buffer; 264 265 /// \brief The BitStreamWriter for the serialized diagnostics. 266 llvm::BitstreamWriter Stream; 267 268 /// \brief The name of the diagnostics file. 269 std::string OutputFile; 270 271 /// \brief The set of constructed record abbreviations. 272 AbbreviationMap Abbrevs; 273 274 /// \brief A utility buffer for constructing record content. 275 RecordData Record; 276 277 /// \brief A text buffer for rendering diagnostic text. 278 SmallString<256> diagBuf; 279 280 /// \brief The collection of diagnostic categories used. 281 llvm::DenseSet<unsigned> Categories; 282 283 /// \brief The collection of files used. 284 llvm::DenseMap<const char *, unsigned> Files; 285 286 typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> > 287 DiagFlagsTy; 288 289 /// \brief Map for uniquing strings. 290 DiagFlagsTy DiagFlags; 291 292 /// \brief Whether we have already started emission of any DIAG blocks. Once 293 /// this becomes \c true, we never close a DIAG block until we know that we're 294 /// starting another one or we're done. 295 bool EmittedAnyDiagBlocks; 296 297 /// \brief Engine for emitting diagnostics about the diagnostics. 298 std::unique_ptr<DiagnosticsEngine> MetaDiagnostics; 299 }; 300 301 /// \brief State shared among the various clones of this diagnostic consumer. 302 std::shared_ptr<SharedState> State; 303}; 304} // end anonymous namespace 305 306namespace clang { 307namespace serialized_diags { 308std::unique_ptr<DiagnosticConsumer> 309create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { 310 return llvm::make_unique<SDiagsWriter>(OutputFile, Diags, MergeChildRecords); 311} 312 313} // end namespace serialized_diags 314} // end namespace clang 315 316//===----------------------------------------------------------------------===// 317// Serialization methods. 318//===----------------------------------------------------------------------===// 319 320/// \brief Emits a block ID in the BLOCKINFO block. 321static void EmitBlockID(unsigned ID, const char *Name, 322 llvm::BitstreamWriter &Stream, 323 RecordDataImpl &Record) { 324 Record.clear(); 325 Record.push_back(ID); 326 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); 327 328 // Emit the block name if present. 329 if (!Name || Name[0] == 0) 330 return; 331 332 Record.clear(); 333 334 while (*Name) 335 Record.push_back(*Name++); 336 337 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); 338} 339 340/// \brief Emits a record ID in the BLOCKINFO block. 341static void EmitRecordID(unsigned ID, const char *Name, 342 llvm::BitstreamWriter &Stream, 343 RecordDataImpl &Record){ 344 Record.clear(); 345 Record.push_back(ID); 346 347 while (*Name) 348 Record.push_back(*Name++); 349 350 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); 351} 352 353void SDiagsWriter::AddLocToRecord(SourceLocation Loc, 354 const SourceManager *SM, 355 PresumedLoc PLoc, 356 RecordDataImpl &Record, 357 unsigned TokSize) { 358 if (PLoc.isInvalid()) { 359 // Emit a "sentinel" location. 360 Record.push_back((unsigned)0); // File. 361 Record.push_back((unsigned)0); // Line. 362 Record.push_back((unsigned)0); // Column. 363 Record.push_back((unsigned)0); // Offset. 364 return; 365 } 366 367 Record.push_back(getEmitFile(PLoc.getFilename())); 368 Record.push_back(PLoc.getLine()); 369 Record.push_back(PLoc.getColumn()+TokSize); 370 Record.push_back(SM->getFileOffset(Loc)); 371} 372 373void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, 374 RecordDataImpl &Record, 375 const SourceManager &SM) { 376 AddLocToRecord(Range.getBegin(), Record, &SM); 377 unsigned TokSize = 0; 378 if (Range.isTokenRange()) 379 TokSize = Lexer::MeasureTokenLength(Range.getEnd(), 380 SM, *LangOpts); 381 382 AddLocToRecord(Range.getEnd(), Record, &SM, TokSize); 383} 384 385unsigned SDiagsWriter::getEmitFile(const char *FileName){ 386 if (!FileName) 387 return 0; 388 389 unsigned &entry = State->Files[FileName]; 390 if (entry) 391 return entry; 392 393 // Lazily generate the record for the file. 394 entry = State->Files.size(); 395 StringRef Name(FileName); 396 RecordData::value_type Record[] = {RECORD_FILENAME, entry, 0 /* For legacy */, 397 0 /* For legacy */, Name.size()}; 398 State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record, 399 Name); 400 401 return entry; 402} 403 404void SDiagsWriter::EmitCharSourceRange(CharSourceRange R, 405 const SourceManager &SM) { 406 State->Record.clear(); 407 State->Record.push_back(RECORD_SOURCE_RANGE); 408 AddCharSourceRangeToRecord(R, State->Record, SM); 409 State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE), 410 State->Record); 411} 412 413/// \brief Emits the preamble of the diagnostics file. 414void SDiagsWriter::EmitPreamble() { 415 // Emit the file header. 416 State->Stream.Emit((unsigned)'D', 8); 417 State->Stream.Emit((unsigned)'I', 8); 418 State->Stream.Emit((unsigned)'A', 8); 419 State->Stream.Emit((unsigned)'G', 8); 420 421 EmitBlockInfoBlock(); 422 EmitMetaBlock(); 423} 424 425static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) { 426 using namespace llvm; 427 Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. 428 Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. 429 Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. 430 Abbrev.Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; 431} 432 433static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev &Abbrev) { 434 AddSourceLocationAbbrev(Abbrev); 435 AddSourceLocationAbbrev(Abbrev); 436} 437 438void SDiagsWriter::EmitBlockInfoBlock() { 439 State->Stream.EnterBlockInfoBlock(); 440 441 using namespace llvm; 442 llvm::BitstreamWriter &Stream = State->Stream; 443 RecordData &Record = State->Record; 444 AbbreviationMap &Abbrevs = State->Abbrevs; 445 446 // ==---------------------------------------------------------------------==// 447 // The subsequent records and Abbrevs are for the "Meta" block. 448 // ==---------------------------------------------------------------------==// 449 450 EmitBlockID(BLOCK_META, "Meta", Stream, Record); 451 EmitRecordID(RECORD_VERSION, "Version", Stream, Record); 452 auto Abbrev = std::make_shared<BitCodeAbbrev>(); 453 Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION)); 454 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); 455 Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev)); 456 457 // ==---------------------------------------------------------------------==// 458 // The subsequent records and Abbrevs are for the "Diagnostic" block. 459 // ==---------------------------------------------------------------------==// 460 461 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); 462 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); 463 EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); 464 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); 465 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); 466 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); 467 EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); 468 469 // Emit abbreviation for RECORD_DIAG. 470 Abbrev = std::make_shared<BitCodeAbbrev>(); 471 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); 472 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. 473 AddSourceLocationAbbrev(*Abbrev); 474 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. 475 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 476 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Text size. 477 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. 478 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 479 480 // Emit abbrevation for RECORD_CATEGORY. 481 Abbrev = std::make_shared<BitCodeAbbrev>(); 482 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 483 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. 484 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. 485 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. 486 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 487 488 // Emit abbrevation for RECORD_SOURCE_RANGE. 489 Abbrev = std::make_shared<BitCodeAbbrev>(); 490 Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); 491 AddRangeLocationAbbrev(*Abbrev); 492 Abbrevs.set(RECORD_SOURCE_RANGE, 493 Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 494 495 // Emit the abbreviation for RECORD_DIAG_FLAG. 496 Abbrev = std::make_shared<BitCodeAbbrev>(); 497 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); 498 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 499 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 500 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. 501 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 502 Abbrev)); 503 504 // Emit the abbreviation for RECORD_FILENAME. 505 Abbrev = std::make_shared<BitCodeAbbrev>(); 506 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); 507 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. 508 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. 509 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time. 510 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 511 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. 512 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 513 Abbrev)); 514 515 // Emit the abbreviation for RECORD_FIXIT. 516 Abbrev = std::make_shared<BitCodeAbbrev>(); 517 Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); 518 AddRangeLocationAbbrev(*Abbrev); 519 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 520 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. 521 Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 522 Abbrev)); 523 524 Stream.ExitBlock(); 525} 526 527void SDiagsWriter::EmitMetaBlock() { 528 llvm::BitstreamWriter &Stream = State->Stream; 529 AbbreviationMap &Abbrevs = State->Abbrevs; 530 531 Stream.EnterSubblock(BLOCK_META, 3); 532 RecordData::value_type Record[] = {RECORD_VERSION, VersionNumber}; 533 Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); 534 Stream.ExitBlock(); 535} 536 537unsigned SDiagsWriter::getEmitCategory(unsigned int category) { 538 if (!State->Categories.insert(category).second) 539 return category; 540 541 // We use a local version of 'Record' so that we can be generating 542 // another record when we lazily generate one for the category entry. 543 StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); 544 RecordData::value_type Record[] = {RECORD_CATEGORY, category, catName.size()}; 545 State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record, 546 catName); 547 548 return category; 549} 550 551unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 552 unsigned DiagID) { 553 if (DiagLevel == DiagnosticsEngine::Note) 554 return 0; // No flag for notes. 555 556 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); 557 return getEmitDiagnosticFlag(FlagName); 558} 559 560unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) { 561 if (FlagName.empty()) 562 return 0; 563 564 // Here we assume that FlagName points to static data whose pointer 565 // value is fixed. This allows us to unique by diagnostic groups. 566 const void *data = FlagName.data(); 567 std::pair<unsigned, StringRef> &entry = State->DiagFlags[data]; 568 if (entry.first == 0) { 569 entry.first = State->DiagFlags.size(); 570 entry.second = FlagName; 571 572 // Lazily emit the string in a separate record. 573 RecordData::value_type Record[] = {RECORD_DIAG_FLAG, entry.first, 574 FlagName.size()}; 575 State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), 576 Record, FlagName); 577 } 578 579 return entry.first; 580} 581 582void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 583 const Diagnostic &Info) { 584 // Enter the block for a non-note diagnostic immediately, rather than waiting 585 // for beginDiagnostic, in case associated notes are emitted before we get 586 // there. 587 if (DiagLevel != DiagnosticsEngine::Note) { 588 if (State->EmittedAnyDiagBlocks) 589 ExitDiagBlock(); 590 591 EnterDiagBlock(); 592 State->EmittedAnyDiagBlocks = true; 593 } 594 595 // Compute the diagnostic text. 596 State->diagBuf.clear(); 597 Info.FormatDiagnostic(State->diagBuf); 598 599 if (Info.getLocation().isInvalid()) { 600 // Special-case diagnostics with no location. We may not have entered a 601 // source file in this case, so we can't use the normal DiagnosticsRenderer 602 // machinery. 603 604 // Make sure we bracket all notes as "sub-diagnostics". This matches 605 // the behavior in SDiagsRenderer::emitDiagnostic(). 606 if (DiagLevel == DiagnosticsEngine::Note) 607 EnterDiagBlock(); 608 609 EmitDiagnosticMessage(SourceLocation(), PresumedLoc(), DiagLevel, 610 State->diagBuf, nullptr, &Info); 611 612 if (DiagLevel == DiagnosticsEngine::Note) 613 ExitDiagBlock(); 614 615 return; 616 } 617 618 assert(Info.hasSourceManager() && LangOpts && 619 "Unexpected diagnostic with valid location outside of a source file"); 620 SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts); 621 Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, 622 State->diagBuf, 623 Info.getRanges(), 624 Info.getFixItHints(), 625 &Info.getSourceManager(), 626 &Info); 627} 628 629static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) { 630 switch (Level) { 631#define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X; 632 CASE(Ignored) 633 CASE(Note) 634 CASE(Remark) 635 CASE(Warning) 636 CASE(Error) 637 CASE(Fatal) 638#undef CASE 639 } 640 641 llvm_unreachable("invalid diagnostic level"); 642} 643 644void SDiagsWriter::EmitDiagnosticMessage(SourceLocation Loc, 645 PresumedLoc PLoc, 646 DiagnosticsEngine::Level Level, 647 StringRef Message, 648 const SourceManager *SM, 649 DiagOrStoredDiag D) { 650 llvm::BitstreamWriter &Stream = State->Stream; 651 RecordData &Record = State->Record; 652 AbbreviationMap &Abbrevs = State->Abbrevs; 653 654 // Emit the RECORD_DIAG record. 655 Record.clear(); 656 Record.push_back(RECORD_DIAG); 657 Record.push_back(getStableLevel(Level)); 658 AddLocToRecord(Loc, SM, PLoc, Record); 659 660 if (const Diagnostic *Info = D.dyn_cast<const Diagnostic*>()) { 661 // Emit the category string lazily and get the category ID. 662 unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); 663 Record.push_back(getEmitCategory(DiagID)); 664 // Emit the diagnostic flag string lazily and get the mapped ID. 665 Record.push_back(getEmitDiagnosticFlag(Level, Info->getID())); 666 } else { 667 Record.push_back(getEmitCategory()); 668 Record.push_back(getEmitDiagnosticFlag(Level)); 669 } 670 671 Record.push_back(Message.size()); 672 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message); 673} 674 675void 676SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc, 677 PresumedLoc PLoc, 678 DiagnosticsEngine::Level Level, 679 StringRef Message, 680 ArrayRef<clang::CharSourceRange> Ranges, 681 const SourceManager *SM, 682 DiagOrStoredDiag D) { 683 Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, SM, D); 684} 685 686void SDiagsWriter::EnterDiagBlock() { 687 State->Stream.EnterSubblock(BLOCK_DIAG, 4); 688} 689 690void SDiagsWriter::ExitDiagBlock() { 691 State->Stream.ExitBlock(); 692} 693 694void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D, 695 DiagnosticsEngine::Level Level) { 696 if (Level == DiagnosticsEngine::Note) 697 Writer.EnterDiagBlock(); 698} 699 700void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, 701 DiagnosticsEngine::Level Level) { 702 // Only end note diagnostics here, because we can't be sure when we've seen 703 // the last note associated with a non-note diagnostic. 704 if (Level == DiagnosticsEngine::Note) 705 Writer.ExitDiagBlock(); 706} 707 708void SDiagsWriter::EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, 709 ArrayRef<FixItHint> Hints, 710 const SourceManager &SM) { 711 llvm::BitstreamWriter &Stream = State->Stream; 712 RecordData &Record = State->Record; 713 AbbreviationMap &Abbrevs = State->Abbrevs; 714 715 // Emit Source Ranges. 716 for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); 717 I != E; ++I) 718 if (I->isValid()) 719 EmitCharSourceRange(*I, SM); 720 721 // Emit FixIts. 722 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 723 I != E; ++I) { 724 const FixItHint &Fix = *I; 725 if (Fix.isNull()) 726 continue; 727 Record.clear(); 728 Record.push_back(RECORD_FIXIT); 729 AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM); 730 Record.push_back(Fix.CodeToInsert.size()); 731 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, 732 Fix.CodeToInsert); 733 } 734} 735 736void SDiagsRenderer::emitCodeContext(SourceLocation Loc, 737 DiagnosticsEngine::Level Level, 738 SmallVectorImpl<CharSourceRange> &Ranges, 739 ArrayRef<FixItHint> Hints, 740 const SourceManager &SM) { 741 Writer.EmitCodeContext(Ranges, Hints, SM); 742} 743 744void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message, 745 const SourceManager *SM) { 746 Writer.EnterDiagBlock(); 747 PresumedLoc PLoc = SM ? SM->getPresumedLoc(Loc) : PresumedLoc(); 748 Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, 749 Message, SM, DiagOrStoredDiag()); 750 Writer.ExitDiagBlock(); 751} 752 753DiagnosticsEngine *SDiagsWriter::getMetaDiags() { 754 // FIXME: It's slightly absurd to create a new diagnostics engine here, but 755 // the other options that are available today are worse: 756 // 757 // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a 758 // part of. The DiagnosticsEngine would need to know not to send 759 // diagnostics back to the consumer that failed. This would require us to 760 // rework ChainedDiagnosticsConsumer and teach the engine about multiple 761 // consumers, which is difficult today because most APIs interface with 762 // consumers rather than the engine itself. 763 // 764 // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need 765 // to be distinct from the engine the writer was being added to and would 766 // normally not be used. 767 if (!State->MetaDiagnostics) { 768 IntrusiveRefCntPtr<DiagnosticIDs> IDs(new DiagnosticIDs()); 769 auto Client = 770 new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get()); 771 State->MetaDiagnostics = llvm::make_unique<DiagnosticsEngine>( 772 IDs, State->DiagOpts.get(), Client); 773 } 774 return State->MetaDiagnostics.get(); 775} 776 777void SDiagsWriter::RemoveOldDiagnostics() { 778 if (!llvm::sys::fs::remove(State->OutputFile)) 779 return; 780 781 getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); 782 // Disable merging child records, as whatever is in this file may be 783 // misleading. 784 MergeChildRecords = false; 785} 786 787void SDiagsWriter::finish() { 788 // The original instance is responsible for writing the file. 789 if (!OriginalInstance) 790 return; 791 792 // Finish off any diagnostic we were in the process of emitting. 793 if (State->EmittedAnyDiagBlocks) 794 ExitDiagBlock(); 795 796 if (MergeChildRecords) { 797 if (!State->EmittedAnyDiagBlocks) 798 // We have no diagnostics of our own, so we can just leave the child 799 // process' output alone 800 return; 801 802 if (llvm::sys::fs::exists(State->OutputFile)) 803 if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str())) 804 getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); 805 } 806 807 std::error_code EC; 808 auto OS = llvm::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(), 809 EC, llvm::sys::fs::F_None); 810 if (EC) { 811 getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) 812 << State->OutputFile << EC.message(); 813 return; 814 } 815 816 // Write the generated bitstream to "Out". 817 OS->write((char *)&State->Buffer.front(), State->Buffer.size()); 818 OS->flush(); 819} 820 821std::error_code SDiagsMerger::visitStartOfDiagnostic() { 822 Writer.EnterDiagBlock(); 823 return std::error_code(); 824} 825 826std::error_code SDiagsMerger::visitEndOfDiagnostic() { 827 Writer.ExitDiagBlock(); 828 return std::error_code(); 829} 830 831std::error_code 832SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start, 833 const serialized_diags::Location &End) { 834 RecordData::value_type Record[] = { 835 RECORD_SOURCE_RANGE, FileLookup[Start.FileID], Start.Line, Start.Col, 836 Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset}; 837 Writer.State->Stream.EmitRecordWithAbbrev( 838 Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record); 839 return std::error_code(); 840} 841 842std::error_code SDiagsMerger::visitDiagnosticRecord( 843 unsigned Severity, const serialized_diags::Location &Location, 844 unsigned Category, unsigned Flag, StringRef Message) { 845 RecordData::value_type Record[] = { 846 RECORD_DIAG, Severity, FileLookup[Location.FileID], Location.Line, 847 Location.Col, Location.Offset, CategoryLookup[Category], 848 Flag ? DiagFlagLookup[Flag] : 0, Message.size()}; 849 850 Writer.State->Stream.EmitRecordWithBlob( 851 Writer.State->Abbrevs.get(RECORD_DIAG), Record, Message); 852 return std::error_code(); 853} 854 855std::error_code 856SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start, 857 const serialized_diags::Location &End, 858 StringRef Text) { 859 RecordData::value_type Record[] = {RECORD_FIXIT, FileLookup[Start.FileID], 860 Start.Line, Start.Col, Start.Offset, 861 FileLookup[End.FileID], End.Line, End.Col, 862 End.Offset, Text.size()}; 863 864 Writer.State->Stream.EmitRecordWithBlob( 865 Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text); 866 return std::error_code(); 867} 868 869std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, 870 unsigned Timestamp, 871 StringRef Name) { 872 FileLookup[ID] = Writer.getEmitFile(Name.str().c_str()); 873 return std::error_code(); 874} 875 876std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { 877 CategoryLookup[ID] = Writer.getEmitCategory(ID); 878 return std::error_code(); 879} 880 881std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) { 882 DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name); 883 return std::error_code(); 884} 885