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