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