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