1 //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the PlistDiagnostics object.
11 //===----------------------------------------------------------------------===//
13 #include "clang/Analysis/IssueHash.h"
14 #include "clang/Analysis/MacroExpansionContext.h"
15 #include "clang/Analysis/PathDiagnostic.h"
16 #include "clang/Basic/FileManager.h"
17 #include "clang/Basic/PlistSupport.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Basic/Version.h"
20 #include "clang/CrossTU/CrossTranslationUnit.h"
21 #include "clang/Frontend/ASTUnit.h"
22 #include "clang/Lex/Preprocessor.h"
23 #include "clang/Lex/TokenConcatenation.h"
24 #include "clang/Rewrite/Core/HTMLRewrite.h"
25 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.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"
33 using namespace clang
;
35 using namespace markup
;
37 //===----------------------------------------------------------------------===//
38 // Declarations of helper classes and functions for emitting bug reports in
40 //===----------------------------------------------------------------------===//
43 class PlistDiagnostics
: public PathDiagnosticConsumer
{
44 PathDiagnosticConsumerOptions DiagOpts
;
45 const std::string OutputFile
;
46 const Preprocessor
&PP
;
47 const cross_tu::CrossTranslationUnitContext
&CTU
;
48 const MacroExpansionContext
&MacroExpansions
;
49 const bool SupportsCrossFileDiagnostics
;
51 void printBugPath(llvm::raw_ostream
&o
, const FIDMap
&FM
,
52 const PathPieces
&Path
);
55 PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts
,
56 const std::string
&OutputFile
, const Preprocessor
&PP
,
57 const cross_tu::CrossTranslationUnitContext
&CTU
,
58 const MacroExpansionContext
&MacroExpansions
,
59 bool supportsMultipleFiles
);
61 ~PlistDiagnostics() override
{}
63 void FlushDiagnosticsImpl(std::vector
<const PathDiagnostic
*> &Diags
,
64 FilesMade
*filesMade
) override
;
66 StringRef
getName() const override
{
67 return "PlistDiagnostics";
70 PathGenerationScheme
getGenerationScheme() const override
{
73 bool supportsLogicalOpControlFlow() const override
{ return true; }
74 bool supportsCrossFileDiagnostics() const override
{
75 return SupportsCrossFileDiagnostics
;
78 } // end anonymous namespace
82 /// A helper class for emitting a single report.
85 const Preprocessor
&PP
;
86 const cross_tu::CrossTranslationUnitContext
&CTU
;
87 const MacroExpansionContext
&MacroExpansions
;
88 llvm::SmallVector
<const PathDiagnosticMacroPiece
*, 0> MacroPieces
;
91 PlistPrinter(const FIDMap
&FM
, const Preprocessor
&PP
,
92 const cross_tu::CrossTranslationUnitContext
&CTU
,
93 const MacroExpansionContext
&MacroExpansions
)
94 : FM(FM
), PP(PP
), CTU(CTU
), MacroExpansions(MacroExpansions
) {}
96 void ReportDiag(raw_ostream
&o
, const PathDiagnosticPiece
& P
) {
97 ReportPiece(o
, P
, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
100 /// Print the expansions of the collected macro pieces.
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
106 void ReportMacroExpansions(raw_ostream
&o
, unsigned indent
);
109 void ReportPiece(raw_ostream
&o
, const PathDiagnosticPiece
&P
,
110 unsigned indent
, unsigned depth
, bool includeControlFlow
,
111 bool isKeyEvent
= false) {
112 switch (P
.getKind()) {
113 case PathDiagnosticPiece::ControlFlow
:
114 if (includeControlFlow
)
115 ReportControlFlow(o
, cast
<PathDiagnosticControlFlowPiece
>(P
), indent
);
117 case PathDiagnosticPiece::Call
:
118 ReportCall(o
, cast
<PathDiagnosticCallPiece
>(P
), indent
,
121 case PathDiagnosticPiece::Event
:
122 ReportEvent(o
, cast
<PathDiagnosticEventPiece
>(P
), indent
, depth
,
125 case PathDiagnosticPiece::Macro
:
126 ReportMacroSubPieces(o
, cast
<PathDiagnosticMacroPiece
>(P
), indent
,
129 case PathDiagnosticPiece::Note
:
130 ReportNote(o
, cast
<PathDiagnosticNotePiece
>(P
), indent
);
132 case PathDiagnosticPiece::PopUp
:
133 ReportPopUp(o
, cast
<PathDiagnosticPopUpPiece
>(P
), indent
);
138 void EmitRanges(raw_ostream
&o
, const ArrayRef
<SourceRange
> Ranges
,
140 void EmitMessage(raw_ostream
&o
, StringRef Message
, unsigned indent
);
141 void EmitFixits(raw_ostream
&o
, ArrayRef
<FixItHint
> fixits
, unsigned indent
);
143 void ReportControlFlow(raw_ostream
&o
,
144 const PathDiagnosticControlFlowPiece
& P
,
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
,
155 void ReportPopUp(raw_ostream
&o
, const PathDiagnosticPopUpPiece
&P
,
159 } // end of anonymous namespace
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
,
165 SmallVectorImpl
<FileID
> &Fids
,
167 llvm::raw_fd_ostream
&o
);
169 static std::optional
<StringRef
> getExpandedMacro(
170 SourceLocation MacroLoc
, const cross_tu::CrossTranslationUnitContext
&CTU
,
171 const MacroExpansionContext
&MacroExpansions
, const SourceManager
&SM
);
173 //===----------------------------------------------------------------------===//
174 // Methods of PlistPrinter.
175 //===----------------------------------------------------------------------===//
177 void PlistPrinter::EmitRanges(raw_ostream
&o
,
178 const ArrayRef
<SourceRange
> Ranges
,
184 Indent(o
, indent
) << "<key>ranges</key>\n";
185 Indent(o
, indent
) << "<array>\n";
188 const SourceManager
&SM
= PP
.getSourceManager();
189 const LangOptions
&LangOpts
= PP
.getLangOpts();
191 for (auto &R
: Ranges
)
193 Lexer::getAsCharRange(SM
.getExpansionRange(R
), SM
, LangOpts
),
196 Indent(o
, indent
) << "</array>\n";
199 void PlistPrinter::EmitMessage(raw_ostream
&o
, StringRef Message
,
202 assert(!Message
.empty());
203 Indent(o
, indent
) << "<key>extended_message</key>\n";
205 EmitString(o
, Message
) << '\n';
207 // Output the short text.
208 // FIXME: Really use a short string.
209 Indent(o
, indent
) << "<key>message</key>\n";
211 EmitString(o
, Message
) << '\n';
214 void PlistPrinter::EmitFixits(raw_ostream
&o
, ArrayRef
<FixItHint
> fixits
,
216 if (fixits
.size() == 0)
219 const SourceManager
&SM
= PP
.getSourceManager();
220 const LangOptions
&LangOpts
= PP
.getLangOpts();
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
),
233 Indent(o
, indent
) << " <key>insert_string</key>";
234 EmitString(o
, fixit
.CodeToInsert
);
236 Indent(o
, indent
) << " </dict>\n";
238 Indent(o
, indent
) << "</array>\n";
241 void PlistPrinter::ReportControlFlow(raw_ostream
&o
,
242 const PathDiagnosticControlFlowPiece
& P
,
245 const SourceManager
&SM
= PP
.getSourceManager();
246 const LangOptions
&LangOpts
= PP
.getLangOpts();
248 Indent(o
, indent
) << "<dict>\n";
251 Indent(o
, indent
) << "<key>kind</key><string>control</string>\n";
254 Indent(o
, indent
) << "<key>edges</key>\n";
256 Indent(o
, indent
) << "<array>\n";
258 for (PathDiagnosticControlFlowPiece::const_iterator I
=P
.begin(), E
=P
.end();
260 Indent(o
, indent
) << "<dict>\n";
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
,
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
,
278 Indent(o
, indent
) << "</dict>\n";
281 Indent(o
, indent
) << "</array>\n";
284 // Output any helper text.
285 const auto &s
= P
.getString();
287 Indent(o
, indent
) << "<key>alternate</key>";
288 EmitString(o
, s
) << '\n';
291 assert(P
.getFixits().size() == 0 &&
292 "Fixits on constrol flow pieces are not implemented yet!");
295 Indent(o
, indent
) << "</dict>\n";
298 void PlistPrinter::ReportEvent(raw_ostream
&o
, const PathDiagnosticEventPiece
& P
,
299 unsigned indent
, unsigned depth
,
302 const SourceManager
&SM
= PP
.getSourceManager();
304 Indent(o
, indent
) << "<dict>\n";
307 Indent(o
, indent
) << "<key>kind</key><string>event</string>\n";
310 Indent(o
, indent
) << "<key>key_event</key><true/>\n";
313 // Output the location.
314 FullSourceLoc L
= P
.getLocation().asLocation();
316 Indent(o
, indent
) << "<key>location</key>\n";
317 EmitLocation(o
, SM
, L
, FM
, indent
);
319 // Output the ranges (if any).
320 ArrayRef
<SourceRange
> Ranges
= P
.getRanges();
321 EmitRanges(o
, Ranges
, indent
);
323 // Output the call depth.
324 Indent(o
, indent
) << "<key>depth</key>";
325 EmitInteger(o
, depth
) << '\n';
328 EmitMessage(o
, P
.getString(), indent
);
330 // Output the fixits.
331 EmitFixits(o
, P
.getFixits(), indent
);
335 Indent(o
, indent
); o
<< "</dict>\n";
338 void PlistPrinter::ReportCall(raw_ostream
&o
, const PathDiagnosticCallPiece
&P
,
342 if (auto callEnter
= P
.getCallEnterEvent())
343 ReportPiece(o
, *callEnter
, indent
, depth
, /*includeControlFlow*/ true,
344 P
.isLastInMainSourceFile());
349 if (auto callEnterWithinCaller
= P
.getCallEnterWithinCallerEvent())
350 ReportPiece(o
, *callEnterWithinCaller
, indent
, depth
,
351 /*includeControlFlow*/ true);
353 for (PathPieces::const_iterator I
= P
.path
.begin(), E
= P
.path
.end();I
!=E
;++I
)
354 ReportPiece(o
, **I
, indent
, depth
, /*includeControlFlow*/ true);
358 if (auto callExit
= P
.getCallExitEvent())
359 ReportPiece(o
, *callExit
, indent
, depth
, /*includeControlFlow*/ true);
361 assert(P
.getFixits().size() == 0 &&
362 "Fixits on call pieces are not implemented yet!");
365 void PlistPrinter::ReportMacroSubPieces(raw_ostream
&o
,
366 const PathDiagnosticMacroPiece
& P
,
367 unsigned indent
, unsigned depth
) {
368 MacroPieces
.push_back(&P
);
370 for (const auto &SubPiece
: P
.subPieces
) {
371 ReportPiece(o
, *SubPiece
, indent
, depth
, /*includeControlFlow*/ false);
374 assert(P
.getFixits().size() == 0 &&
375 "Fixits on constrol flow pieces are not implemented yet!");
378 void PlistPrinter::ReportMacroExpansions(raw_ostream
&o
, unsigned indent
) {
380 for (const PathDiagnosticMacroPiece
*P
: MacroPieces
) {
381 const SourceManager
&SM
= PP
.getSourceManager();
383 SourceLocation MacroExpansionLoc
=
384 P
->getLocation().asLocation().getExpansionLoc();
386 const std::optional
<StringRef
> MacroName
=
387 MacroExpansions
.getOriginalText(MacroExpansionLoc
);
388 const std::optional
<StringRef
> ExpansionText
=
389 getExpandedMacro(MacroExpansionLoc
, CTU
, MacroExpansions
, SM
);
391 if (!MacroName
|| !ExpansionText
)
394 Indent(o
, indent
) << "<dict>\n";
397 // Output the location.
398 FullSourceLoc L
= P
->getLocation().asLocation();
400 Indent(o
, indent
) << "<key>location</key>\n";
401 EmitLocation(o
, SM
, L
, FM
, indent
);
403 // Output the ranges (if any).
404 ArrayRef
<SourceRange
> Ranges
= P
->getRanges();
405 EmitRanges(o
, Ranges
, indent
);
407 // Output the macro name.
408 Indent(o
, indent
) << "<key>name</key>";
409 EmitString(o
, *MacroName
) << '\n';
411 // Output what it expands into.
412 Indent(o
, indent
) << "<key>expansion</key>";
413 EmitString(o
, *ExpansionText
) << '\n';
422 void PlistPrinter::ReportNote(raw_ostream
&o
, const PathDiagnosticNotePiece
& P
,
425 const SourceManager
&SM
= PP
.getSourceManager();
427 Indent(o
, indent
) << "<dict>\n";
430 // Output the location.
431 FullSourceLoc L
= P
.getLocation().asLocation();
433 Indent(o
, indent
) << "<key>location</key>\n";
434 EmitLocation(o
, SM
, L
, FM
, indent
);
436 // Output the ranges (if any).
437 ArrayRef
<SourceRange
> Ranges
= P
.getRanges();
438 EmitRanges(o
, Ranges
, indent
);
441 EmitMessage(o
, P
.getString(), indent
);
443 // Output the fixits.
444 EmitFixits(o
, P
.getFixits(), indent
);
448 Indent(o
, indent
); o
<< "</dict>\n";
451 void PlistPrinter::ReportPopUp(raw_ostream
&o
,
452 const PathDiagnosticPopUpPiece
&P
,
454 const SourceManager
&SM
= PP
.getSourceManager();
456 Indent(o
, indent
) << "<dict>\n";
459 Indent(o
, indent
) << "<key>kind</key><string>pop-up</string>\n";
461 // Output the location.
462 FullSourceLoc L
= P
.getLocation().asLocation();
464 Indent(o
, indent
) << "<key>location</key>\n";
465 EmitLocation(o
, SM
, L
, FM
, indent
);
467 // Output the ranges (if any).
468 ArrayRef
<SourceRange
> Ranges
= P
.getRanges();
469 EmitRanges(o
, Ranges
, indent
);
472 EmitMessage(o
, P
.getString(), indent
);
474 assert(P
.getFixits().size() == 0 &&
475 "Fixits on pop-up pieces are not implemented yet!");
479 Indent(o
, indent
) << "</dict>\n";
482 //===----------------------------------------------------------------------===//
483 // Static function definitions.
484 //===----------------------------------------------------------------------===//
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
,
490 SmallVectorImpl
<FileID
> &Fids
,
492 llvm::raw_fd_ostream
&o
) {
493 unsigned IndentLevel
= InputIndentLevel
;
495 Indent(o
, IndentLevel
) << "<key>ExecutedLines</key>\n";
496 Indent(o
, IndentLevel
) << "<dict>\n";
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";
506 for (unsigned LineNo
: Lines
) {
507 Indent(o
, IndentLevel
);
508 EmitInteger(o
, LineNo
) << "\n";
511 Indent(o
, IndentLevel
) << "</array>\n";
514 Indent(o
, IndentLevel
) << "</dict>\n";
516 assert(IndentLevel
== InputIndentLevel
);
519 //===----------------------------------------------------------------------===//
520 // Methods of PlistDiagnostics.
521 //===----------------------------------------------------------------------===//
523 PlistDiagnostics::PlistDiagnostics(
524 PathDiagnosticConsumerOptions DiagOpts
, const std::string
&output
,
525 const Preprocessor
&PP
, const cross_tu::CrossTranslationUnitContext
&CTU
,
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.
534 void ento::createPlistDiagnosticConsumer(
535 PathDiagnosticConsumerOptions DiagOpts
, PathDiagnosticConsumers
&C
,
536 const std::string
&OutputFile
, const Preprocessor
&PP
,
537 const cross_tu::CrossTranslationUnitContext
&CTU
,
538 const MacroExpansionContext
&MacroExpansions
) {
540 // TODO: Emit an error here.
541 if (OutputFile
.empty())
544 C
.push_back(new PlistDiagnostics(DiagOpts
, OutputFile
, PP
, CTU
,
546 /*supportsMultipleFiles=*/false));
547 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts
), C
, OutputFile
,
548 PP
, CTU
, MacroExpansions
);
551 void ento::createPlistMultiFileDiagnosticConsumer(
552 PathDiagnosticConsumerOptions DiagOpts
, PathDiagnosticConsumers
&C
,
553 const std::string
&OutputFile
, const Preprocessor
&PP
,
554 const cross_tu::CrossTranslationUnitContext
&CTU
,
555 const MacroExpansionContext
&MacroExpansions
) {
557 // TODO: Emit an error here.
558 if (OutputFile
.empty())
561 C
.push_back(new PlistDiagnostics(DiagOpts
, OutputFile
, PP
, CTU
,
563 /*supportsMultipleFiles=*/true));
564 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts
), C
, OutputFile
,
565 PP
, CTU
, MacroExpansions
);
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
;
575 "PathDiagnostic is not partitioned so that notes precede the rest");
577 PathPieces::const_iterator FirstNonNote
= std::partition_point(
578 Path
.begin(), Path
.end(), [](const PathDiagnosticPieceRef
&E
) {
579 return E
->getKind() == PathDiagnosticPiece::Note
;
582 PathPieces::const_iterator I
= Path
.begin();
584 if (FirstNonNote
!= Path
.begin()) {
585 o
<< " <key>notes</key>\n"
588 for (; I
!= FirstNonNote
; ++I
)
589 Printer
.ReportDiag(o
, **I
);
594 o
<< " <key>path</key>\n";
598 for (const auto &Piece
: llvm::make_range(I
, Path
.end()))
599 Printer
.ReportDiag(o
, *Piece
);
603 if (!DiagOpts
.ShouldDisplayMacroExpansions
)
606 o
<< " <key>macro_expansions</key>\n"
608 Printer
.ReportMacroExpansions(o
, /* indent */ 4);
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.
618 SmallVector
<FileID
, 10> Fids
;
619 const SourceManager
& SM
= PP
.getSourceManager();
620 const LangOptions
&LangOpts
= PP
.getLangOpts();
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());
631 for (const PathDiagnostic
*D
: Diags
) {
633 SmallVector
<const PathPieces
*, 5> WorkList
;
634 WorkList
.push_back(&D
->path
);
636 while (!WorkList
.empty()) {
637 const PathPieces
&Path
= *WorkList
.pop_back_val();
639 for (const auto &Iter
: Path
) {
640 const PathDiagnosticPiece
&Piece
= *Iter
;
643 if (const PathDiagnosticCallPiece
*Call
=
644 dyn_cast
<PathDiagnosticCallPiece
>(&Piece
)) {
645 if (auto CallEnterWithin
= Call
->getCallEnterWithinCallerEvent())
646 AddPieceFID(*CallEnterWithin
);
648 if (auto CallEnterEvent
= Call
->getCallEnterEvent())
649 AddPieceFID(*CallEnterEvent
);
651 WorkList
.push_back(&Call
->path
);
652 } else if (const PathDiagnosticMacroPiece
*Macro
=
653 dyn_cast
<PathDiagnosticMacroPiece
>(&Piece
)) {
654 WorkList
.push_back(&Macro
->subPieces
);
662 llvm::raw_fd_ostream
o(OutputFile
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
664 llvm::errs() << "warning: could not create file: " << EC
.message() << '\n';
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
675 " <key>clang_version</key>\n";
676 EmitString(o
, getClangFullVersion()) << '\n';
677 o
<< " <key>diagnostics</key>\n"
680 for (std::vector
<const PathDiagnostic
*>::iterator DI
=Diags
.begin(),
681 DE
= Diags
.end(); DI
!=DE
; ++DI
) {
685 const PathDiagnostic
*D
= *DI
;
686 printBugPath(o
, FM
, D
->path
);
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';
698 o
<< " <!-- This hash is experimental and going to change! -->\n";
699 o
<< " <key>issue_hash_content_of_line_in_context</key>";
700 PathDiagnosticLocation UPDLoc
= D
->getUniqueingLoc();
701 FullSourceLoc
L(SM
.getExpansionLoc(UPDLoc
.isValid()
702 ? UPDLoc
.asLocation()
703 : D
->getLocation().asLocation()),
705 const Decl
*DeclWithIssue
= D
->getDeclWithIssue();
706 EmitString(o
, getIssueHash(L
, D
->getCheckerName(), D
->getBugType(),
707 DeclWithIssue
, LangOpts
))
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
)) {
716 switch (ND
->getKind()) {
717 case Decl::CXXRecord
:
718 declKind
= "C++ class";
720 case Decl::CXXMethod
:
721 declKind
= "C++ method";
723 case Decl::ObjCMethod
:
724 declKind
= "Objective-C method";
727 declKind
= "function";
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';
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()) {
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()) {
752 D
->getUniqueingDecl()->getBody()->getBeginLoc()),
754 o
<< " <key>issue_hash_function_offset</key><string>"
755 << L
.getExpansionLineNumber() - UFunL
.getExpansionLineNumber()
758 // Otherwise, use the location on which the bug is reported.
760 FullSourceLoc
FunL(SM
.getExpansionLoc(Body
->getBeginLoc()), SM
);
761 o
<< " <key>issue_hash_function_offset</key><string>"
762 << L
.getExpansionLineNumber() - FunL
.getExpansionLineNumber()
770 // Output the location of the bug.
771 o
<< " <key>location</key>\n";
772 EmitLocation(o
, SM
, D
->getLocation().asLocation(), FM
, 2);
774 // Output the diagnostic to the sub-diagnostic client, if any.
775 if (!filesMade
->empty()) {
777 PDFileEntry::ConsumerFiles
*files
= filesMade
->getFiles(*D
);
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()) {
787 o
<< " <key>" << lastName
<< "_files</key>\n";
790 o
<< " <string>" << CI
->second
<< "</string>\n";
796 printCoverage(D
, /*IndentLevel=*/2, Fids
, FM
, o
);
798 // Close up the entry.
804 o
<< " <key>files</key>\n"
806 for (FileID FID
: Fids
)
807 EmitString(o
<< " ", SM
.getFileEntryRefForID(FID
)->getName()) << '\n';
810 if (llvm::AreStatisticsEnabled() && DiagOpts
.ShouldSerializeStats
) {
811 o
<< " <key>statistics</key>\n";
813 llvm::raw_string_ostream
os(stats
);
814 llvm::PrintStatisticsJSON(os
);
816 EmitString(o
, html::EscapeText(stats
)) << '\n';
820 o
<< "</dict>\n</plist>\n";
823 //===----------------------------------------------------------------------===//
824 // Definitions of helper functions and methods for expanding macros.
825 //===----------------------------------------------------------------------===//
827 static std::optional
<StringRef
>
828 getExpandedMacro(SourceLocation MacroExpansionLoc
,
829 const cross_tu::CrossTranslationUnitContext
&CTU
,
830 const MacroExpansionContext
&MacroExpansions
,
831 const SourceManager
&SM
) {
832 if (auto CTUMacroExpCtx
=
833 CTU
.getMacroExpansionContextForSourceLocation(MacroExpansionLoc
)) {
834 return CTUMacroExpCtx
->getExpandedText(MacroExpansionLoc
);
836 return MacroExpansions
.getExpandedText(MacroExpansionLoc
);