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