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