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