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:
64 DiagnosticsEngine::Level Level, StringRef Message,
66 DiagOrStoredDiag D) override;
67
70 ArrayRef<CharSourceRange> Ranges) override {}
71
72 void emitNote(FullSourceLoc Loc, StringRef Message) override;
73
76 ArrayRef<FixItHint> Hints) override;
77
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,
112 StringRef CodeToInsert) override;
113 std::error_code
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
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,
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())
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(
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,
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,
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[] = {
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,
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.
const Decl * D
Expr * E
StringRef Text
Definition: Format.cpp:3178
int Category
Definition: Format.cpp:3180
StringRef Filename
Definition: Format.cpp:3177
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)
Definition: OffloadArch.cpp:16
SourceRange Range
Definition: SemaObjC.cpp:753
SourceLocation Loc
Definition: SemaObjC.cpp:754
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.
#define CASE(X)
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.
Represents a character-granular source range.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1722
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
Definition: Diagnostic.cpp:812
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Definition: Diagnostic.h:1758
virtual void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP=nullptr)
Callback to inform the diagnostic client that processing of a source file is beginning.
Definition: Diagnostic.h:1746
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.
virtual void emitNote(FullSourceLoc Loc, StringRef Message)=0
Options for controlling the compiler diagnostics engine.
virtual void endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)
virtual void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef< CharSourceRange > Ranges)=0
virtual void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, DiagOrStoredDiag Info)=0
virtual void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl< CharSourceRange > &Ranges, ArrayRef< FixItHint > Hints)=0
virtual void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine a...
Definition: Diagnostic.h:1548
const SourceLocation & getLocation() const
Definition: Diagnostic.h:1564
void FormatDiagnostic(SmallVectorImpl< char > &OutStr) const
Format this diagnostic into a string, substituting the formal arguments into the %0 slots.
SourceManager & getSourceManager() const
Definition: Diagnostic.h:1566
ArrayRef< FixItHint > getFixItHints() const
Definition: Diagnostic.h:1651
bool hasSourceManager() const
Definition: Diagnostic.h:1565
unsigned getID() const
Definition: Diagnostic.h:1563
ArrayRef< CharSourceRange > getRanges() const
Return an array reference for this diagnostic's ranges.
Definition: Diagnostic.h:1642
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:231
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:236
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:78
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.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:434
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
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:145
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.
This class handles loading and caching of source files into memory.
SourceLocation getEnd() const
SourceLocation getBegin() const
A base class that handles reading serialized diagnostics from a file.
virtual std::error_code visitCategoryRecord(unsigned ID, StringRef Name)
Visit a category. This associates the category ID to a Name.
virtual std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name)
Visit a flag. This associates the flag's ID to a Name.
virtual std::error_code visitStartOfDiagnostic()
Visit the start of a diagnostic block.
virtual std::error_code visitDiagnosticRecord(unsigned Severity, const Location &Location, unsigned Category, unsigned Flag, StringRef Message)
Visit a diagnostic.
virtual std::error_code visitSourceRangeRecord(const Location &Start, const Location &End)
Visit a source range.
virtual std::error_code visitFixitRecord(const Location &Start, const Location &End, StringRef Text)
Visit a fixit hint.
virtual std::error_code visitFilenameRecord(unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name)
Visit a filename. This associates the file's ID to a Name.
std::error_code readDiagnostics(StringRef File)
Read the diagnostics in File.
virtual std::error_code visitEndOfDiagnostic()
Visit the end of a diagnostic block.
Severity
Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs to either Ignore (nothing),...
Definition: DiagnosticIDs.h:82
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
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
#define true
Definition: stdbool.h:25
#define false
Definition: stdbool.h:26
A location that is represented in the serialized diagnostics.