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