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