25#include "llvm/ADT/SmallVector.h"
26#include "llvm/ADT/Statistic.h"
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;
48 void printBugPath(llvm::raw_ostream &o,
const FIDMap &FM,
49 const PathPieces &Path);
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);
58 ~PlistDiagnostics()
override {}
60 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
61 FilesMade *filesMade)
override;
63 StringRef
getName()
const override {
64 return "PlistDiagnostics";
67 PathGenerationScheme getGenerationScheme()
const override {
70 bool supportsLogicalOpControlFlow()
const override {
return true; }
71 bool supportsCrossFileDiagnostics()
const override {
72 return SupportsCrossFileDiagnostics;
82 const Preprocessor &PP;
83 const cross_tu::CrossTranslationUnitContext &CTU;
84 const MacroExpansionContext &MacroExpansions;
85 llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
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) {}
93 void ReportDiag(raw_ostream &o,
const PathDiagnosticPiece& P) {
94 ReportPiece(o, P, 4, 0,
true);
103 void ReportMacroExpansions(raw_ostream &o,
unsigned indent);
106 void ReportPiece(raw_ostream &o,
const PathDiagnosticPiece &P,
107 unsigned indent,
unsigned depth,
bool includeControlFlow,
108 bool isKeyEvent =
false) {
111 if (includeControlFlow)
135 void EmitRanges(raw_ostream &o,
const ArrayRef<SourceRange> Ranges,
137 void EmitMessage(raw_ostream &o, StringRef Message,
unsigned indent);
138 void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
unsigned indent);
140 void ReportControlFlow(raw_ostream &o,
141 const PathDiagnosticControlFlowPiece& P,
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,
152 void ReportPopUp(raw_ostream &o,
const PathDiagnosticPopUpPiece &P,
161 unsigned InputIndentLevel,
164 llvm::raw_fd_ostream &o);
174void PlistPrinter::EmitRanges(raw_ostream &o,
181 Indent(o, indent) <<
"<key>ranges</key>\n";
182 Indent(o, indent) <<
"<array>\n";
188 for (
auto &R : Ranges)
193 Indent(o, indent) <<
"</array>\n";
196void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
200 Indent(o, indent) <<
"<key>extended_message</key>\n";
206 Indent(o, indent) <<
"<key>message</key>\n";
211void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
213 if (fixits.size() == 0)
219 Indent(o, indent) <<
"<key>fixits</key>\n";
220 Indent(o, indent) <<
"<array>\n";
221 for (
const auto &fixit : fixits) {
222 assert(!fixit.isNull());
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";
230 Indent(o, indent) <<
" <key>insert_string</key>";
233 Indent(o, indent) <<
" </dict>\n";
235 Indent(o, indent) <<
"</array>\n";
238void PlistPrinter::ReportControlFlow(raw_ostream &o,
239 const PathDiagnosticControlFlowPiece& P,
245 Indent(o, indent) <<
"<dict>\n";
248 Indent(o, indent) <<
"<key>kind</key><string>control</string>\n";
251 Indent(o, indent) <<
"<key>edges</key>\n";
253 Indent(o, indent) <<
"<array>\n";
257 Indent(o, indent) <<
"<dict>\n";
263 Indent(o, indent) <<
"<key>start</key>\n";
264 SourceRange StartEdge(
265 SM.getExpansionLoc(I->getStart().asRange().getBegin()));
269 Indent(o, indent) <<
"<key>end</key>\n";
270 SourceRange EndEdge(
SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
275 Indent(o, indent) <<
"</dict>\n";
278 Indent(o, indent) <<
"</array>\n";
284 Indent(o, indent) <<
"<key>alternate</key>";
289 "Fixits on constrol flow pieces are not implemented yet!");
292 Indent(o, indent) <<
"</dict>\n";
295void PlistPrinter::ReportEvent(raw_ostream &o,
const PathDiagnosticEventPiece& P,
296 unsigned indent,
unsigned depth,
301 Indent(o, indent) <<
"<dict>\n";
304 Indent(o, indent) <<
"<key>kind</key><string>event</string>\n";
307 Indent(o, indent) <<
"<key>key_event</key><true/>\n";
313 Indent(o, indent) <<
"<key>location</key>\n";
317 ArrayRef<SourceRange> Ranges = P.
getRanges();
318 EmitRanges(o, Ranges, indent);
321 Indent(o, indent) <<
"<key>depth</key>";
332 Indent(o, indent); o <<
"</dict>\n";
335void PlistPrinter::ReportCall(raw_ostream &o,
const PathDiagnosticCallPiece &P,
340 ReportPiece(o, *callEnter, indent, depth,
true,
347 ReportPiece(o, *callEnterWithinCaller, indent, depth,
350 for (PathPieces::const_iterator I = P.
path.begin(), E = P.
path.end();I!=E;++I)
351 ReportPiece(o, **I, indent, depth,
true);
356 ReportPiece(o, *callExit, indent, depth,
true);
359 "Fixits on call pieces are not implemented yet!");
362void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
363 const PathDiagnosticMacroPiece& P,
364 unsigned indent,
unsigned depth) {
365 MacroPieces.push_back(&P);
367 for (
const auto &SubPiece : P.
subPieces) {
368 ReportPiece(o, *SubPiece, indent, depth,
false);
372 "Fixits on constrol flow pieces are not implemented yet!");
375void PlistPrinter::ReportMacroExpansions(raw_ostream &o,
unsigned indent) {
377 for (
const PathDiagnosticMacroPiece *P : MacroPieces) {
380 SourceLocation MacroExpansionLoc =
383 const std::optional<StringRef> MacroName =
385 const std::optional<StringRef> ExpansionText =
388 if (!MacroName || !ExpansionText)
391 Indent(o, indent) <<
"<dict>\n";
397 Indent(o, indent) <<
"<key>location</key>\n";
401 ArrayRef<SourceRange> Ranges = P->
getRanges();
402 EmitRanges(o, Ranges, indent);
405 Indent(o, indent) <<
"<key>name</key>";
409 Indent(o, indent) <<
"<key>expansion</key>";
419void PlistPrinter::ReportNote(raw_ostream &o,
const PathDiagnosticNotePiece& P,
424 Indent(o, indent) <<
"<dict>\n";
430 Indent(o, indent) <<
"<key>location</key>\n";
434 ArrayRef<SourceRange> Ranges = P.
getRanges();
435 EmitRanges(o, Ranges, indent);
445 Indent(o, indent); o <<
"</dict>\n";
448void PlistPrinter::ReportPopUp(raw_ostream &o,
449 const PathDiagnosticPopUpPiece &P,
453 Indent(o, indent) <<
"<dict>\n";
456 Indent(o, indent) <<
"<key>kind</key><string>pop-up</string>\n";
461 Indent(o, indent) <<
"<key>location</key>\n";
465 ArrayRef<SourceRange> Ranges = P.
getRanges();
466 EmitRanges(o, Ranges, indent);
472 "Fixits on pop-up pieces are not implemented yet!");
476 Indent(o, indent) <<
"</dict>\n";
486 unsigned InputIndentLevel,
489 llvm::raw_fd_ostream &o) {
498 for (
const auto &[FID, Lines] : ExecutedLines) {
499 unsigned FileKey =
AddFID(FM, Fids, FID);
503 for (
unsigned LineNo : Lines) {
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) {
531void ento::createPlistDiagnosticConsumer(
538 if (OutputFile.empty())
541 C.push_back(std::make_unique<PlistDiagnostics>(
542 DiagOpts, OutputFile, PP, CTU, MacroExpansions,
544 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
545 PP, CTU, MacroExpansions);
548void ento::createPlistMultiFileDiagnosticConsumer(
555 if (OutputFile.empty())
558 C.push_back(std::make_unique<PlistDiagnostics>(
559 DiagOpts, OutputFile, PP, CTU, MacroExpansions,
561 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
562 PP, CTU, MacroExpansions);
565void PlistDiagnostics::printBugPath(llvm::raw_ostream &o,
const FIDMap &FM,
567 PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
568 assert(std::is_partitioned(Path.begin(), Path.end(),
570 return E->getKind() == PathDiagnosticPiece::Note;
572 "PathDiagnostic is not partitioned so that notes precede the rest");
574 PathPieces::const_iterator FirstNonNote =
579 PathPieces::const_iterator I = Path.begin();
581 if (FirstNonNote != Path.begin()) {
582 o <<
" <key>notes</key>\n"
585 for (; I != FirstNonNote; ++I)
586 Printer.ReportDiag(o, **I);
591 o <<
" <key>path</key>\n";
595 for (
const auto &Piece : llvm::make_range(I, Path.end()))
596 Printer.ReportDiag(o, *Piece);
603 o <<
" <key>macro_expansions</key>\n"
605 Printer.ReportMacroExpansions(o, 4);
609void PlistDiagnostics::FlushDiagnosticsImpl(
610 std::vector<const PathDiagnostic *> &Diags,
611 FilesMade *filesMade) {
615 SmallVector<FileID, 10> Fids;
619 auto AddPieceFID = [&FM, &Fids, &
SM](
const PathDiagnosticPiece &Piece) {
620 AddFID(FM, Fids,
SM, Piece.getLocation().asLocation());
621 ArrayRef<SourceRange> Ranges = Piece.getRanges();
622 for (
const SourceRange &Range : Ranges) {
628 for (
const PathDiagnostic *D : Diags) {
630 SmallVector<const PathPieces *, 5> WorkList;
631 WorkList.push_back(&D->path);
633 while (!WorkList.empty()) {
634 const PathPieces &Path = *WorkList.pop_back_val();
636 for (
const auto &Iter : Path) {
637 const PathDiagnosticPiece &Piece = *Iter;
640 if (
const PathDiagnosticCallPiece *
Call =
641 dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
642 if (
auto CallEnterWithin =
Call->getCallEnterWithinCallerEvent())
643 AddPieceFID(*CallEnterWithin);
645 if (
auto CallEnterEvent =
Call->getCallEnterEvent())
646 AddPieceFID(*CallEnterEvent);
648 WorkList.push_back(&
Call->path);
649 }
else if (
const PathDiagnosticMacroPiece *
Macro =
650 dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
651 WorkList.push_back(&
Macro->subPieces);
659 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
661 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
672 " <key>clang_version</key>\n";
674 o <<
" <key>diagnostics</key>\n"
677 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
678 DE = Diags.end(); DI!=DE; ++DI) {
682 const PathDiagnostic *D = *DI;
683 printBugPath(o, FM, D->
path);
686 o <<
" <key>description</key>";
688 o <<
" <key>category</key>";
690 o <<
" <key>type</key>";
692 o <<
" <key>check_name</key>";
695 o <<
" <!-- This hash is experimental and going to change! -->\n";
696 o <<
" <key>issue_hash_content_of_line_in_context</key>";
698 FullSourceLoc L(
SM.getExpansionLoc(UPDLoc.
isValid()
704 DeclWithIssue, LangOpts))
711 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
713 switch (ND->getKind()) {
714 case Decl::CXXRecord:
715 declKind =
"C++ class";
717 case Decl::CXXMethod:
718 declKind =
"C++ method";
720 case Decl::ObjCMethod:
721 declKind =
"Objective-C method";
724 declKind =
"function";
729 if (!declKind.empty()) {
730 const std::string &declName = ND->getDeclName().getAsString();
731 o <<
" <key>issue_context_kind</key>";
733 o <<
" <key>issue_context</key>";
739 if (
const Stmt *Body = DeclWithIssue->
getBody()) {
751 o <<
" <key>issue_hash_function_offset</key><string>"
757 FullSourceLoc FunL(
SM.getExpansionLoc(Body->getBeginLoc()),
SM);
758 o <<
" <key>issue_hash_function_offset</key><string>"
768 o <<
" <key>location</key>\n";
772 if (!filesMade->empty()) {
774 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
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()) {
784 o <<
" <key>" << lastName <<
"_files</key>\n";
787 o <<
" <string>" << CI->second <<
"</string>\n";
801 o <<
" <key>files</key>\n"
803 for (FileID FID : Fids)
804 EmitString(o <<
" ",
SM.getFileEntryRefForID(FID)->getName()) <<
'\n';
808 o <<
" <key>statistics</key>\n";
810 llvm::raw_string_ostream os(stats);
811 llvm::PrintStatisticsJSON(os);
816 o <<
"</dict>\n</plist>\n";
823static std::optional<StringRef>
828 if (
auto CTUMacroExpCtx =
830 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.
__device__ __2f16 float __ockl_bool s
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
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.
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
PathDiagnosticLocation getLocation() const
std::vector< std::unique_ptr< PathDiagnosticConsumer > > PathDiagnosticConsumers
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)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
raw_ostream & Indent(raw_ostream &Out, const unsigned int Space, bool IsDot)
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.
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.