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

1

2

3

4

5

6

7

8

9

10

11

12

29#include "llvm/ADT/ArrayRef.h"

30#include "llvm/ADT/RewriteBuffer.h"

31#include "llvm/ADT/STLExtras.h"

32#include "llvm/ADT/Sequence.h"

33#include "llvm/ADT/SmallString.h"

34#include "llvm/ADT/StringRef.h"

35#include "llvm/ADT/iterator_range.h"

36#include "llvm/Support/Casting.h"

37#include "llvm/Support/Errc.h"

38#include "llvm/Support/ErrorHandling.h"

39#include "llvm/Support/FileSystem.h"

40#include "llvm/Support/MemoryBuffer.h"

41#include "llvm/Support/Path.h"

42#include "llvm/Support/raw_ostream.h"

43#include

44#include

45#include

46#include

47#include

48#include

49#include

50#include <system_error>

51#include

52#include

53

54using namespace clang;

55using namespace ento;

56using llvm::RewriteBuffer;

57

58

59

60

61

62namespace {

63

64class ArrowMap;

65

68 std::string Directory;

69 bool createdDir = false;

70 bool noDir = false;

72 const bool SupportsCrossFileDiagnostics;

73 llvm::StringSet<> EmittedHashes;

76

77public:

79 const std::string &OutputDir, const Preprocessor &pp,

80 bool supportsMultipleFiles)

81 : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp),

82 SupportsCrossFileDiagnostics(supportsMultipleFiles) {}

83

85

87 FilesMade *filesMade) override;

88

89 StringRef getName() const override { return "HTMLDiagnostics"; }

90

92 return SupportsCrossFileDiagnostics;

93 }

94

96 unsigned num);

97

98 unsigned ProcessControlFlowPiece(Rewriter &R, FileID BugFileID,

100 unsigned Number);

101

103 const std::vector &PopUpRanges, unsigned num,

104 unsigned max);

105

107 const char *HighlightStart = "<span class=\"mrange\">",

108 const char *HighlightEnd = "");

109

110 void ReportDiag(const PathDiagnostic &D, FilesMade *filesMade);

111

112

115 const char *declName);

116

117

121

122

124

127 }

128

129private:

131 const ArrowMap &ArrowIndices);

132

133

134 StringRef showHelpJavascript();

135

136

137 StringRef generateKeyboardNavigationJavascript();

138

139

140 StringRef generateArrowDrawingJavascript();

141

142

143 std::string showRelevantLinesJavascript(const PathDiagnostic &D,

145

146

148 llvm::raw_string_ostream &os);

149};

150

152 return isa(P) && P.getString().empty();

153}

154

155unsigned getPathSizeWithoutArrows(const PathPieces &Path) {

156 unsigned TotalPieces = Path.size();

157 unsigned TotalArrowPieces = llvm::count_if(

159 return TotalPieces - TotalArrowPieces;

160}

161

162class ArrowMap : public std::vector {

163 using Base = std::vector;

164

165public:

166 ArrowMap(unsigned Size) : Base(Size, 0) {}

167 unsigned getTotalNumberOfArrows() const { return at(0); }

168};

169

170llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) {

171 OS << "[ ";

172 llvm::interleave(Indices, OS, ",");

173 return OS << " ]";

174}

175

176}

177

178void ento::createHTMLDiagnosticConsumer(

180 const std::string &OutputDir, const Preprocessor &PP,

183

184

185

186

187

188

189 createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,

190 MacroExpansions);

191

192

193 if (OutputDir.empty())

194 return;

195

196 C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true));

197}

198

199void ento::createHTMLSingleFileDiagnosticConsumer(

201 const std::string &OutputDir, const Preprocessor &PP,

204 createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU,

205 MacroExpansions);

206

207

208 if (OutputDir.empty())

209 return;

210

211 C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false));

212}

213

214void ento::createPlistHTMLDiagnosticConsumer(

216 const std::string &prefix, const Preprocessor &PP,

219 createHTMLDiagnosticConsumer(

220 DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU,

221 MacroExpansions);

222 createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU,

223 MacroExpansions);

224 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP,

225 CTU, MacroExpansions);

226}

227

228void ento::createSarifHTMLDiagnosticConsumer(

230 const std::string &sarif_file, const Preprocessor &PP,

233 createHTMLDiagnosticConsumer(

234 DiagOpts, C, std::string(llvm::sys::path::parent_path(sarif_file)), PP,

235 CTU, MacroExpansions);

236 createSarifDiagnosticConsumer(DiagOpts, C, sarif_file, PP, CTU,

237 MacroExpansions);

238 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, sarif_file,

239 PP, CTU, MacroExpansions);

240}

241

242

243

244

245

246void HTMLDiagnostics::FlushDiagnosticsImpl(

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

248 FilesMade *filesMade) {

249 for (const auto Diag : Diags)

250 ReportDiag(*Diag, filesMade);

251}

252

260 SMgr);

261 return getIssueHash(L, D.getCheckerName(), D.getBugType(),

263}

264

266 FilesMade *filesMade) {

267

268 if (!createdDir) {

269 createdDir = true;

270 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {

271 llvm::errs() << "warning: could not create directory '"

272 << Directory << "': " << ec.message() << '\n';

273 noDir = true;

274 return;

275 }

276 }

277

278 if (noDir)

279 return;

280

281

282 PathPieces path = D.path.flatten(false);

283

284

285 assert(!path.empty());

286 const SourceManager &SMgr = path.front()->getLocation().getManager();

287

288

290

291

293 int offsetDecl = 0;

294 if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {

295 if (const auto *ND = dyn_cast(DeclWithIssue))

296 declName = ND->getDeclName().getAsString();

297

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

299

300

302 SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),

303 SMgr);

305 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();

306 }

307 }

308

310 auto [It, IsNew] = EmittedHashes.insert(IssueHash);

311 if (!IsNew) {

312

313 return;

314 }

315

316 std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());

317 if (report.empty()) {

318 llvm::errs() << "warning: no diagnostics generated for main file.\n";

319 return;

320 }

321

322

323 int FD;

324

326 llvm::raw_svector_ostream FileName(FileNameStr);

328

329

330

331

332

333

334

336

337

339 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();

340

342

343 FileName << llvm::sys::path::filename(Entry->getName()).str() << "-"

344 << declName.c_str() << "-" << offsetDecl << "-";

345 }

346

347 FileName << StringRef(IssueHash).substr(0, 6).str() << ".html";

348

350 llvm::sys::path::append(ResultPath, Directory, FileName.str());

351 if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {

352 llvm::errs() << "warning: could not make '" << ResultPath

353 << "' absolute: " << EC.message() << '\n';

354 return;

355 }

356

357 if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(

358 ResultPath, FD, llvm::sys::fs::CD_CreateNew,

359 llvm::sys::fs::OF_Text)) {

360

361

362

363

364

365 if (EC != llvm::errc::file_exists) {

366 llvm::errs() << "warning: could not create file in '" << Directory

367 << "': " << EC.message() << '\n';

368 }

369 return;

370 }

371

372 llvm::raw_fd_ostream os(FD, true);

373

374 if (filesMade)

375 filesMade->addDiagnostic(D, getName(),

376 llvm::sys::path::filename(ResultPath));

377

378

379 os << report;

380}

381

384

385 std::vector FileIDs;

386 for (auto I : path) {

387 FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();

388 if (llvm::is_contained(FileIDs, FID))

389 continue;

390

391 FileIDs.push_back(FID);

392 RewriteFile(R, path, FID);

393 }

394

395 if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {

396

397 for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {

398 std::string s;

399 llvm::raw_string_ostream os(s);

400

401 if (I != FileIDs.begin())

402 os << "


\n";

403

404 os << "<div id=File" << I->getHashValue() << ">\n";

405

406

407 if (I != FileIDs.begin())

408 os << "

<a href=\"#File" << (I - 1)->getHashValue()

409 << "\">←

";

410

412 << "\n";

413

414

415 if (I + 1 != E)

416 os << "

<a href=\"#File" << (I + 1)->getHashValue()

417 << "\">→

";

418

419 os << "\n";

420

422 }

423

424

425 for (auto I : llvm::drop_begin(FileIDs)) {

426 std::string s;

427 llvm::raw_string_ostream os(s);

428

430 for (auto BI : *Buf)

431 os << BI;

432

434 }

435 }

436

438 if (!Buf)

439 return {};

440

441

443 path.back()->getLocation().asLocation().getExpansionLoc().getFileID();

445 FinalizeHTML(D, R, SMgr, path, FileIDs[0], *Entry, declName);

446

447 std::string file;

448 llvm::raw_string_ostream os(file);

449 for (auto BI : *Buf)

450 os << BI;

451

452 return file;

453}

454

455void HTMLDiagnostics::dumpCoverageData(

458 llvm::raw_string_ostream &os) {

459

461

462 os << "var relevant_lines = {";

463 for (auto I = ExecutedLines.begin(),

464 E = ExecutedLines.end(); I != E; ++I) {

465 if (I != ExecutedLines.begin())

466 os << ", ";

467

468 os << "\"" << I->first.getHashValue() << "\": {";

469 for (unsigned LineNo : I->second) {

470 if (LineNo != *(I->second.begin()))

471 os << ", ";

472

473 os << "\"" << LineNo << "\": 1";

474 }

475 os << "}";

476 }

477

478 os << "};";

479}

480

481std::string HTMLDiagnostics::showRelevantLinesJavascript(

483 std::string s;

484 llvm::raw_string_ostream os(s);

485 os << "

535

536

537

538

539 Show only relevant lines

540

541 <input type="checkbox" name="showArrows"

542 id="showArrows" style="margin-left: 10px" />

543

544 Show control flow arrows

545

546

547)<<<";

548

549 return s;

550}

551

556

557

558

559

561

562 if (llvm::sys::path::is_relative(Entry.getName())) {

563 llvm::sys::fs::current_path(DirName);

564 DirName += '/';

565 }

566

567 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();

568 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();

569

571

573 generateKeyboardNavigationJavascript());

574

576 generateArrowDrawingJavascript());

577

578

580 showRelevantLinesJavascript(D, path));

581

582

583 {

584 std::string s;

585 llvm::raw_string_ostream os(s);

586

587 os << "\n"

588 << "

Bug Summary

\n<table class=\"simpletable\">\n"

589 "<td class=\"rowname\">File:"

592 << "\n<td class=\"rowname\">Warning:"

593 "<a href=\"#EndPath\">line "

594 << LineNumber

595 << ", column "

596 << ColumnNumber

597 << "
"

598 << D.getVerboseDescription() << "\n";

599

600

601 unsigned NumExtraPieces = 0;

602 for (const auto &Piece : path) {

603 if (const auto *P = dyn_cast(Piece.get())) {

604 int LineNumber =

605 P->getLocation().asLocation().getExpansionLineNumber();

606 int ColumnNumber =

607 P->getLocation().asLocation().getExpansionColumnNumber();

608 ++NumExtraPieces;

609 os << "<td class=\"rowname\">Note:"

610 << "<a href=\"#Note" << NumExtraPieces << "\">line "

611 << LineNumber << ", column " << ColumnNumber << "
"

612 << P->getString() << "";

613 }

614 }

615

616

617

618 for (const std::string &Metadata :

619 llvm::make_range(D.meta_begin(), D.meta_end())) {

620 os << "" << html::EscapeText(Metadata) << "\n";

621 }

622

623 os << R"<<<(

624

625

626

Annotated Source Code

627

Press '?'

628 to see keyboard shortcuts

629

630Show analyzer invocation

631

clang -cc1 )<<<";

633 os << R"<<<(

634

635

636

Keyboard shortcuts:

637

642 Close

643

644)<<<";

645

647 }

648

649

651 std::string s;

652 llvm::raw_string_ostream os(s);

653

654 StringRef BugDesc = D.getVerboseDescription();

655 if (!BugDesc.empty())

656 os << "\n\n";

657

658 StringRef BugType = D.getBugType();

660 os << "\n\n";

661

666 SMgr);

668 StringRef BugCategory = D.getCategory();

669 if (!BugCategory.empty())

670 os << "\n\n";

671

672 os << "\n\n";

673

674 os << "\n\n";

675

676 os << "\n\n";

677

678 os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)

679 << " -->\n";

680

681 os << "\n<!-- BUGLINE "

682 << LineNumber

683 << " -->\n";

684

685 os << "\n<!-- BUGCOLUMN "

686 << ColumnNumber

687 << " -->\n";

688

689 os << "\n\n";

690

691

692 os << "\n\n";

693

694

696 }

697

699}

700

701StringRef HTMLDiagnostics::showHelpJavascript() {

702 return R"<<<(

703

726)<<<";

727}

728

730 return !(Range.getBegin().isMacroID() || Range.getEnd().isMacroID());

731}

732

733static void

735 const std::vector &PopUpRanges) {

736 for (const auto &Range : PopUpRanges) {

738 continue;

739

741 "

",

742 true);

743 }

744}

745

748 std::vector &PopUpRanges,

749 unsigned int LastReportedPieceIndex,

750 unsigned int PopUpPieceIndex) {

752 llvm::raw_svector_ostream Out(Buf);

753

756 return;

757

758

759 Out << "

760 << LastReportedPieceIndex;

761

762

763 Out << '.' << PopUpPieceIndex;

764

765 Out << "

";

766

767

768 if (!llvm::is_contained(PopUpRanges, Range)) {

769

770 PopUpRanges.push_back(Range);

771

772 Out << "

"
" << Piece.getString() << "
";

774 "", Buf.c_str(),

775 true);

776 } else {

777

779 true);

780 }

781}

782

783void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,

785

786

787

788 unsigned TotalPieces = getPathSizeWithoutArrows(path);

789 unsigned TotalNotePieces =

791 return isa(*p);

792 });

793 unsigned PopUpPieceCount =

795 return isa(*p);

796 });

797

798 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;

799 unsigned NumRegularPieces = TotalRegularPieces;

800 unsigned NumNotePieces = TotalNotePieces;

801 unsigned NumberOfArrows = 0;

802

803 std::map<int, int> IndexMap;

804 ArrowMap ArrowIndices(TotalRegularPieces + 1);

805

806

807 std::vector PopUpRanges;

809 const auto &Piece = *I.get();

810

811 if (isa(Piece)) {

812 ++IndexMap[NumRegularPieces];

813 } else if (isa(Piece)) {

814

815

816

817 HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);

818 --NumNotePieces;

819

820 } else if (isArrowPiece(Piece)) {

821 NumberOfArrows = ProcessControlFlowPiece(

822 R, FID, cast(Piece), NumberOfArrows);

823 ArrowIndices[NumRegularPieces] = NumberOfArrows;

824

825 } else {

826 HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,

827 TotalRegularPieces);

828 --NumRegularPieces;

829 ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];

830 }

831 }

832 ArrowIndices[0] = NumberOfArrows;

833

834

835

836

837

838

839

840

841

842 assert(ArrowIndices.back() == 0 &&

843 "No arrows should be after the last event");

844

845 assert(llvm::is_sorted(ArrowIndices, std::greater()) &&

846 "Incorrect arrow indices map");

847

848

849

850 NumRegularPieces = TotalRegularPieces;

852 const auto &Piece = *I.get();

853

854 if (const auto *PopUpP = dyn_cast(&Piece)) {

855 int PopUpPieceIndex = IndexMap[NumRegularPieces];

856

857

858

859

860

861

863 PopUpPieceIndex);

864

865 if (PopUpPieceIndex > 0)

866 --IndexMap[NumRegularPieces];

867

868 } else if (!isa(Piece) && !isArrowPiece(Piece)) {

869 --NumRegularPieces;

870 }

871 }

872

873

875

876

879

880 addArrowSVGs(R, FID, ArrowIndices);

881

882

883

884

887}

888

889void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,

891 const std::vector &PopUpRanges,

892 unsigned num, unsigned max) {

893

894

896

898 return;

899

901 assert(&Pos.getManager() == &SM && "SourceManagers are different!");

902 std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);

903

904 if (LPosInfo.first != BugFileID)

905 return;

906

907 llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first);

908 const char *FileStart = Buf.getBufferStart();

909

910

911

912 unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);

914 const char *LineStart = TokInstantiationPtr-ColNo;

915

916

917 const char *LineEnd = TokInstantiationPtr;

918 const char *FileEnd = Buf.getBufferEnd();

919 while (*LineEnd != '\n' && LineEnd != FileEnd)

920 ++LineEnd;

921

922

923 unsigned PosNo = 0;

924 for (const char* c = LineStart; c != TokInstantiationPtr; ++c)

925 PosNo += *c == '\t' ? 8 : 1;

926

927

928

929 const char *Kind = nullptr;

930 bool IsNote = false;

931 bool SuppressIndex = (max == 1);

932 switch (P.getKind()) {

935

938 Kind = "Note";

939 IsNote = true;

940 SuppressIndex = true;

941 break;

944 llvm_unreachable("Calls and extra notes should already be handled");

945 }

946

947 std::string sbuf;

948 llvm::raw_string_ostream os(sbuf);

949

950 os << "\n<td class=\"num\"><td class=\"line\"><div id=\"";

951

952 if (IsNote)

953 os << "Note" << num;

954 else if (num == max)

955 os << "EndPath";

956 else

957 os << "Path" << num;

958

959 os << "\" class=\"msg";

960 if (Kind)

961 os << " msg" << Kind;

962 os << "\" style=\"margin-left:" << PosNo << "ex";

963

964

965 if (!isa(P)) {

966

967 const auto &Msg = P.getString();

968 unsigned max_token = 0;

969 unsigned cnt = 0;

970 unsigned len = Msg.size();

971

972 for (char C : Msg)

973 switch (C) {

974 default:

975 ++cnt;

976 continue;

977 case ' ':

978 case '\t':

979 case '\n':

980 if (cnt > max_token) max_token = cnt;

981 cnt = 0;

982 }

983

984 if (cnt > max_token)

985 max_token = cnt;

986

987

988 unsigned em;

989 const unsigned max_line = 120;

990

991 if (max_token >= max_line)

992 em = max_token / 2;

993 else {

994 unsigned characters = max_line;

995 unsigned lines = len / max_line;

996

997 if (lines > 0) {

998 for (; characters > max_token; --characters)

999 if (len / characters > lines) {

1000 ++characters;

1001 break;

1002 }

1003 }

1004

1005 em = characters / 2;

1006 }

1007

1008 if (em < max_line/2)

1009 os << "; max-width:" << em << "em";

1010 }

1011 else

1012 os << "; max-width:100em";

1013

1014 os << "\">";

1015

1016 if (!SuppressIndex) {

1017 os << "<table class=\"msgT\"><td valign=\"top\">";

1018 os << "<div class=\"PathIndex";

1019 if (Kind) os << " PathIndex" << Kind;

1020 os << "\">" << num << "";

1021

1022 if (num > 1) {

1023 os << "<div class=\"PathNav\"><a href=\"#Path"

1024 << (num - 1)

1025 << "\" title=\"Previous event ("

1026 << (num - 1)

1027 << ")\">←";

1028 }

1029

1030 os << "";

1031 }

1032

1033 if (const auto *MP = dyn_cast(&P)) {

1034 os << "Within the expansion of the macro '";

1035

1036

1037 {

1042 const char* MacroName = LocInfo.second + BufferInfo.data();

1043 Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),

1044 BufferInfo.begin(), MacroName, BufferInfo.end());

1045

1048 for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)

1049 os << MacroName[i];

1050 }

1051

1052 os << "':\n";

1053

1054 if (!SuppressIndex) {

1055 os << "";

1056 if (num < max) {

1057 os << "<div class=\"PathNav\"><a href=\"#";

1058 if (num == max - 1)

1059 os << "EndPath";

1060 else

1061 os << "Path" << (num + 1);

1062 os << "\" title=\"Next event ("

1063 << (num + 1)

1064 << ")\">→";

1065 }

1066

1067 os << "";

1068 }

1069

1070

1071 ProcessMacroPiece(os, *MP, 0);

1072 }

1073 else {

1075

1076 if (!SuppressIndex) {

1077 os << "";

1078 if (num < max) {

1079 os << "<div class=\"PathNav\"><a href=\"#";

1080 if (num == max - 1)

1081 os << "EndPath";

1082 else

1083 os << "Path" << (num + 1);

1084 os << "\" title=\"Next event ("

1085 << (num + 1)

1086 << ")\">→";

1088

1089 os << "";

1090 }

1091 }

1092

1093 os << "";

1094

1095

1096 unsigned DisplayPos = LineEnd - FileStart;

1098 SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);

1101

1102

1104 for (const auto &Range : Ranges) {

1105

1106 if (llvm::is_contained(PopUpRanges, Range))

1107 continue;

1108

1109 HighlightRange(R, LPosInfo.first, Range);

1110 }

1111}

1112

1114 unsigned x = n % ('z' - 'a');

1115 n /= 'z' - 'a';

1116

1117 if (n > 0)

1119

1120 os << char('a' + x);

1121}

1122

1123unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,

1125 unsigned num) {

1126 for (const auto &subPiece : P.subPieces) {

1127 if (const auto *MP = dyn_cast(subPiece.get())) {

1128 num = ProcessMacroPiece(os, *MP, num);

1129 continue;

1130 }

1131

1132 if (const auto *EP = dyn_cast(subPiece.get())) {

1133 os << "<div class=\"msg msgEvent\" style=\"width:94%; "

1134 "margin-left:5px\">"

1135 "<table class=\"msgT\">"

1136 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";

1138 os << "<td valign=\"top\">"

1140 << "\n";

1141 }

1142 }

1143

1144 return num;

1145}

1146

1147void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,

1148 const ArrowMap &ArrowIndices) {

1149 std::string S;

1150 llvm::raw_string_ostream OS(S);

1151

1152 OS << R"<<<(

1153

1182

1183

1184 <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"

1185 viewBox="0 0 10 10" refX="3" refY="5"

1186 markerWidth="4" markerHeight="4">

1187

1188

1189 <marker id="arrowhead" class="arrowhead" opacity="0.2"

1190 viewBox="0 0 10 10" refX="3" refY="5"

1191 markerWidth="4" markerHeight="4">

1192

1193

1194

1195

1196)<<<";

1197

1198 for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {

1199 OS << " <path class=\"arrow\" id=\"arrow" << Index << "\"/>\n";

1200 }

1201

1202 OS << R"<<<(

1203

1204

1205\n";

1209

1211 OS.str());

1212}

1213

1215 unsigned Index) {

1216 std::string Result;

1217 llvm::raw_string_ostream OS(Result);

1218 OS << "<span id=\"" << ClassName << Index << "\">";

1219 return Result;

1220}

1221

1224}

1225

1228}

1229

1230unsigned HTMLDiagnostics::ProcessControlFlowPiece(

1232 unsigned Number) {

1236

1237 HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),

1238 Start.c_str());

1239 HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),

1240 End.c_str());

1241 }

1242

1243 return Number;

1244}

1245

1246void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,

1248 const char *HighlightStart,

1249 const char *HighlightEnd) {

1252

1254 unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);

1255

1257 unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);

1258

1259 if (EndLineNo < StartLineNo)

1260 return;

1261

1262 if (SM.getFileID(InstantiationStart) != BugFileID ||

1263 SM.getFileID(InstantiationEnd) != BugFileID)

1264 return;

1265

1266

1267 unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);

1268 unsigned OldEndColNo = EndColNo;

1269

1270 if (EndColNo) {

1271

1273 }

1274

1275

1276

1277

1280

1282}

1283

1284StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {

1285 return R"<<<(

1286

1386 )<<<";

1387}

1388

1389StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {

1390 return R"<<<(

1391

1641 )<<<";

1642}

Defines the clang::FileManager interface and associated types.

static bool shouldDisplayPopUpRange(const SourceRange &Range)

static void EmitAlphaCounter(raw_ostream &os, unsigned n)

static std::string getSpanBeginForControl(const char *ClassName, unsigned Index)

static llvm::SmallString< 32 > getIssueHash(const PathDiagnostic &D, const Preprocessor &PP)

static void HandlePopUpPieceStartTag(Rewriter &R, const std::vector< SourceRange > &PopUpRanges)

static std::string getSpanBeginForControlEnd(unsigned Index)

static void HandlePopUpPieceEndTag(Rewriter &R, const PathDiagnosticPopUpPiece &Piece, std::vector< SourceRange > &PopUpRanges, unsigned int LastReportedPieceIndex, unsigned int PopUpPieceIndex)

static std::string getSpanBeginForControlStart(unsigned Index)

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)

Produce a diagnostic highlighting some portion of a literal.

Defines the clang::Preprocessor interface.

static std::string getName(const CallEvent &Call)

Defines the clang::SourceLocation class and associated facilities.

Defines the SourceManager interface.

__DEVICE__ int max(int __a, int __b)

__device__ __2f16 float __ockl_bool s

__device__ __2f16 float c

Decl - This represents one declaration (or definition), e.g.

SourceLocation getLocation() const

A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...

StringRef getName() const

The name of this FileEntry.

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

A SourceLocation and its associated SourceManager.

FullSourceLoc getExpansionLoc() const

const char * getCharacterData(bool *Invalid=nullptr) const

StringRef getBufferData(bool *Invalid=nullptr) const

Return a StringRef to the source buffer data for the specified FileID.

std::pair< FileID, unsigned > getDecomposedLoc() const

Decompose the specified location into a raw FileID + Offset pair.

const SourceManager & getManager() const

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.

bool LexFromRawLexer(Token &Result)

LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...

static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)

MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...

MacroExpansionContext tracks the macro expansions processed by the Preprocessor.

Engages in a tight little dance with the lexer to efficiently preprocess tokens.

SourceManager & getSourceManager() const

const LangOptions & getLangOpts() const

Rewriter - This is the main interface to the rewrite buffers.

bool InsertTextBefore(SourceLocation Loc, StringRef Str)

InsertText - Insert the specified string at the specified location in the original buffer.

SourceManager & getSourceMgr() const

const LangOptions & getLangOpts() const

const llvm::RewriteBuffer * getRewriteBufferFor(FileID FID) const

getRewriteBufferFor - Return the rewrite buffer for the specified FileID.

bool InsertTextAfter(SourceLocation Loc, StringRef Str)

InsertTextAfter - Insert the specified string at the specified location in the original buffer.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

SourceLocation getLocWithOffset(IntTy Offset) const

Return a source location with the specified offset from this SourceLocation.

This class handles loading and caching of source files into memory.

OptionalFileEntryRef getFileEntryRefForID(FileID FID) const

Returns the FileEntryRef for the provided FileID.

SourceLocation getLocForEndOfFile(FileID FID) const

Return the source location corresponding to the last byte of the specified file.

SourceLocation getLocForStartOfFile(FileID FID) const

Return the source location corresponding to the first byte of the specified file.

SourceLocation getExpansionLoc(SourceLocation Loc) const

Given a SourceLocation object Loc, return the expansion location referenced by the ID.

A trivial tuple used to represent a source range.

Stmt - This represents one statement.

Token - This structure provides full information about a lexed token.

unsigned getLength() const

This class is used for tools that requires cross translation unit capability.

@ Everything

Used for HTML, shows both "arrows" and control notes.

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

void FlushDiagnostics(FilesMade *FilesMade)

PathDiagnosticRange asRange() const

FullSourceLoc asLocation() const

StringRef getString() const

PathDiagnosticLocation getLocation() const override

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 AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, StringRef title)

void HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, const char *StartTag, const char *EndTag, bool IsTokenRange=true)

HighlightRange - Highlight a range in the source code with the specified start/end tags.

RelexRewriteCacheRef instantiateRelexRewriteCache()

If you need to rewrite the same file multiple times, you can instantiate a RelexRewriteCache and refe...

void AddLineNumbers(Rewriter &R, FileID FID)

void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)

SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords,...

void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)

HighlightMacros - This uses the macro table state from the end of the file, to reexpand macros and in...

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 ...

std::shared_ptr< RelexRewriteCache > RelexRewriteCacheRef

The JSON file list parser is used to communicate input to InstallAPI.

const StreamingDiagnostic & operator<<(const StreamingDiagnostic &DB, const ASTContext::SectionInfo &Section)

Insertion operator for diagnostics.

These options tweak the behavior of path diangostic consumers.

bool ShouldWriteVerboseReportFilename

If the consumer intends to produce multiple output files, should it use a pseudo-random file name or ...

std::string ToolInvocation

Run-line of the tool that produced the diagnostic.