clang: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
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
31#include
32
33using namespace clang;
34using namespace ento;
35using namespace markup;
36
37
38
39
40
41
42namespace {
45 const std::string OutputFile;
49 const bool SupportsCrossFileDiagnostics;
50
51 void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
53
54 public:
56 const std::string &OutputFile, const Preprocessor &PP,
59 bool supportsMultipleFiles);
60
61 ~PlistDiagnostics() override {}
62
64 FilesMade *filesMade) override;
65
66 StringRef getName() const override {
67 return "PlistDiagnostics";
68 }
69
72 }
75 return SupportsCrossFileDiagnostics;
76 }
77 };
78}
79
80namespace {
81
82
83class PlistPrinter {
89
90public:
94 : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
95
97 ReportPiece(o, P, 4, 0, true);
98 }
99
100
101
102
103
104
105
106 void ReportMacroExpansions(raw_ostream &o, unsigned indent);
107
108private:
110 unsigned indent, unsigned depth, bool includeControlFlow,
111 bool isKeyEvent = false) {
112 switch (P.getKind()) {
114 if (includeControlFlow)
115 ReportControlFlow(o, cast(P), indent);
116 break;
118 ReportCall(o, cast(P), indent,
119 depth);
120 break;
122 ReportEvent(o, cast(P), indent, depth,
123 isKeyEvent);
124 break;
126 ReportMacroSubPieces(o, cast(P), indent,
127 depth);
128 break;
130 ReportNote(o, cast(P), indent);
131 break;
133 ReportPopUp(o, cast(P), indent);
134 break;
135 }
136 }
137
139 unsigned indent);
140 void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
141 void EmitFixits(raw_ostream &o, ArrayRef fixits, unsigned indent);
142
143 void ReportControlFlow(raw_ostream &o,
145 unsigned indent);
147 unsigned indent, unsigned depth, bool isKeyEvent = false);
149 unsigned indent, unsigned depth);
151 unsigned indent, unsigned depth);
153 unsigned indent);
154
156 unsigned indent);
157};
158
159}
160
161
162
164 unsigned InputIndentLevel,
167 llvm::raw_fd_ostream &o);
168
172
173
174
175
176
177void PlistPrinter::EmitRanges(raw_ostream &o,
179 unsigned indent) {
180
181 if (Ranges.empty())
182 return;
183
184 Indent(o, indent) << "ranges\n";
185 Indent(o, indent) << "\n";
186 ++indent;
187
190
191 for (auto &R : Ranges)
194 FM, indent + 1);
195 --indent;
196 Indent(o, indent) << "\n";
197}
198
199void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
200 unsigned indent) {
201
202 assert(!Message.empty());
203 Indent(o, indent) << "extended_message\n";
204 Indent(o, indent);
206
207
208
209 Indent(o, indent) << "message\n";
210 Indent(o, indent);
212}
213
215 unsigned indent) {
216 if (fixits.size() == 0)
217 return;
218
221
222 Indent(o, indent) << "fixits\n";
223 Indent(o, indent) << "\n";
224 for (const auto &fixit : fixits) {
225 assert(!fixit.isNull());
226
227 assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");
228 assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");
229 Indent(o, indent) << " \n";
230 Indent(o, indent) << " remove_range\n";
232 FM, indent + 2);
233 Indent(o, indent) << " insert_string";
235 o << "\n";
236 Indent(o, indent) << " \n";
237 }
238 Indent(o, indent) << "\n";
239}
240
241void PlistPrinter::ReportControlFlow(raw_ostream &o,
243 unsigned indent) {
244
247
248 Indent(o, indent) << "\n";
249 ++indent;
250
251 Indent(o, indent) << "kindcontrol\n";
252
253
254 Indent(o, indent) << "edges\n";
255 ++indent;
256 Indent(o, indent) << "\n";
257 ++indent;
259 I!=E; ++I) {
260 Indent(o, indent) << "\n";
261 ++indent;
262
263
264
265
266 Indent(o, indent) << "start\n";
268 SM.getExpansionLoc(I->getStart().asRange().getBegin()));
270 indent + 1);
271
272 Indent(o, indent) << "end\n";
273 SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
275 indent + 1);
276
277 --indent;
278 Indent(o, indent) << "\n";
279 }
280 --indent;
281 Indent(o, indent) << "\n";
282 --indent;
283
284
285 const auto &s = P.getString();
286 if (.empty()) {
287 Indent(o, indent) << "alternate";
289 }
290
291 assert(P.getFixits().size() == 0 &&
292 "Fixits on constrol flow pieces are not implemented yet!");
293
294 --indent;
295 Indent(o, indent) << "\n";
296}
297
299 unsigned indent, unsigned depth,
300 bool isKeyEvent) {
301
303
304 Indent(o, indent) << "\n";
305 ++indent;
306
307 Indent(o, indent) << "kindevent\n";
308
309 if (isKeyEvent) {
310 Indent(o, indent) << "key_event\n";
311 }
312
313
315
316 Indent(o, indent) << "location\n";
318
319
321 EmitRanges(o, Ranges, indent);
322
323
324 Indent(o, indent) << "depth";
326
327
328 EmitMessage(o, P.getString(), indent);
329
330
331 EmitFixits(o, P.getFixits(), indent);
332
333
334 --indent;
335 Indent(o, indent); o << "\n";
336}
337
339 unsigned indent,
340 unsigned depth) {
341
342 if (auto callEnter = P.getCallEnterEvent())
343 ReportPiece(o, *callEnter, indent, depth, true,
344 P.isLastInMainSourceFile());
345
346
347 ++depth;
348
349 if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
350 ReportPiece(o, *callEnterWithinCaller, indent, depth,
351 true);
352
353 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
354 ReportPiece(o, **I, indent, depth, true);
355
356 --depth;
357
358 if (auto callExit = P.getCallExitEvent())
359 ReportPiece(o, *callExit, indent, depth, true);
360
361 assert(P.getFixits().size() == 0 &&
362 "Fixits on call pieces are not implemented yet!");
363}
364
365void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
367 unsigned indent, unsigned depth) {
368 MacroPieces.push_back(&P);
369
370 for (const auto &SubPiece : P.subPieces) {
371 ReportPiece(o, *SubPiece, indent, depth, false);
372 }
373
374 assert(P.getFixits().size() == 0 &&
375 "Fixits on constrol flow pieces are not implemented yet!");
376}
377
378void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
379
382
384 P->getLocation().asLocation().getExpansionLoc();
385
386 const std::optional MacroName =
388 const std::optional ExpansionText =
390
391 if (!MacroName || !ExpansionText)
392 continue;
393
394 Indent(o, indent) << "\n";
395 ++indent;
396
397
399
400 Indent(o, indent) << "location\n";
402
403
405 EmitRanges(o, Ranges, indent);
406
407
408 Indent(o, indent) << "name";
410
411
412 Indent(o, indent) << "expansion";
413 EmitString(o, *ExpansionText) << '\n';
414
415
416 --indent;
417 Indent(o, indent);
418 o << "\n";
419 }
420}
421
423 unsigned indent) {
424
426
427 Indent(o, indent) << "\n";
428 ++indent;
429
430
432
433 Indent(o, indent) << "location\n";
435
436
438 EmitRanges(o, Ranges, indent);
439
440
441 EmitMessage(o, P.getString(), indent);
442
443
444 EmitFixits(o, P.getFixits(), indent);
445
446
447 --indent;
448 Indent(o, indent); o << "\n";
449}
450
451void PlistPrinter::ReportPopUp(raw_ostream &o,
453 unsigned indent) {
455
456 Indent(o, indent) << "\n";
457 ++indent;
458
459 Indent(o, indent) << "kindpop-up\n";
460
461
463
464 Indent(o, indent) << "location\n";
466
467
469 EmitRanges(o, Ranges, indent);
470
471
472 EmitMessage(o, P.getString(), indent);
473
474 assert(P.getFixits().size() == 0 &&
475 "Fixits on pop-up pieces are not implemented yet!");
476
477
478 --indent;
479 Indent(o, indent) << "\n";
480}
481
482
483
484
485
486
487
489 unsigned InputIndentLevel,
492 llvm::raw_fd_ostream &o) {
493 unsigned IndentLevel = InputIndentLevel;
494
495 Indent(o, IndentLevel) << "ExecutedLines\n";
496 Indent(o, IndentLevel) << "\n";
497 IndentLevel++;
498
499
501 for (const auto &[FID, Lines] : ExecutedLines) {
502 unsigned FileKey = AddFID(FM, Fids, FID);
503 Indent(o, IndentLevel) << "" << FileKey << "\n";
504 Indent(o, IndentLevel) << "\n";
505 IndentLevel++;
506 for (unsigned LineNo : Lines) {
507 Indent(o, IndentLevel);
509 }
510 IndentLevel--;
511 Indent(o, IndentLevel) << "\n";
512 }
513 IndentLevel--;
514 Indent(o, IndentLevel) << "\n";
515
516 assert(IndentLevel == InputIndentLevel);
517}
518
519
520
521
522
523PlistDiagnostics::PlistDiagnostics(
527 : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
528 MacroExpansions(MacroExpansions),
529 SupportsCrossFileDiagnostics(supportsMultipleFiles) {
530
531 (void)this->CTU;
532}
533
534void ento::createPlistDiagnosticConsumer(
536 const std::string &OutputFile, const Preprocessor &PP,
539
540
541 if (OutputFile.empty())
542 return;
543
544 C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
545 MacroExpansions,
546 false));
547 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
548 PP, CTU, MacroExpansions);
549}
550
551void ento::createPlistMultiFileDiagnosticConsumer(
553 const std::string &OutputFile, const Preprocessor &PP,
556
557
558 if (OutputFile.empty())
559 return;
560
561 C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
562 MacroExpansions,
563 true));
564 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
565 PP, CTU, MacroExpansions);
566}
567
568void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
570 PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
571 assert(std::is_partitioned(Path.begin(), Path.end(),
573 return E->getKind() == PathDiagnosticPiece::Note;
574 }) &&
575 "PathDiagnostic is not partitioned so that notes precede the rest");
576
577 PathPieces::const_iterator FirstNonNote = std::partition_point(
579 return E->getKind() == PathDiagnosticPiece::Note;
580 });
581
582 PathPieces::const_iterator I = Path.begin();
583
584 if (FirstNonNote != Path.begin()) {
585 o << " notes\n"
586 " \n";
587
588 for (; I != FirstNonNote; ++I)
589 Printer.ReportDiag(o, **I);
590
591 o << " \n";
592 }
593
594 o << " path\n";
595
596 o << " \n";
597
598 for (const auto &Piece : llvm::make_range(I, Path.end()))
599 Printer.ReportDiag(o, *Piece);
600
601 o << " \n";
602
604 return;
605
606 o << " macro_expansions\n"
607 " \n";
608 Printer.ReportMacroExpansions(o, 4);
609 o << " \n";
610}
611
612void PlistDiagnostics::FlushDiagnosticsImpl(
613 std::vector<const PathDiagnostic *> &Diags,
614 FilesMade *filesMade) {
615
616
621
623 AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
628 }
629 };
630
632
635
638
639 for (const auto &Iter : Path) {
641 AddPieceFID(Piece);
642
644 dyn_cast(&Piece)) {
645 if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
646 AddPieceFID(*CallEnterWithin);
647
648 if (auto CallEnterEvent = Call->getCallEnterEvent())
649 AddPieceFID(*CallEnterEvent);
650
653 dyn_cast(&Piece)) {
655 }
656 }
657 }
658 }
659
660
661 std::error_code EC;
662 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
663 if (EC) {
664 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
665 return;
666 }
667
669
670
671
672
673
674 o << "\n" <<
675 " clang_version\n";
677 o << " diagnostics\n"
678 " \n";
679
680 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
681 DE = Diags.end(); DI!=DE; ++DI) {
682
683 o << " \n";
684
686 printBugPath(o, FM, D->path);
687
688
689 o << " description";
690 EmitString(o, D->getShortDescription()) << '\n';
691 o << " category";
693 o << " type";
695 o << " check_name";
696 EmitString(o, D->getCheckerName()) << '\n';
697
698 o << " \n";
699 o << " issue_hash_content_of_line_in_context";
705 const Decl *DeclWithIssue = D->getDeclWithIssue();
707 DeclWithIssue, LangOpts))
708 << '\n';
709
710
711
712 if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
713
714 if (const NamedDecl *ND = dyn_cast(DeclWithIssue)) {
715 StringRef declKind;
716 switch (ND->getKind()) {
717 case Decl::CXXRecord:
718 declKind = "C++ class";
719 break;
720 case Decl::CXXMethod:
721 declKind = "C++ method";
722 break;
723 case Decl::ObjCMethod:
724 declKind = "Objective-C method";
725 break;
726 case Decl::Function:
727 declKind = "function";
728 break;
729 default:
730 break;
731 }
732 if (!declKind.empty()) {
733 const std::string &declName = ND->getDeclName().getAsString();
734 o << " issue_context_kind";
736 o << " issue_context";
738 }
739
740
741
742 if (const Stmt *Body = DeclWithIssue->getBody()) {
743
744
745
746
747
748
751 SM.getExpansionLoc(
754 o << " issue_hash_function_offset"
756 << "\n";
757
758
759 } else {
761 o << " issue_hash_function_offset"
763 << "\n";
764 }
765
766 }
767 }
768 }
769
770
771 o << " location\n";
773
774
775 if (!filesMade->empty()) {
776 StringRef lastName;
777 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
778 if (files) {
779 for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
780 CE = files->end(); CI != CE; ++CI) {
781 StringRef newName = CI->first;
782 if (newName != lastName) {
783 if (!lastName.empty()) {
784 o << " \n";
785 }
786 lastName = newName;
787 o << " " << lastName << "_files\n";
788 o << " \n";
789 }
790 o << " " << CI->second << "\n";
791 }
792 o << " \n";
793 }
794 }
795
797
798
799 o << " \n";
800 }
801
802 o << " \n";
803
804 o << " files\n"
805 " \n";
806 for (FileID FID : Fids)
807 EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n';
808 o << " \n";
809
811 o << " statistics\n";
812 std::string stats;
813 llvm::raw_string_ostream os(stats);
814 llvm::PrintStatisticsJSON(os);
816 }
817
818
819 o << "\n\n";
820}
821
822
823
824
825
826static std::optional
831 if (auto CTUMacroExpCtx =
833 return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
834 }
835 return MacroExpansions.getExpandedText(MacroExpansionLoc);
836}
Defines the clang::FileManager interface and associated types.
static void printCoverage(const PathDiagnostic *D, unsigned InputIndentLevel, SmallVectorImpl< FileID > &Fids, FIDMap &FM, llvm::raw_fd_ostream &o)
Print coverage information to output stream o.
static std::optional< StringRef > getExpandedMacro(SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, const SourceManager &SM)
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Defines version macros and version-related utility functions for Clang.
__device__ __2f16 float __ockl_bool s
Decl - This represents one declaration (or definition), e.g.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
SourceLocation getLocation() const
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
A SourceLocation and its associated SourceManager.
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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.
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
std::optional< StringRef > getExpandedText(SourceLocation MacroExpansionLoc) const
std::optional< StringRef > getOriginalText(SourceLocation MacroExpansionLoc) const
This represents a decl that may have a name.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
SourceManager & getSourceManager() const
const LangOptions & getLangOpts() const
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
SourceLocation getBeginLoc() const LLVM_READONLY
This class is used for tools that requires cross translation unit capability.
std::optional< clang::MacroExpansionContext > getMacroExpansionContextForSourceLocation(const clang::SourceLocation &ToLoc) const
Returns the MacroExpansionContext for the imported TU to which the given source-location corresponds.
@ Extensive
Used for plist output, used for "arrows" generation.
virtual bool supportsLogicalOpControlFlow() const
virtual void FlushDiagnosticsImpl(std::vector< const PathDiagnostic * > &Diags, FilesMade *filesMade)=0
virtual bool supportsCrossFileDiagnostics() const
Return true if the PathDiagnosticConsumer supports individual PathDiagnostics that span multiple file...
virtual StringRef getName() const =0
virtual PathGenerationScheme getGenerationScheme() const
std::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
FullSourceLoc asLocation() const
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
A Range represents the closed range [from, to].
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
void EscapeText(Rewriter &R, FileID FID, bool EscapeSpaces=false, bool ReplaceTabs=false)
EscapeText - HTMLize a specified file so that special characters are are translated so that they are ...
void EmitRange(raw_ostream &o, const SourceManager &SM, CharSourceRange R, const FIDMap &FM, unsigned indent)
raw_ostream & EmitString(raw_ostream &o, StringRef s)
unsigned AddFID(FIDMap &FIDs, SmallVectorImpl< FileID > &V, FileID FID)
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
llvm::DenseMap< FileID, unsigned > FIDMap
raw_ostream & EmitPlistHeader(raw_ostream &o)
void EmitLocation(raw_ostream &o, const SourceManager &SM, SourceLocation L, const FIDMap &FM, unsigned indent)
The JSON file list parser is used to communicate input to InstallAPI.
llvm::SmallString< 32 > getIssueHash(const FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef WarningMessage, const Decl *IssueDecl, const LangOptions &LangOpts)
Returns an opaque identifier for a diagnostic.
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
These options tweak the behavior of path diangostic consumers.
bool ShouldDisplayMacroExpansions
Whether to include additional information about macro expansions with the diagnostics,...
bool ShouldSerializeStats
Whether to include LLVM statistics of the process in the diagnostic.