26#include "llvm/ADT/SmallPtrSet.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/Statistic.h"
29#include "llvm/Support/Casting.h"
35using namespace markup;
45 const std::string OutputFile;
49 const bool SupportsCrossFileDiagnostics;
51 void printBugPath(llvm::raw_ostream &o,
const FIDMap &FM,
59 bool supportsMultipleFiles);
61 ~PlistDiagnostics()
override {}
64 FilesMade *filesMade)
override;
66 StringRef
getName()
const override {
67 return "PlistDiagnostics";
75 return SupportsCrossFileDiagnostics;
94 : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
97 ReportPiece(o,
P, 4, 0,
true);
106 void ReportMacroExpansions(raw_ostream &o,
unsigned indent);
110 unsigned indent,
unsigned depth,
bool includeControlFlow,
111 bool isKeyEvent =
false) {
112 switch (
P.getKind()) {
114 if (includeControlFlow)
115 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(
P), indent);
118 ReportCall(o, cast<PathDiagnosticCallPiece>(
P), indent,
122 ReportEvent(o, cast<PathDiagnosticEventPiece>(
P), indent, depth,
126 ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(
P), indent,
130 ReportNote(o, cast<PathDiagnosticNotePiece>(
P), indent);
133 ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(
P), indent);
140 void EmitMessage(raw_ostream &o, StringRef Message,
unsigned indent);
143 void ReportControlFlow(raw_ostream &o,
147 unsigned indent,
unsigned depth,
bool isKeyEvent =
false);
149 unsigned indent,
unsigned depth);
151 unsigned indent,
unsigned depth);
164 unsigned InputIndentLevel,
167 llvm::raw_fd_ostream &o);
177void PlistPrinter::EmitRanges(raw_ostream &o,
184 Indent(o, indent) <<
"<key>ranges</key>\n";
185 Indent(o, indent) <<
"<array>\n";
191 for (
auto &R : Ranges)
196 Indent(o, indent) <<
"</array>\n";
199void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
202 assert(!Message.empty());
203 Indent(o, indent) <<
"<key>extended_message</key>\n";
209 Indent(o, indent) <<
"<key>message</key>\n";
216 if (fixits.size() == 0)
222 Indent(o, indent) <<
"<key>fixits</key>\n";
223 Indent(o, indent) <<
"<array>\n";
224 for (
const auto &fixit : fixits) {
225 assert(!fixit.isNull());
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";
233 Indent(o, indent) <<
" <key>insert_string</key>";
236 Indent(o, indent) <<
" </dict>\n";
238 Indent(o, indent) <<
"</array>\n";
241void PlistPrinter::ReportControlFlow(raw_ostream &o,
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";
260 Indent(o, indent) <<
"<dict>\n";
266 Indent(o, indent) <<
"<key>start</key>\n";
268 SM.getExpansionLoc(I->getStart().asRange().getBegin()));
272 Indent(o, indent) <<
"<key>end</key>\n";
273 SourceRange EndEdge(
SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
278 Indent(o, indent) <<
"</dict>\n";
281 Indent(o, indent) <<
"</array>\n";
285 const auto &
s =
P.getString();
287 Indent(o, indent) <<
"<key>alternate</key>";
291 assert(
P.getFixits().size() == 0 &&
292 "Fixits on constrol flow pieces are not implemented yet!");
295 Indent(o, indent) <<
"</dict>\n";
299 unsigned indent,
unsigned depth,
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";
316 Indent(o, indent) <<
"<key>location</key>\n";
321 EmitRanges(o, Ranges, indent);
324 Indent(o, indent) <<
"<key>depth</key>";
328 EmitMessage(o,
P.getString(), indent);
331 EmitFixits(o,
P.getFixits(), indent);
335 Indent(o, indent); o <<
"</dict>\n";
342 if (
auto callEnter =
P.getCallEnterEvent())
343 ReportPiece(o, *callEnter, indent, depth,
true,
344 P.isLastInMainSourceFile());
349 if (
auto callEnterWithinCaller =
P.getCallEnterWithinCallerEvent())
350 ReportPiece(o, *callEnterWithinCaller, indent, depth,
353 for (PathPieces::const_iterator I =
P.path.begin(),
E =
P.path.end();I!=
E;++I)
354 ReportPiece(o, **I, indent, depth,
true);
358 if (
auto callExit =
P.getCallExitEvent())
359 ReportPiece(o, *callExit, indent, depth,
true);
361 assert(
P.getFixits().size() == 0 &&
362 "Fixits on call pieces are not implemented yet!");
365void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
367 unsigned indent,
unsigned depth) {
368 MacroPieces.push_back(&
P);
370 for (
const auto &SubPiece :
P.subPieces) {
371 ReportPiece(o, *SubPiece, indent, depth,
false);
374 assert(
P.getFixits().size() == 0 &&
375 "Fixits on constrol flow pieces are not implemented yet!");
378void PlistPrinter::ReportMacroExpansions(raw_ostream &o,
unsigned indent) {
384 P->getLocation().asLocation().getExpansionLoc();
386 const std::optional<StringRef> MacroName =
388 const std::optional<StringRef> ExpansionText =
391 if (!MacroName || !ExpansionText)
394 Indent(o, indent) <<
"<dict>\n";
400 Indent(o, indent) <<
"<key>location</key>\n";
405 EmitRanges(o, Ranges, indent);
408 Indent(o, indent) <<
"<key>name</key>";
412 Indent(o, indent) <<
"<key>expansion</key>";
427 Indent(o, indent) <<
"<dict>\n";
433 Indent(o, indent) <<
"<key>location</key>\n";
438 EmitRanges(o, Ranges, indent);
441 EmitMessage(o,
P.getString(), indent);
444 EmitFixits(o,
P.getFixits(), indent);
448 Indent(o, indent); o <<
"</dict>\n";
451void PlistPrinter::ReportPopUp(raw_ostream &o,
456 Indent(o, indent) <<
"<dict>\n";
459 Indent(o, indent) <<
"<key>kind</key><string>pop-up</string>\n";
464 Indent(o, indent) <<
"<key>location</key>\n";
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";
489 unsigned InputIndentLevel,
492 llvm::raw_fd_ostream &o) {
493 unsigned IndentLevel = InputIndentLevel;
495 Indent(o, IndentLevel) <<
"<key>ExecutedLines</key>\n";
496 Indent(o, IndentLevel) <<
"<dict>\n";
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);
511 Indent(o, IndentLevel) <<
"</array>\n";
514 Indent(o, IndentLevel) <<
"</dict>\n";
516 assert(IndentLevel == InputIndentLevel);
523PlistDiagnostics::PlistDiagnostics(
527 : DiagOpts(
std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
528 MacroExpansions(MacroExpansions),
529 SupportsCrossFileDiagnostics(supportsMultipleFiles) {
534void ento::createPlistDiagnosticConsumer(
541 if (OutputFile.empty())
544 C.push_back(
new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
547 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
548 PP, CTU, MacroExpansions);
551void ento::createPlistMultiFileDiagnosticConsumer(
558 if (OutputFile.empty())
561 C.push_back(
new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
564 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts),
C, OutputFile,
565 PP, CTU, MacroExpansions);
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;
575 "PathDiagnostic is not partitioned so that notes precede the rest");
577 PathPieces::const_iterator FirstNonNote = std::partition_point(
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);
606 o <<
" <key>macro_expansions</key>\n"
608 Printer.ReportMacroExpansions(o, 4);
612void PlistDiagnostics::FlushDiagnosticsImpl(
613 std::vector<const PathDiagnostic *> &Diags,
614 FilesMade *filesMade) {
623 AddFID(FM, Fids,
SM, Piece.getLocation().asLocation());
644 dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
645 if (
auto CallEnterWithin =
Call->getCallEnterWithinCallerEvent())
646 AddPieceFID(*CallEnterWithin);
648 if (
auto CallEnterEvent =
Call->getCallEnterEvent())
649 AddPieceFID(*CallEnterEvent);
653 dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
662 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
664 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
675 " <key>clang_version</key>\n";
677 o <<
" <key>diagnostics</key>\n"
680 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
681 DE = Diags.end(); DI!=DE; ++DI) {
686 printBugPath(o, FM,
D->path);
689 o <<
" <key>description</key>";
691 o <<
" <key>category</key>";
693 o <<
" <key>type</key>";
695 o <<
" <key>check_name</key>";
698 o <<
" <!-- This hash is experimental and going to change! -->\n";
699 o <<
" <key>issue_hash_content_of_line_in_context</key>";
705 const Decl *DeclWithIssue =
D->getDeclWithIssue();
707 DeclWithIssue, LangOpts))
712 if (
const Decl *DeclWithIssue =
D->getDeclWithIssue()) {
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>";
736 o <<
" <key>issue_context</key>";
754 o <<
" <key>issue_hash_function_offset</key><string>"
761 o <<
" <key>issue_hash_function_offset</key><string>"
771 o <<
" <key>location</key>\n";
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";
804 o <<
" <key>files</key>\n"
807 EmitString(o <<
" ",
SM.getFileEntryRefForID(FID)->getName()) <<
'\n';
811 o <<
" <key>statistics</key>\n";
813 llvm::raw_string_ostream os(stats);
814 llvm::PrintStatisticsJSON(os);
820 o <<
"</dict>\n</plist>\n";
827static std::optional<StringRef>
832 if (
auto CTUMacroExpCtx =
834 return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
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.