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