clang 22.0.0git
PlistDiagnostics.cpp
Go to the documentation of this file.
1//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
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//
9// This file defines the PlistDiagnostics object.
10//
11//===----------------------------------------------------------------------===//
12
13#include "PlistDiagnostics.h"
19#include "clang/Basic/Version.h"
26#include "llvm/ADT/SmallVector.h"
27#include "llvm/ADT/Statistic.h"
28#include <memory>
29#include <optional>
30
31using namespace clang;
32using namespace ento;
33using namespace markup;
34
35//===----------------------------------------------------------------------===//
36// Declarations of helper classes and functions for emitting bug reports in
37// plist format.
38//===----------------------------------------------------------------------===//
39
40namespace {
41 class PlistDiagnostics : public PathDiagnosticConsumer {
42 PathDiagnosticConsumerOptions DiagOpts;
43 const std::string OutputFile;
44 const Preprocessor &PP;
45 const cross_tu::CrossTranslationUnitContext &CTU;
46 const MacroExpansionContext &MacroExpansions;
47 const bool SupportsCrossFileDiagnostics;
48
49 void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
50 const PathPieces &Path);
51
52 public:
53 PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
54 const std::string &OutputFile, const Preprocessor &PP,
55 const cross_tu::CrossTranslationUnitContext &CTU,
56 const MacroExpansionContext &MacroExpansions,
57 bool supportsMultipleFiles);
58
59 ~PlistDiagnostics() override {}
60
61 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
62 FilesMade *filesMade) override;
63
64 StringRef getName() const override {
65 return "PlistDiagnostics";
66 }
67
68 PathGenerationScheme getGenerationScheme() const override {
69 return Extensive;
70 }
71 bool supportsLogicalOpControlFlow() const override { return true; }
72 bool supportsCrossFileDiagnostics() const override {
73 return SupportsCrossFileDiagnostics;
74 }
75 };
76} // end anonymous namespace
77
78namespace {
79
80/// A helper class for emitting a single report.
81class PlistPrinter {
82 const FIDMap& FM;
83 const Preprocessor &PP;
84 const cross_tu::CrossTranslationUnitContext &CTU;
85 const MacroExpansionContext &MacroExpansions;
86 llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
87
88public:
89 PlistPrinter(const FIDMap &FM, const Preprocessor &PP,
90 const cross_tu::CrossTranslationUnitContext &CTU,
91 const MacroExpansionContext &MacroExpansions)
92 : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
93
94 void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
95 ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
96 }
97
98 /// Print the expansions of the collected macro pieces.
99 ///
100 /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one
101 /// is found through a call piece, etc), it's subpieces are reported, and the
102 /// piece itself is collected. Call this function after the entire bugpath
103 /// was reported.
104 void ReportMacroExpansions(raw_ostream &o, unsigned indent);
105
106private:
107 void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P,
108 unsigned indent, unsigned depth, bool includeControlFlow,
109 bool isKeyEvent = false) {
110 switch (P.getKind()) {
112 if (includeControlFlow)
113 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent);
114 break;
116 ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent,
117 depth);
118 break;
120 ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth,
121 isKeyEvent);
122 break;
124 ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent,
125 depth);
126 break;
128 ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
129 break;
131 ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent);
132 break;
133 }
134 }
135
136 void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges,
137 unsigned indent);
138 void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
139 void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent);
140
141 void ReportControlFlow(raw_ostream &o,
142 const PathDiagnosticControlFlowPiece& P,
143 unsigned indent);
144 void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
145 unsigned indent, unsigned depth, bool isKeyEvent = false);
146 void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
147 unsigned indent, unsigned depth);
148 void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P,
149 unsigned indent, unsigned depth);
150 void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
151 unsigned indent);
152
153 void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P,
154 unsigned indent);
155};
156
157} // end of anonymous namespace
158
159/// Print coverage information to output stream @c o.
160/// May modify the used list of files @c Fids by inserting new ones.
161static void printCoverage(const PathDiagnostic *D,
162 unsigned InputIndentLevel,
164 FIDMap &FM,
165 llvm::raw_fd_ostream &o);
166
167static std::optional<StringRef> getExpandedMacro(
169 const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
170
171//===----------------------------------------------------------------------===//
172// Methods of PlistPrinter.
173//===----------------------------------------------------------------------===//
174
175void PlistPrinter::EmitRanges(raw_ostream &o,
176 const ArrayRef<SourceRange> Ranges,
177 unsigned indent) {
178
179 if (Ranges.empty())
180 return;
181
182 Indent(o, indent) << "<key>ranges</key>\n";
183 Indent(o, indent) << "<array>\n";
184 ++indent;
185
186 const SourceManager &SM = PP.getSourceManager();
187 const LangOptions &LangOpts = PP.getLangOpts();
188
189 for (auto &R : Ranges)
190 EmitRange(o, SM,
191 Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
192 FM, indent + 1);
193 --indent;
194 Indent(o, indent) << "</array>\n";
195}
196
197void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
198 unsigned indent) {
199 // Output the text.
200 assert(!Message.empty());
201 Indent(o, indent) << "<key>extended_message</key>\n";
202 Indent(o, indent);
203 EmitString(o, Message) << '\n';
204
205 // Output the short text.
206 // FIXME: Really use a short string.
207 Indent(o, indent) << "<key>message</key>\n";
208 Indent(o, indent);
209 EmitString(o, Message) << '\n';
210}
211
212void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
213 unsigned indent) {
214 if (fixits.size() == 0)
215 return;
216
217 const SourceManager &SM = PP.getSourceManager();
218 const LangOptions &LangOpts = PP.getLangOpts();
219
220 Indent(o, indent) << "<key>fixits</key>\n";
221 Indent(o, indent) << "<array>\n";
222 for (const auto &fixit : fixits) {
223 assert(!fixit.isNull());
224 // FIXME: Add support for InsertFromRange and BeforePreviousInsertion.
225 assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");
226 assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");
227 Indent(o, indent) << " <dict>\n";
228 Indent(o, indent) << " <key>remove_range</key>\n";
229 EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts),
230 FM, indent + 2);
231 Indent(o, indent) << " <key>insert_string</key>";
232 EmitString(o, fixit.CodeToInsert);
233 o << "\n";
234 Indent(o, indent) << " </dict>\n";
235 }
236 Indent(o, indent) << "</array>\n";
237}
238
239void PlistPrinter::ReportControlFlow(raw_ostream &o,
240 const PathDiagnosticControlFlowPiece& P,
241 unsigned indent) {
242
243 const SourceManager &SM = PP.getSourceManager();
244 const LangOptions &LangOpts = PP.getLangOpts();
245
246 Indent(o, indent) << "<dict>\n";
247 ++indent;
248
249 Indent(o, indent) << "<key>kind</key><string>control</string>\n";
250
251 // Emit edges.
252 Indent(o, indent) << "<key>edges</key>\n";
253 ++indent;
254 Indent(o, indent) << "<array>\n";
255 ++indent;
257 I!=E; ++I) {
258 Indent(o, indent) << "<dict>\n";
259 ++indent;
260
261 // Make the ranges of the start and end point self-consistent with adjacent edges
262 // by forcing to use only the beginning of the range. This simplifies the layout
263 // logic for clients.
264 Indent(o, indent) << "<key>start</key>\n";
265 SourceRange StartEdge(
266 SM.getExpansionLoc(I->getStart().asRange().getBegin()));
267 EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
268 indent + 1);
269
270 Indent(o, indent) << "<key>end</key>\n";
271 SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
272 EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
273 indent + 1);
274
275 --indent;
276 Indent(o, indent) << "</dict>\n";
277 }
278 --indent;
279 Indent(o, indent) << "</array>\n";
280 --indent;
281
282 // Output any helper text.
283 const auto &s = P.getString();
284 if (!s.empty()) {
285 Indent(o, indent) << "<key>alternate</key>";
286 EmitString(o, s) << '\n';
287 }
288
289 assert(P.getFixits().size() == 0 &&
290 "Fixits on constrol flow pieces are not implemented yet!");
291
292 --indent;
293 Indent(o, indent) << "</dict>\n";
294}
295
296void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
297 unsigned indent, unsigned depth,
298 bool isKeyEvent) {
299
300 const SourceManager &SM = PP.getSourceManager();
301
302 Indent(o, indent) << "<dict>\n";
303 ++indent;
304
305 Indent(o, indent) << "<key>kind</key><string>event</string>\n";
306
307 if (isKeyEvent) {
308 Indent(o, indent) << "<key>key_event</key><true/>\n";
309 }
310
311 // Output the location.
312 FullSourceLoc L = P.getLocation().asLocation();
313
314 Indent(o, indent) << "<key>location</key>\n";
315 EmitLocation(o, SM, L, FM, indent);
316
317 // Output the ranges (if any).
318 ArrayRef<SourceRange> Ranges = P.getRanges();
319 EmitRanges(o, Ranges, indent);
320
321 // Output the call depth.
322 Indent(o, indent) << "<key>depth</key>";
323 EmitInteger(o, depth) << '\n';
324
325 // Output the text.
326 EmitMessage(o, P.getString(), indent);
327
328 // Output the fixits.
329 EmitFixits(o, P.getFixits(), indent);
330
331 // Finish up.
332 --indent;
333 Indent(o, indent); o << "</dict>\n";
334}
335
336void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
337 unsigned indent,
338 unsigned depth) {
339
340 if (auto callEnter = P.getCallEnterEvent())
341 ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true,
343
344
345 ++depth;
346
347 if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
348 ReportPiece(o, *callEnterWithinCaller, indent, depth,
349 /*includeControlFlow*/ true);
350
351 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
352 ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true);
353
354 --depth;
355
356 if (auto callExit = P.getCallExitEvent())
357 ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true);
358
359 assert(P.getFixits().size() == 0 &&
360 "Fixits on call pieces are not implemented yet!");
361}
362
363void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
364 const PathDiagnosticMacroPiece& P,
365 unsigned indent, unsigned depth) {
366 MacroPieces.push_back(&P);
367
368 for (const auto &SubPiece : P.subPieces) {
369 ReportPiece(o, *SubPiece, indent, depth, /*includeControlFlow*/ false);
370 }
371
372 assert(P.getFixits().size() == 0 &&
373 "Fixits on constrol flow pieces are not implemented yet!");
374}
375
376void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
377
378 for (const PathDiagnosticMacroPiece *P : MacroPieces) {
379 const SourceManager &SM = PP.getSourceManager();
380
381 SourceLocation MacroExpansionLoc =
383
384 const std::optional<StringRef> MacroName =
385 MacroExpansions.getOriginalText(MacroExpansionLoc);
386 const std::optional<StringRef> ExpansionText =
387 getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
388
389 if (!MacroName || !ExpansionText)
390 continue;
391
392 Indent(o, indent) << "<dict>\n";
393 ++indent;
394
395 // Output the location.
396 FullSourceLoc L = P->getLocation().asLocation();
397
398 Indent(o, indent) << "<key>location</key>\n";
399 EmitLocation(o, SM, L, FM, indent);
400
401 // Output the ranges (if any).
402 ArrayRef<SourceRange> Ranges = P->getRanges();
403 EmitRanges(o, Ranges, indent);
404
405 // Output the macro name.
406 Indent(o, indent) << "<key>name</key>";
407 EmitString(o, *MacroName) << '\n';
408
409 // Output what it expands into.
410 Indent(o, indent) << "<key>expansion</key>";
411 EmitString(o, *ExpansionText) << '\n';
412
413 // Finish up.
414 --indent;
415 Indent(o, indent);
416 o << "</dict>\n";
417 }
418}
419
420void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
421 unsigned indent) {
422
423 const SourceManager &SM = PP.getSourceManager();
424
425 Indent(o, indent) << "<dict>\n";
426 ++indent;
427
428 // Output the location.
429 FullSourceLoc L = P.getLocation().asLocation();
430
431 Indent(o, indent) << "<key>location</key>\n";
432 EmitLocation(o, SM, L, FM, indent);
433
434 // Output the ranges (if any).
435 ArrayRef<SourceRange> Ranges = P.getRanges();
436 EmitRanges(o, Ranges, indent);
437
438 // Output the text.
439 EmitMessage(o, P.getString(), indent);
440
441 // Output the fixits.
442 EmitFixits(o, P.getFixits(), indent);
443
444 // Finish up.
445 --indent;
446 Indent(o, indent); o << "</dict>\n";
447}
448
449void PlistPrinter::ReportPopUp(raw_ostream &o,
450 const PathDiagnosticPopUpPiece &P,
451 unsigned indent) {
452 const SourceManager &SM = PP.getSourceManager();
453
454 Indent(o, indent) << "<dict>\n";
455 ++indent;
456
457 Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n";
458
459 // Output the location.
460 FullSourceLoc L = P.getLocation().asLocation();
461
462 Indent(o, indent) << "<key>location</key>\n";
463 EmitLocation(o, SM, L, FM, indent);
464
465 // Output the ranges (if any).
466 ArrayRef<SourceRange> Ranges = P.getRanges();
467 EmitRanges(o, Ranges, indent);
468
469 // Output the text.
470 EmitMessage(o, P.getString(), indent);
471
472 assert(P.getFixits().size() == 0 &&
473 "Fixits on pop-up pieces are not implemented yet!");
474
475 // Finish up.
476 --indent;
477 Indent(o, indent) << "</dict>\n";
478}
479
480//===----------------------------------------------------------------------===//
481// Static function definitions.
482//===----------------------------------------------------------------------===//
483
484/// Print coverage information to output stream @c o.
485/// May modify the used list of files @c Fids by inserting new ones.
486static void printCoverage(const PathDiagnostic *D,
487 unsigned InputIndentLevel,
489 FIDMap &FM,
490 llvm::raw_fd_ostream &o) {
491 unsigned IndentLevel = InputIndentLevel;
492
493 Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n";
494 Indent(o, IndentLevel) << "<dict>\n";
495 IndentLevel++;
496
497 // Mapping from file IDs to executed lines.
498 const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines();
499 for (const auto &[FID, Lines] : ExecutedLines) {
500 unsigned FileKey = AddFID(FM, Fids, FID);
501 Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n";
502 Indent(o, IndentLevel) << "<array>\n";
503 IndentLevel++;
504 for (unsigned LineNo : Lines) {
506 EmitInteger(o, LineNo) << "\n";
507 }
508 IndentLevel--;
509 Indent(o, IndentLevel) << "</array>\n";
510 }
511 IndentLevel--;
512 Indent(o, IndentLevel) << "</dict>\n";
513
514 assert(IndentLevel == InputIndentLevel);
515}
516
517//===----------------------------------------------------------------------===//
518// Methods of PlistDiagnostics.
519//===----------------------------------------------------------------------===//
520
521PlistDiagnostics::PlistDiagnostics(
522 PathDiagnosticConsumerOptions DiagOpts, const std::string &output,
523 const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
524 const MacroExpansionContext &MacroExpansions, bool supportsMultipleFiles)
525 : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
526 MacroExpansions(MacroExpansions),
527 SupportsCrossFileDiagnostics(supportsMultipleFiles) {
528 // FIXME: Will be used by a later planned change.
529 (void)this->CTU;
530}
531
532/// Creates and registers a Plist diagnostic consumer, without any additional
533/// text consumer.
536 const std::string &OutputFile, const Preprocessor &PP,
538 const MacroExpansionContext &MacroExpansions, bool SupportsMultipleFiles) {
539
540 // TODO: Emit an error here.
541 if (OutputFile.empty())
542 return;
543
544 C.push_back(std::make_unique<PlistDiagnostics>(
545 DiagOpts, OutputFile, PP, CTU, MacroExpansions, SupportsMultipleFiles));
546}
547
548void ento::createPlistDiagnosticConsumer(
550 const std::string &OutputFile, const Preprocessor &PP,
552 const MacroExpansionContext &MacroExpansions) {
553
554 createPlistDiagnosticConsumerImpl(DiagOpts, C, OutputFile, PP, CTU,
555 MacroExpansions,
556 /*SupportsMultipleFiles=*/false);
557 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
558 PP, CTU, MacroExpansions);
559}
560
561void ento::createPlistMultiFileDiagnosticConsumer(
563 const std::string &OutputFile, const Preprocessor &PP,
565 const MacroExpansionContext &MacroExpansions) {
566
567 createPlistDiagnosticConsumerImpl(DiagOpts, C, OutputFile, PP, CTU,
568 MacroExpansions,
569 /*SupportsMultipleFiles=*/true);
570
571 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
572 PP, CTU, MacroExpansions);
573}
574
575void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
576 const PathPieces &Path) {
577 PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
578 assert(std::is_partitioned(Path.begin(), Path.end(),
579 [](const PathDiagnosticPieceRef &E) {
580 return E->getKind() == PathDiagnosticPiece::Note;
581 }) &&
582 "PathDiagnostic is not partitioned so that notes precede the rest");
583
584 PathPieces::const_iterator FirstNonNote =
585 llvm::partition_point(Path, [](const PathDiagnosticPieceRef &E) {
586 return E->getKind() == PathDiagnosticPiece::Note;
587 });
588
589 PathPieces::const_iterator I = Path.begin();
590
591 if (FirstNonNote != Path.begin()) {
592 o << " <key>notes</key>\n"
593 " <array>\n";
594
595 for (; I != FirstNonNote; ++I)
596 Printer.ReportDiag(o, **I);
597
598 o << " </array>\n";
599 }
600
601 o << " <key>path</key>\n";
602
603 o << " <array>\n";
604
605 for (const auto &Piece : llvm::make_range(I, Path.end()))
606 Printer.ReportDiag(o, *Piece);
607
608 o << " </array>\n";
609
610 if (!DiagOpts.ShouldDisplayMacroExpansions)
611 return;
612
613 o << " <key>macro_expansions</key>\n"
614 " <array>\n";
615 Printer.ReportMacroExpansions(o, /* indent */ 4);
616 o << " </array>\n";
617}
618
619void PlistDiagnostics::FlushDiagnosticsImpl(
620 std::vector<const PathDiagnostic *> &Diags,
621 FilesMade *filesMade) {
622 // Build up a set of FIDs that we use by scanning the locations and
623 // ranges of the diagnostics.
624 FIDMap FM;
625 SmallVector<FileID, 10> Fids;
626 const SourceManager& SM = PP.getSourceManager();
627 const LangOptions &LangOpts = PP.getLangOpts();
628
629 auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) {
630 AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
631 ArrayRef<SourceRange> Ranges = Piece.getRanges();
632 for (const SourceRange &Range : Ranges) {
633 AddFID(FM, Fids, SM, Range.getBegin());
634 AddFID(FM, Fids, SM, Range.getEnd());
635 }
636 };
637
638 for (const PathDiagnostic *D : Diags) {
639
640 SmallVector<const PathPieces *, 5> WorkList;
641 WorkList.push_back(&D->path);
642
643 while (!WorkList.empty()) {
644 const PathPieces &Path = *WorkList.pop_back_val();
645
646 for (const auto &Iter : Path) {
647 const PathDiagnosticPiece &Piece = *Iter;
648 AddPieceFID(Piece);
649
650 if (const PathDiagnosticCallPiece *Call =
651 dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
652 if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
653 AddPieceFID(*CallEnterWithin);
654
655 if (auto CallEnterEvent = Call->getCallEnterEvent())
656 AddPieceFID(*CallEnterEvent);
657
658 WorkList.push_back(&Call->path);
659 } else if (const PathDiagnosticMacroPiece *Macro =
660 dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
661 WorkList.push_back(&Macro->subPieces);
662 }
663 }
664 }
665 }
666
667 // Open the file.
668 std::error_code EC;
669 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
670 if (EC) {
671 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
672 return;
673 }
674
676
677 // Write the root object: a <dict> containing...
678 // - "clang_version", the string representation of clang version
679 // - "files", an <array> mapping from FIDs to file names
680 // - "diagnostics", an <array> containing the path diagnostics
681 o << "<dict>\n" <<
682 " <key>clang_version</key>\n";
683 EmitString(o, getClangFullVersion()) << '\n';
684 o << " <key>diagnostics</key>\n"
685 " <array>\n";
686
687 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
688 DE = Diags.end(); DI!=DE; ++DI) {
689
690 o << " <dict>\n";
691
692 const PathDiagnostic *D = *DI;
693 printBugPath(o, FM, D->path);
694
695 // Output the bug type and bug category.
696 o << " <key>description</key>";
697 EmitString(o, D->getShortDescription()) << '\n';
698 o << " <key>category</key>";
699 EmitString(o, D->getCategory()) << '\n';
700 o << " <key>type</key>";
701 EmitString(o, D->getBugType()) << '\n';
702 o << " <key>check_name</key>";
703 EmitString(o, D->getCheckerName()) << '\n';
704
705 o << " <!-- This hash is experimental and going to change! -->\n";
706 o << " <key>issue_hash_content_of_line_in_context</key>";
707 PathDiagnosticLocation UPDLoc = D->getUniqueingLoc();
708 FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid()
709 ? UPDLoc.asLocation()
710 : D->getLocation().asLocation()),
711 SM);
712 const Decl *DeclWithIssue = D->getDeclWithIssue();
714 DeclWithIssue, LangOpts))
715 << '\n';
716
717 // Output information about the semantic context where
718 // the issue occurred.
719 if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
720 // FIXME: handle blocks, which have no name.
721 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
722 StringRef declKind;
723 switch (ND->getKind()) {
724 case Decl::CXXRecord:
725 declKind = "C++ class";
726 break;
727 case Decl::CXXMethod:
728 declKind = "C++ method";
729 break;
730 case Decl::ObjCMethod:
731 declKind = "Objective-C method";
732 break;
733 case Decl::Function:
734 declKind = "function";
735 break;
736 default:
737 break;
738 }
739 if (!declKind.empty()) {
740 const std::string &declName = ND->getDeclName().getAsString();
741 o << " <key>issue_context_kind</key>";
742 EmitString(o, declKind) << '\n';
743 o << " <key>issue_context</key>";
744 EmitString(o, declName) << '\n';
745 }
746
747 // Output the bug hash for issue unique-ing. Currently, it's just an
748 // offset from the beginning of the function.
749 if (const Stmt *Body = DeclWithIssue->getBody()) {
750
751 // If the bug uniqueing location exists, use it for the hash.
752 // For example, this ensures that two leaks reported on the same line
753 // will have different issue_hashes and that the hash will identify
754 // the leak location even after code is added between the allocation
755 // site and the end of scope (leak report location).
756 if (UPDLoc.isValid()) {
757 FullSourceLoc UFunL(
758 SM.getExpansionLoc(
760 SM);
761 o << " <key>issue_hash_function_offset</key><string>"
762 << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
763 << "</string>\n";
764
765 // Otherwise, use the location on which the bug is reported.
766 } else {
767 FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM);
768 o << " <key>issue_hash_function_offset</key><string>"
769 << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
770 << "</string>\n";
771 }
772
773 }
774 }
775 }
776
777 // Output the location of the bug.
778 o << " <key>location</key>\n";
779 EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2);
780
781 // Output the diagnostic to the sub-diagnostic client, if any.
782 if (!filesMade->empty()) {
783 StringRef lastName;
784 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
785 if (files) {
786 for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
787 CE = files->end(); CI != CE; ++CI) {
788 StringRef newName = CI->first;
789 if (newName != lastName) {
790 if (!lastName.empty()) {
791 o << " </array>\n";
792 }
793 lastName = newName;
794 o << " <key>" << lastName << "_files</key>\n";
795 o << " <array>\n";
796 }
797 o << " <string>" << CI->second << "</string>\n";
798 }
799 o << " </array>\n";
800 }
801 }
802
803 printCoverage(D, /*IndentLevel=*/2, Fids, FM, o);
804
805 // Close up the entry.
806 o << " </dict>\n";
807 }
808
809 o << " </array>\n";
810
811 o << " <key>files</key>\n"
812 " <array>\n";
813 for (FileID FID : Fids)
814 EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n';
815 o << " </array>\n";
816
817 if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) {
818 o << " <key>statistics</key>\n";
819 std::string stats;
820 llvm::raw_string_ostream os(stats);
821 llvm::PrintStatisticsJSON(os);
822 EmitString(o, html::EscapeText(stats)) << '\n';
823 }
824
825 // Finish.
826 o << "</dict>\n</plist>\n";
827}
828
829//===----------------------------------------------------------------------===//
830// Definitions of helper functions and methods for expanding macros.
831//===----------------------------------------------------------------------===//
832
833static std::optional<StringRef>
836 const MacroExpansionContext &MacroExpansions,
837 const SourceManager &SM) {
838 if (auto CTUMacroExpCtx =
839 CTU.getMacroExpansionContextForSourceLocation(MacroExpansionLoc)) {
840 return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
841 }
842 return MacroExpansions.getExpandedText(MacroExpansionLoc);
843}
unsigned IndentLevel
The indent level of this token. Copied from the surrounding line.
#define SM(sm)
static void printCoverage(const PathDiagnostic *D, unsigned InputIndentLevel, SmallVectorImpl< FileID > &Fids, FIDMap &FM, llvm::raw_fd_ostream &o)
Print coverage information to output stream o.
static std::optional< StringRef > getExpandedMacro(SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, const SourceManager &SM)
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Defines version macros and version-related utility functions for Clang.
__device__ __2f16 float __ockl_bool s
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition DeclBase.h:1087
FullSourceLoc getExpansionLoc() const
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
static CharSourceRange getAsCharRange(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Given a token range, produce a corresponding CharSourceRange that is not a token range.
Definition Lexer.h:430
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
std::optional< StringRef > getExpandedText(SourceLocation MacroExpansionLoc) const
std::optional< StringRef > getOriginalText(SourceLocation MacroExpansionLoc) const
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
SourceManager & getSourceManager() const
const LangOptions & getLangOpts() const
Encodes a location in the source.
This class handles loading and caching of source files into memory.
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:346
This class is used for tools that requires cross translation unit capability.
std::optional< clang::MacroExpansionContext > getMacroExpansionContextForSourceLocation(const clang::SourceLocation &ToLoc) const
Returns the MacroExpansionContext for the imported TU to which the given source-location corresponds.
std::shared_ptr< PathDiagnosticEventPiece > getCallExitEvent() const
std::shared_ptr< PathDiagnosticEventPiece > getCallEnterWithinCallerEvent() const
std::shared_ptr< PathDiagnosticEventPiece > getCallEnterEvent() const
std::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
ArrayRef< FixItHint > getFixits() const
Return the fix-it hints associated with this PathDiagnosticPiece.
PathDiagnosticLocation getLocation() const override
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
StringRef getCheckerName() const
PathDiagnosticLocation getUniqueingLoc() const
Get the location on which the report should be uniqued.
const Decl * getDeclWithIssue() const
Return the semantic context where an issue occurred.
const FilesToLineNumsMap & getExecutedLines() const
const Decl * getUniqueingDecl() const
Get the declaration containing the uniqueing location.
StringRef getCategory() const
StringRef getShortDescription() const
PathDiagnosticLocation getLocation() const
std::vector< std::unique_ptr< PathDiagnosticConsumer > > PathDiagnosticConsumers
void createPlistDiagnosticConsumerImpl(PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Output, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, bool SupportsMultipleFiles)
Creates and registers a Plist diagnostic consumer, without any additional text consumer.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
void EscapeText(Rewriter &R, FileID FID, bool EscapeSpaces=false, bool ReplaceTabs=false)
EscapeText - HTMLize a specified file so that special characters are are translated so that they are ...
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
void EmitRange(raw_ostream &o, const SourceManager &SM, CharSourceRange R, const FIDMap &FM, unsigned indent)
raw_ostream & EmitString(raw_ostream &o, StringRef s)
llvm::DenseMap< FileID, unsigned > FIDMap
unsigned AddFID(FIDMap &FIDs, SmallVectorImpl< FileID > &V, FileID FID)
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
raw_ostream & EmitPlistHeader(raw_ostream &o)
void EmitLocation(raw_ostream &o, const SourceManager &SM, SourceLocation L, const FIDMap &FM, unsigned indent)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
raw_ostream & Indent(raw_ostream &Out, const unsigned int Space, bool IsDot)
Definition JsonSupport.h:21
llvm::SmallString< 32 > getIssueHash(const FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef WarningMessage, const Decl *IssueDecl, const LangOptions &LangOpts)
Returns an opaque identifier for a diagnostic.
U cast(CodeGen::Address addr)
Definition Address.h:327
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
Definition Version.cpp:96
These options tweak the behavior of path diangostic consumers.
bool ShouldDisplayMacroExpansions
Whether to include additional information about macro expansions with the diagnostics,...
bool ShouldSerializeStats
Whether to include LLVM statistics of the process in the diagnostic.