26#include "llvm/ADT/SmallVector.h"
27#include "llvm/ADT/Statistic.h"
28#include "llvm/Support/FileSystem.h"
43 PathDiagnosticConsumerOptions DiagOpts;
44 const std::string OutputFile;
45 const Preprocessor &PP;
46 const cross_tu::CrossTranslationUnitContext &CTU;
47 const MacroExpansionContext &MacroExpansions;
48 const bool SupportsCrossFileDiagnostics;
50 void printBugPath(llvm::raw_ostream &o,
const FIDMap &FM,
51 const PathPieces &Path);
54 PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
55 const std::string &OutputFile,
const Preprocessor &PP,
56 const cross_tu::CrossTranslationUnitContext &CTU,
57 const MacroExpansionContext &MacroExpansions,
58 bool supportsMultipleFiles);
60 ~PlistDiagnostics()
override {}
62 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
63 FilesMade *filesMade)
override;
65 StringRef
getName()
const override {
66 return "PlistDiagnostics";
69 PathGenerationScheme getGenerationScheme()
const override {
72 bool supportsLogicalOpControlFlow()
const override {
return true; }
73 bool supportsCrossFileDiagnostics()
const override {
74 return SupportsCrossFileDiagnostics;
84 const Preprocessor &PP;
85 const cross_tu::CrossTranslationUnitContext &CTU;
86 const MacroExpansionContext &MacroExpansions;
87 llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
90 PlistPrinter(
const FIDMap &FM,
const Preprocessor &PP,
91 const cross_tu::CrossTranslationUnitContext &CTU,
92 const MacroExpansionContext &MacroExpansions)
93 : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
95 void ReportDiag(raw_ostream &o,
const PathDiagnosticPiece& P) {
96 ReportPiece(o, P, 4, 0,
true);
105 void ReportMacroExpansions(raw_ostream &o,
unsigned indent);
108 void ReportPiece(raw_ostream &o,
const PathDiagnosticPiece &P,
109 unsigned indent,
unsigned depth,
bool includeControlFlow,
110 bool isKeyEvent =
false) {
113 if (includeControlFlow)
137 void EmitRanges(raw_ostream &o,
const ArrayRef<SourceRange> Ranges,
139 void EmitMessage(raw_ostream &o, StringRef Message,
unsigned indent);
140 void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
unsigned indent);
142 void ReportControlFlow(raw_ostream &o,
143 const PathDiagnosticControlFlowPiece& P,
145 void ReportEvent(raw_ostream &o,
const PathDiagnosticEventPiece& P,
146 unsigned indent,
unsigned depth,
bool isKeyEvent =
false);
147 void ReportCall(raw_ostream &o,
const PathDiagnosticCallPiece &P,
148 unsigned indent,
unsigned depth);
149 void ReportMacroSubPieces(raw_ostream &o,
const PathDiagnosticMacroPiece& P,
150 unsigned indent,
unsigned depth);
151 void ReportNote(raw_ostream &o,
const PathDiagnosticNotePiece& P,
154 void ReportPopUp(raw_ostream &o,
const PathDiagnosticPopUpPiece &P,
163 unsigned InputIndentLevel,
166 llvm::raw_fd_ostream &o);
176void PlistPrinter::EmitRanges(raw_ostream &o,
183 Indent(o, indent) <<
"<key>ranges</key>\n";
184 Indent(o, indent) <<
"<array>\n";
190 for (
auto &R : Ranges)
195 Indent(o, indent) <<
"</array>\n";
198void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
202 Indent(o, indent) <<
"<key>extended_message</key>\n";
208 Indent(o, indent) <<
"<key>message</key>\n";
213void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
215 if (fixits.size() == 0)
221 Indent(o, indent) <<
"<key>fixits</key>\n";
222 Indent(o, indent) <<
"<array>\n";
223 for (
const auto &fixit : fixits) {
224 assert(!fixit.isNull());
226 assert(!fixit.InsertFromRange.isValid() &&
"Not implemented yet!");
227 assert(!fixit.BeforePreviousInsertions &&
"Not implemented yet!");
228 Indent(o, indent) <<
" <dict>\n";
229 Indent(o, indent) <<
" <key>remove_range</key>\n";
232 Indent(o, indent) <<
" <key>insert_string</key>";
235 Indent(o, indent) <<
" </dict>\n";
237 Indent(o, indent) <<
"</array>\n";
240void PlistPrinter::ReportControlFlow(raw_ostream &o,
241 const PathDiagnosticControlFlowPiece& P,
247 Indent(o, indent) <<
"<dict>\n";
250 Indent(o, indent) <<
"<key>kind</key><string>control</string>\n";
253 Indent(o, indent) <<
"<key>edges</key>\n";
255 Indent(o, indent) <<
"<array>\n";
259 Indent(o, indent) <<
"<dict>\n";
265 Indent(o, indent) <<
"<key>start</key>\n";
266 SourceRange StartEdge(
271 Indent(o, indent) <<
"<key>end</key>\n";
272 SourceRange EndEdge(SM.
getExpansionLoc(I->getEnd().asRange().getBegin()));
277 Indent(o, indent) <<
"</dict>\n";
280 Indent(o, indent) <<
"</array>\n";
286 Indent(o, indent) <<
"<key>alternate</key>";
291 "Fixits on constrol flow pieces are not implemented yet!");
294 Indent(o, indent) <<
"</dict>\n";
297void PlistPrinter::ReportEvent(raw_ostream &o,
const PathDiagnosticEventPiece& P,
298 unsigned indent,
unsigned depth,
303 Indent(o, indent) <<
"<dict>\n";
306 Indent(o, indent) <<
"<key>kind</key><string>event</string>\n";
309 Indent(o, indent) <<
"<key>key_event</key><true/>\n";
315 Indent(o, indent) <<
"<key>location</key>\n";
319 ArrayRef<SourceRange> Ranges = P.
getRanges();
320 EmitRanges(o, Ranges, indent);
323 Indent(o, indent) <<
"<key>depth</key>";
334 Indent(o, indent); o <<
"</dict>\n";
337void PlistPrinter::ReportCall(raw_ostream &o,
const PathDiagnosticCallPiece &P,
342 ReportPiece(o, *callEnter, indent, depth,
true,
349 ReportPiece(o, *callEnterWithinCaller, indent, depth,
352 for (PathPieces::const_iterator I = P.
path.begin(), E = P.
path.end();I!=E;++I)
353 ReportPiece(o, **I, indent, depth,
true);
358 ReportPiece(o, *callExit, indent, depth,
true);
361 "Fixits on call pieces are not implemented yet!");
364void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
365 const PathDiagnosticMacroPiece& P,
366 unsigned indent,
unsigned depth) {
367 MacroPieces.push_back(&P);
369 for (
const auto &SubPiece : P.
subPieces) {
370 ReportPiece(o, *SubPiece, indent, depth,
false);
374 "Fixits on constrol flow pieces are not implemented yet!");
377void PlistPrinter::ReportMacroExpansions(raw_ostream &o,
unsigned indent) {
379 for (
const PathDiagnosticMacroPiece *P : MacroPieces) {
382 SourceLocation MacroExpansionLoc =
385 const std::optional<StringRef> MacroName =
387 const std::optional<StringRef> ExpansionText =
390 if (!MacroName || !ExpansionText)
393 Indent(o, indent) <<
"<dict>\n";
399 Indent(o, indent) <<
"<key>location</key>\n";
403 ArrayRef<SourceRange> Ranges = P->
getRanges();
404 EmitRanges(o, Ranges, indent);
407 Indent(o, indent) <<
"<key>name</key>";
411 Indent(o, indent) <<
"<key>expansion</key>";
421void PlistPrinter::ReportNote(raw_ostream &o,
const PathDiagnosticNotePiece& P,
426 Indent(o, indent) <<
"<dict>\n";
432 Indent(o, indent) <<
"<key>location</key>\n";
436 ArrayRef<SourceRange> Ranges = P.
getRanges();
437 EmitRanges(o, Ranges, indent);
447 Indent(o, indent); o <<
"</dict>\n";
450void PlistPrinter::ReportPopUp(raw_ostream &o,
451 const PathDiagnosticPopUpPiece &P,
455 Indent(o, indent) <<
"<dict>\n";
458 Indent(o, indent) <<
"<key>kind</key><string>pop-up</string>\n";
463 Indent(o, indent) <<
"<key>location</key>\n";
467 ArrayRef<SourceRange> Ranges = P.
getRanges();
468 EmitRanges(o, Ranges, indent);
474 "Fixits on pop-up pieces are not implemented yet!");
478 Indent(o, indent) <<
"</dict>\n";
488 unsigned InputIndentLevel,
491 llvm::raw_fd_ostream &o) {
500 for (
const auto &[FID, Lines] : ExecutedLines) {
501 unsigned FileKey =
AddFID(FM, Fids, FID);
505 for (
unsigned LineNo : Lines) {
522PlistDiagnostics::PlistDiagnostics(
523 PathDiagnosticConsumerOptions DiagOpts,
const std::string &output,
524 const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU,
525 const MacroExpansionContext &MacroExpansions,
bool supportsMultipleFiles)
526 : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
527 MacroExpansions(MacroExpansions),
528 SupportsCrossFileDiagnostics(supportsMultipleFiles) {
542 if (OutputFile.empty())
545 C.push_back(std::make_unique<PlistDiagnostics>(
546 DiagOpts, OutputFile, PP, CTU, MacroExpansions, SupportsMultipleFiles));
549void ento::createPlistDiagnosticConsumer(
558 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
559 PP, CTU, MacroExpansions);
562void ento::createPlistMultiFileDiagnosticConsumer(
572 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
573 PP, CTU, MacroExpansions);
576void PlistDiagnostics::printBugPath(llvm::raw_ostream &o,
const FIDMap &FM,
578 PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
579 assert(std::is_partitioned(Path.begin(), Path.end(),
581 return E->getKind() == PathDiagnosticPiece::Note;
583 "PathDiagnostic is not partitioned so that notes precede the rest");
585 PathPieces::const_iterator FirstNonNote =
590 PathPieces::const_iterator I = Path.begin();
592 if (FirstNonNote != Path.begin()) {
593 o <<
" <key>notes</key>\n"
596 for (; I != FirstNonNote; ++I)
597 Printer.ReportDiag(o, **I);
602 o <<
" <key>path</key>\n";
606 for (
const auto &Piece : llvm::make_range(I, Path.end()))
607 Printer.ReportDiag(o, *Piece);
614 o <<
" <key>macro_expansions</key>\n"
616 Printer.ReportMacroExpansions(o, 4);
620void PlistDiagnostics::FlushDiagnosticsImpl(
621 std::vector<const PathDiagnostic *> &Diags,
622 FilesMade *filesMade) {
626 SmallVector<FileID, 10> Fids;
630 auto AddPieceFID = [&FM, &Fids, &SM](
const PathDiagnosticPiece &Piece) {
631 AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
632 ArrayRef<SourceRange> Ranges = Piece.getRanges();
633 for (
const SourceRange &Range : Ranges) {
639 for (
const PathDiagnostic *D : Diags) {
641 SmallVector<const PathPieces *, 5> WorkList;
642 WorkList.push_back(&D->path);
644 while (!WorkList.empty()) {
645 const PathPieces &Path = *WorkList.pop_back_val();
647 for (
const auto &Iter : Path) {
648 const PathDiagnosticPiece &Piece = *Iter;
651 if (
const PathDiagnosticCallPiece *
Call =
652 dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
653 if (
auto CallEnterWithin =
Call->getCallEnterWithinCallerEvent())
654 AddPieceFID(*CallEnterWithin);
656 if (
auto CallEnterEvent =
Call->getCallEnterEvent())
657 AddPieceFID(*CallEnterEvent);
659 WorkList.push_back(&
Call->path);
660 }
else if (
const PathDiagnosticMacroPiece *
Macro =
661 dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
662 WorkList.push_back(&
Macro->subPieces);
670 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
672 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
683 " <key>clang_version</key>\n";
685 o <<
" <key>diagnostics</key>\n"
688 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
689 DE = Diags.end(); DI!=DE; ++DI) {
693 const PathDiagnostic *D = *DI;
694 printBugPath(o, FM, D->
path);
697 o <<
" <key>description</key>";
699 o <<
" <key>category</key>";
701 o <<
" <key>type</key>";
703 o <<
" <key>check_name</key>";
706 o <<
" <!-- This hash is experimental and going to change! -->\n";
707 o <<
" <key>issue_hash_content_of_line_in_context</key>";
720 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
722 switch (ND->getKind()) {
723 case Decl::CXXRecord:
724 declKind =
"C++ class";
726 case Decl::CXXMethod:
727 declKind =
"C++ method";
729 case Decl::ObjCMethod:
730 declKind =
"Objective-C method";
733 declKind =
"function";
738 if (!declKind.empty()) {
739 const std::string &declName = ND->getDeclName().getAsString();
740 o <<
" <key>issue_context_kind</key>";
742 o <<
" <key>issue_context</key>";
748 if (
const Stmt *Body = DeclWithIssue->getBody()) {
760 o <<
" <key>issue_hash_function_offset</key><string>"
767 o <<
" <key>issue_hash_function_offset</key><string>"
777 o <<
" <key>location</key>\n";
781 if (!filesMade->empty()) {
783 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
785 for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
786 CE = files->end(); CI != CE; ++CI) {
787 StringRef newName = CI->first;
788 if (newName != lastName) {
789 if (!lastName.empty()) {
793 o <<
" <key>" << lastName <<
"_files</key>\n";
796 o <<
" <string>" << CI->second <<
"</string>\n";
810 o <<
" <key>files</key>\n"
812 for (FileID FID : Fids)
817 o <<
" <key>statistics</key>\n";
819 llvm::raw_string_ostream os(stats);
820 llvm::PrintStatisticsJSON(os);
825 o <<
"</dict>\n</plist>\n";
832static std::optional<StringRef>
837 if (
auto CTUMacroExpCtx =
839 return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
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.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
StringRef getName() const
The name of this FileEntry.
FullSourceLoc getExpansionLoc() const
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
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
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.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
CharSourceRange getExpansionRange(SourceLocation Loc) const
Given a SourceLocation object, return the range of tokens covered by the expansion in the ultimate fi...
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID.
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.
std::shared_ptr< PathDiagnosticEventPiece > getCallExitEvent() const
std::shared_ptr< PathDiagnosticEventPiece > getCallEnterWithinCallerEvent() const
std::shared_ptr< PathDiagnosticEventPiece > getCallEnterEvent() const
std::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
FullSourceLoc asLocation() const
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
bool isLastInMainSourceFile() const
StringRef getString() const
ArrayRef< FixItHint > getFixits() const
Return the fix-it hints associated with this PathDiagnosticPiece.
PathDiagnosticLocation getLocation() const override
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
StringRef getCheckerName() const
PathDiagnosticLocation getUniqueingLoc() const
Get the location on which the report should be uniqued.
const Decl * getDeclWithIssue() const
Return the semantic context where an issue occurred.
const FilesToLineNumsMap & getExecutedLines() const
StringRef getBugType() const
const Decl * getUniqueingDecl() const
Get the declaration containing the uniqueing location.
StringRef getCategory() const
StringRef getShortDescription() const
SmallString< 32 > getIssueHash(const SourceManager &SrcMgr, const LangOptions &LangOpts) const
Get a hash that identifies the issue.
PathDiagnosticLocation getLocation() const
std::vector< std::unique_ptr< PathDiagnosticConsumer > > PathDiagnosticConsumers
void createPlistDiagnosticConsumerImpl(PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Output, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, const MacroExpansionContext &MacroExpansions, bool SupportsMultipleFiles)
Creates and registers a Plist diagnostic consumer, without any additional text consumer.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
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 ...
StringRef getName(const HeaderType T)
void EmitRange(raw_ostream &o, const SourceManager &SM, CharSourceRange R, const FIDMap &FM, unsigned indent)
raw_ostream & EmitString(raw_ostream &o, StringRef s)
llvm::DenseMap< FileID, unsigned > FIDMap
unsigned AddFID(FIDMap &FIDs, SmallVectorImpl< FileID > &V, FileID FID)
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
raw_ostream & EmitPlistHeader(raw_ostream &o)
void EmitLocation(raw_ostream &o, const SourceManager &SM, SourceLocation L, const FIDMap &FM, unsigned indent)
Top level wrappers for InstallAPI frontend operations.
raw_ostream & Indent(raw_ostream &Out, const unsigned int Space, bool IsDot)
U cast(CodeGen::Address addr)
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.