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