clang: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

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

31#include

32

33using namespace clang;

34using namespace ento;

35using namespace markup;

36

37

38

39

40

41

42namespace {

45 const std::string OutputFile;

49 const bool SupportsCrossFileDiagnostics;

50

51 void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,

53

54 public:

56 const std::string &OutputFile, const Preprocessor &PP,

59 bool supportsMultipleFiles);

60

61 ~PlistDiagnostics() override {}

62

64 FilesMade *filesMade) override;

65

66 StringRef getName() const override {

67 return "PlistDiagnostics";

68 }

69

72 }

75 return SupportsCrossFileDiagnostics;

76 }

77 };

78}

79

80namespace {

81

82

83class PlistPrinter {

89

90public:

94 : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}

95

97 ReportPiece(o, P, 4, 0, true);

98 }

99

100

101

102

103

104

105

106 void ReportMacroExpansions(raw_ostream &o, unsigned indent);

107

108private:

110 unsigned indent, unsigned depth, bool includeControlFlow,

111 bool isKeyEvent = false) {

112 switch (P.getKind()) {

114 if (includeControlFlow)

115 ReportControlFlow(o, cast(P), indent);

116 break;

118 ReportCall(o, cast(P), indent,

119 depth);

120 break;

122 ReportEvent(o, cast(P), indent, depth,

123 isKeyEvent);

124 break;

126 ReportMacroSubPieces(o, cast(P), indent,

127 depth);

128 break;

130 ReportNote(o, cast(P), indent);

131 break;

133 ReportPopUp(o, cast(P), indent);

134 break;

135 }

136 }

137

139 unsigned indent);

140 void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);

141 void EmitFixits(raw_ostream &o, ArrayRef fixits, unsigned indent);

142

143 void ReportControlFlow(raw_ostream &o,

145 unsigned indent);

147 unsigned indent, unsigned depth, bool isKeyEvent = false);

149 unsigned indent, unsigned depth);

151 unsigned indent, unsigned depth);

153 unsigned indent);

154

156 unsigned indent);

157};

158

159}

160

161

162

164 unsigned InputIndentLevel,

167 llvm::raw_fd_ostream &o);

168

172

173

174

175

176

177void PlistPrinter::EmitRanges(raw_ostream &o,

179 unsigned indent) {

180

181 if (Ranges.empty())

182 return;

183

184 Indent(o, indent) << "ranges\n";

185 Indent(o, indent) << "\n";

186 ++indent;

187

190

191 for (auto &R : Ranges)

194 FM, indent + 1);

195 --indent;

196 Indent(o, indent) << "\n";

197}

198

199void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,

200 unsigned indent) {

201

202 assert(!Message.empty());

203 Indent(o, indent) << "extended_message\n";

204 Indent(o, indent);

206

207

208

209 Indent(o, indent) << "message\n";

210 Indent(o, indent);

212}

213

215 unsigned indent) {

216 if (fixits.size() == 0)

217 return;

218

221

222 Indent(o, indent) << "fixits\n";

223 Indent(o, indent) << "\n";

224 for (const auto &fixit : fixits) {

225 assert(!fixit.isNull());

226

227 assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");

228 assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");

229 Indent(o, indent) << " \n";

230 Indent(o, indent) << " remove_range\n";

232 FM, indent + 2);

233 Indent(o, indent) << " insert_string";

235 o << "\n";

236 Indent(o, indent) << " \n";

237 }

238 Indent(o, indent) << "\n";

239}

240

241void PlistPrinter::ReportControlFlow(raw_ostream &o,

243 unsigned indent) {

244

247

248 Indent(o, indent) << "\n";

249 ++indent;

250

251 Indent(o, indent) << "kindcontrol\n";

252

253

254 Indent(o, indent) << "edges\n";

255 ++indent;

256 Indent(o, indent) << "\n";

257 ++indent;

259 I!=E; ++I) {

260 Indent(o, indent) << "\n";

261 ++indent;

262

263

264

265

266 Indent(o, indent) << "start\n";

268 SM.getExpansionLoc(I->getStart().asRange().getBegin()));

270 indent + 1);

271

272 Indent(o, indent) << "end\n";

273 SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));

275 indent + 1);

276

277 --indent;

278 Indent(o, indent) << "\n";

279 }

280 --indent;

281 Indent(o, indent) << "\n";

282 --indent;

283

284

285 const auto &s = P.getString();

286 if (s.empty()) {

287 Indent(o, indent) << "alternate";

289 }

290

291 assert(P.getFixits().size() == 0 &&

292 "Fixits on constrol flow pieces are not implemented yet!");

293

294 --indent;

295 Indent(o, indent) << "\n";

296}

297

299 unsigned indent, unsigned depth,

300 bool isKeyEvent) {

301

303

304 Indent(o, indent) << "\n";

305 ++indent;

306

307 Indent(o, indent) << "kindevent\n";

308

309 if (isKeyEvent) {

310 Indent(o, indent) << "key_event\n";

311 }

312

313

315

316 Indent(o, indent) << "location\n";

318

319

321 EmitRanges(o, Ranges, indent);

322

323

324 Indent(o, indent) << "depth";

326

327

328 EmitMessage(o, P.getString(), indent);

329

330

331 EmitFixits(o, P.getFixits(), indent);

332

333

334 --indent;

335 Indent(o, indent); o << "\n";

336}

337

339 unsigned indent,

340 unsigned depth) {

341

342 if (auto callEnter = P.getCallEnterEvent())

343 ReportPiece(o, *callEnter, indent, depth, true,

344 P.isLastInMainSourceFile());

345

346

347 ++depth;

348

349 if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())

350 ReportPiece(o, *callEnterWithinCaller, indent, depth,

351 true);

352

353 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)

354 ReportPiece(o, **I, indent, depth, true);

355

356 --depth;

357

358 if (auto callExit = P.getCallExitEvent())

359 ReportPiece(o, *callExit, indent, depth, true);

360

361 assert(P.getFixits().size() == 0 &&

362 "Fixits on call pieces are not implemented yet!");

363}

364

365void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,

367 unsigned indent, unsigned depth) {

368 MacroPieces.push_back(&P);

369

370 for (const auto &SubPiece : P.subPieces) {

371 ReportPiece(o, *SubPiece, indent, depth, false);

372 }

373

374 assert(P.getFixits().size() == 0 &&

375 "Fixits on constrol flow pieces are not implemented yet!");

376}

377

378void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {

379

382

384 P->getLocation().asLocation().getExpansionLoc();

385

386 const std::optional MacroName =

388 const std::optional ExpansionText =

390

391 if (!MacroName || !ExpansionText)

392 continue;

393

394 Indent(o, indent) << "\n";

395 ++indent;

396

397

399

400 Indent(o, indent) << "location\n";

402

403

405 EmitRanges(o, Ranges, indent);

406

407

408 Indent(o, indent) << "name";

410

411

412 Indent(o, indent) << "expansion";

413 EmitString(o, *ExpansionText) << '\n';

414

415

416 --indent;

417 Indent(o, indent);

418 o << "\n";

419 }

420}

421

423 unsigned indent) {

424

426

427 Indent(o, indent) << "\n";

428 ++indent;

429

430

432

433 Indent(o, indent) << "location\n";

435

436

438 EmitRanges(o, Ranges, indent);

439

440

441 EmitMessage(o, P.getString(), indent);

442

443

444 EmitFixits(o, P.getFixits(), indent);

445

446

447 --indent;

448 Indent(o, indent); o << "\n";

449}

450

451void PlistPrinter::ReportPopUp(raw_ostream &o,

453 unsigned indent) {

455

456 Indent(o, indent) << "\n";

457 ++indent;

458

459 Indent(o, indent) << "kindpop-up\n";

460

461

463

464 Indent(o, indent) << "location\n";

466

467

469 EmitRanges(o, Ranges, indent);

470

471

472 EmitMessage(o, P.getString(), indent);

473

474 assert(P.getFixits().size() == 0 &&

475 "Fixits on pop-up pieces are not implemented yet!");

476

477

478 --indent;

479 Indent(o, indent) << "\n";

480}

481

482

483

484

485

486

487

489 unsigned InputIndentLevel,

492 llvm::raw_fd_ostream &o) {

493 unsigned IndentLevel = InputIndentLevel;

494

495 Indent(o, IndentLevel) << "ExecutedLines\n";

496 Indent(o, IndentLevel) << "\n";

497 IndentLevel++;

498

499

501 for (const auto &[FID, Lines] : ExecutedLines) {

502 unsigned FileKey = AddFID(FM, Fids, FID);

503 Indent(o, IndentLevel) << "" << FileKey << "\n";

504 Indent(o, IndentLevel) << "\n";

505 IndentLevel++;

506 for (unsigned LineNo : Lines) {

507 Indent(o, IndentLevel);

509 }

510 IndentLevel--;

511 Indent(o, IndentLevel) << "\n";

512 }

513 IndentLevel--;

514 Indent(o, IndentLevel) << "\n";

515

516 assert(IndentLevel == InputIndentLevel);

517}

518

519

520

521

522

523PlistDiagnostics::PlistDiagnostics(

527 : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),

528 MacroExpansions(MacroExpansions),

529 SupportsCrossFileDiagnostics(supportsMultipleFiles) {

530

531 (void)this->CTU;

532}

533

534void ento::createPlistDiagnosticConsumer(

536 const std::string &OutputFile, const Preprocessor &PP,

539

540

541 if (OutputFile.empty())

542 return;

543

544 C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,

545 MacroExpansions,

546 false));

547 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,

548 PP, CTU, MacroExpansions);

549}

550

551void ento::createPlistMultiFileDiagnosticConsumer(

553 const std::string &OutputFile, const Preprocessor &PP,

556

557

558 if (OutputFile.empty())

559 return;

560

561 C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,

562 MacroExpansions,

563 true));

564 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,

565 PP, CTU, MacroExpansions);

566}

567

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;

574 }) &&

575 "PathDiagnostic is not partitioned so that notes precede the rest");

576

577 PathPieces::const_iterator FirstNonNote = std::partition_point(

579 return E->getKind() == PathDiagnosticPiece::Note;

580 });

581

582 PathPieces::const_iterator I = Path.begin();

583

584 if (FirstNonNote != Path.begin()) {

585 o << " notes\n"

586 " \n";

587

588 for (; I != FirstNonNote; ++I)

589 Printer.ReportDiag(o, **I);

590

591 o << " \n";

592 }

593

594 o << " path\n";

595

596 o << " \n";

597

598 for (const auto &Piece : llvm::make_range(I, Path.end()))

599 Printer.ReportDiag(o, *Piece);

600

601 o << " \n";

602

604 return;

605

606 o << " macro_expansions\n"

607 " \n";

608 Printer.ReportMacroExpansions(o, 4);

609 o << " \n";

610}

611

612void PlistDiagnostics::FlushDiagnosticsImpl(

613 std::vector<const PathDiagnostic *> &Diags,

614 FilesMade *filesMade) {

615

616

621

623 AddFID(FM, Fids, SM, Piece.getLocation().asLocation());

628 }

629 };

630

632

635

638

639 for (const auto &Iter : Path) {

641 AddPieceFID(Piece);

642

644 dyn_cast(&Piece)) {

645 if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())

646 AddPieceFID(*CallEnterWithin);

647

648 if (auto CallEnterEvent = Call->getCallEnterEvent())

649 AddPieceFID(*CallEnterEvent);

650

653 dyn_cast(&Piece)) {

655 }

656 }

657 }

658 }

659

660

661 std::error_code EC;

662 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);

663 if (EC) {

664 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';

665 return;

666 }

667

669

670

671

672

673

674 o << "\n" <<

675 " clang_version\n";

677 o << " diagnostics\n"

678 " \n";

679

680 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),

681 DE = Diags.end(); DI!=DE; ++DI) {

682

683 o << " \n";

684

686 printBugPath(o, FM, D->path);

687

688

689 o << " description";

690 EmitString(o, D->getShortDescription()) << '\n';

691 o << " category";

693 o << " type";

695 o << " check_name";

696 EmitString(o, D->getCheckerName()) << '\n';

697

698 o << " \n";

699 o << " issue_hash_content_of_line_in_context";

705 const Decl *DeclWithIssue = D->getDeclWithIssue();

707 DeclWithIssue, LangOpts))

708 << '\n';

709

710

711

712 if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {

713

714 if (const NamedDecl *ND = dyn_cast(DeclWithIssue)) {

715 StringRef declKind;

716 switch (ND->getKind()) {

717 case Decl::CXXRecord:

718 declKind = "C++ class";

719 break;

720 case Decl::CXXMethod:

721 declKind = "C++ method";

722 break;

723 case Decl::ObjCMethod:

724 declKind = "Objective-C method";

725 break;

726 case Decl::Function:

727 declKind = "function";

728 break;

729 default:

730 break;

731 }

732 if (!declKind.empty()) {

733 const std::string &declName = ND->getDeclName().getAsString();

734 o << " issue_context_kind";

736 o << " issue_context";

738 }

739

740

741

742 if (const Stmt *Body = DeclWithIssue->getBody()) {

743

744

745

746

747

748

751 SM.getExpansionLoc(

754 o << " issue_hash_function_offset"

756 << "\n";

757

758

759 } else {

761 o << " issue_hash_function_offset"

763 << "\n";

764 }

765

766 }

767 }

768 }

769

770

771 o << " location\n";

773

774

775 if (!filesMade->empty()) {

776 StringRef lastName;

777 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);

778 if (files) {

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()) {

784 o << " \n";

785 }

786 lastName = newName;

787 o << " " << lastName << "_files\n";

788 o << " \n";

789 }

790 o << " " << CI->second << "\n";

791 }

792 o << " \n";

793 }

794 }

795

797

798

799 o << " \n";

800 }

801

802 o << " \n";

803

804 o << " files\n"

805 " \n";

806 for (FileID FID : Fids)

807 EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n';

808 o << " \n";

809

811 o << " statistics\n";

812 std::string stats;

813 llvm::raw_string_ostream os(stats);

814 llvm::PrintStatisticsJSON(os);

816 }

817

818

819 o << "\n\n";

820}

821

822

823

824

825

826static std::optional

831 if (auto CTUMacroExpCtx =

833 return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);

834 }

835 return MacroExpansions.getExpandedText(MacroExpansionLoc);

836}

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.