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