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