clang  19.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"
21 #include "clang/Frontend/ASTUnit.h"
22 #include "clang/Lex/Preprocessor.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 
33 using namespace clang;
34 using namespace ento;
35 using namespace markup;
36 
37 //===----------------------------------------------------------------------===//
38 // Declarations of helper classes and functions for emitting bug reports in
39 // plist format.
40 //===----------------------------------------------------------------------===//
41 
42 namespace {
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 
80 namespace {
81 
82 /// A helper class for emitting a single report.
83 class PlistPrinter {
84  const FIDMap& FM;
85  const Preprocessor &PP;
87  const MacroExpansionContext &MacroExpansions;
89 
90 public:
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 
108 private:
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.
163 static void printCoverage(const PathDiagnostic *D,
164  unsigned InputIndentLevel,
166  FIDMap &FM,
167  llvm::raw_fd_ostream &o);
168 
169 static std::optional<StringRef> getExpandedMacro(
171  const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
172 
173 //===----------------------------------------------------------------------===//
174 // Methods of PlistPrinter.
175 //===----------------------------------------------------------------------===//
176 
177 void 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 
199 void 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 
214 void 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 
241 void 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;
258  for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
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 
298 void 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 
338 void 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 
365 void 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 
378 void 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 
422 void 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 
451 void 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.
488 static 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 
523 PlistDiagnostics::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 
534 void 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 
551 void 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 
568 void 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 
612 void 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 
668  EmitPlistHeader(o);
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>";
701  FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid()
702  ? UPDLoc.asLocation()
703  : D->getLocation().asLocation()),
704  SM);
705  const Decl *DeclWithIssue = D->getDeclWithIssue();
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(
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  os.flush();
816  EmitString(o, html::EscapeText(stats)) << '\n';
817  }
818 
819  // Finish.
820  o << "</dict>\n</plist>\n";
821 }
822 
823 //===----------------------------------------------------------------------===//
824 // Definitions of helper functions and methods for expanding macros.
825 //===----------------------------------------------------------------------===//
826 
827 static std::optional<StringRef>
830  const MacroExpansionContext &MacroExpansions,
831  const SourceManager &SM) {
832  if (auto CTUMacroExpCtx =
833  CTU.getMacroExpansionContextForSourceLocation(MacroExpansionLoc)) {
834  return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
835  }
836  return MacroExpansions.getExpandedText(MacroExpansionLoc);
837 }
StringRef P
#define SM(sm)
Definition: Cuda.cpp:83
Defines the clang::FileManager interface and associated types.
static llvm::SmallString< 32 > getIssueHash(const PathDiagnostic &D, const Preprocessor &PP)
unsigned Iter
Definition: HTMLLogger.cpp:154
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.
static std::string getName(const CallEvent &Call)
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:1077
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:482
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:249
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
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:338
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::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
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 Decl * getUniqueingDecl() const
Get the declaration containing the uniqueing location.
StringRef getBugType() const
const FilesToLineNumsMap & getExecutedLines() const
StringRef getCategory() const
StringRef getShortDescription() const
PathDiagnosticLocation getLocation() const
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 ...
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
void EmitRange(raw_ostream &o, const SourceManager &SM, CharSourceRange R, const FIDMap &FM, unsigned indent)
Definition: PlistSupport.h:122
raw_ostream & EmitPlistHeader(raw_ostream &o)
Definition: PlistSupport.h:62
unsigned AddFID(FIDMap &FIDs, SmallVectorImpl< FileID > &V, FileID FID)
Definition: PlistSupport.h:27
raw_ostream & EmitString(raw_ostream &o, StringRef s)
Definition: PlistSupport.h:78
llvm::DenseMap< FileID, unsigned > FIDMap
Definition: PlistSupport.h:25
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
Definition: PlistSupport.h:71
void EmitLocation(raw_ostream &o, const SourceManager &SM, SourceLocation L, const FIDMap &FM, unsigned indent)
Definition: PlistSupport.h:106
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
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
Definition: Version.cpp:96
Definition: Format.h:5433
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.