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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp