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