clang: lib/Frontend/TextDiagnostic.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

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

17#include "llvm/ADT/StringExtras.h"

18#include "llvm/Support/ConvertUTF.h"

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

20#include "llvm/Support/Locale.h"

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

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

23#include

24#include

25

26using namespace clang;

27

28static const enum raw_ostream::Colors noteColor = raw_ostream::CYAN;

29static const enum raw_ostream::Colors remarkColor =

30 raw_ostream::BLUE;

31static const enum raw_ostream::Colors fixitColor =

32 raw_ostream::GREEN;

33static const enum raw_ostream::Colors caretColor =

34 raw_ostream::GREEN;

36 raw_ostream::MAGENTA;

38 raw_ostream::CYAN;

39static const enum raw_ostream::Colors errorColor = raw_ostream::RED;

40static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;

41

42static const enum raw_ostream::Colors savedColor =

43 raw_ostream::SAVEDCOLOR;

44

45

46

47

48

49static constexpr raw_ostream::Colors CommentColor = raw_ostream::YELLOW;

50static constexpr raw_ostream::Colors LiteralColor = raw_ostream::GREEN;

51static constexpr raw_ostream::Colors KeywordColor = raw_ostream::BLUE;

52

53

55 bool &Normal, bool Bold) {

56 while (true) {

58 OS << Str.slice(0, Pos);

59 if (Pos == StringRef::npos)

60 break;

61

62 Str = Str.substr(Pos + 1);

65 else {

66 OS.resetColor();

67 if (Bold)

69 }

71 }

72}

73

74

76

79 while (0<i) {

80 if (SourceLine[--i]=='\t')

81 break;

83 }

85}

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106static std::pair<SmallString<16>, bool>

108 unsigned TabStop) {

109 assert(I && "I must not be null");

110 assert(*I < SourceLine.size() && "must point to a valid index");

111

112 if (SourceLine[*I] == '\t') {

114 "Invalid -ftabstop value");

116 unsigned NumSpaces = TabStop - (Col % TabStop);

117 assert(0 < NumSpaces && NumSpaces <= TabStop

118 && "Invalid computation of space amt");

119 ++(*I);

120

122 ExpandedTab.assign(NumSpaces, ' ');

123 return std::make_pair(ExpandedTab, true);

124 }

125

126 const unsigned char *Begin = SourceLine.bytes_begin() + *I;

127

128

129 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) {

130 ++(*I);

132 }

133 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin);

134 const unsigned char *End = Begin + CharSize;

135

136

137 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) {

138 llvm::UTF32 C;

139 llvm::UTF32 *CPtr = &C;

140

141

142 unsigned char const *OriginalBegin = Begin;

143 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(

144 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);

145 (void)Res;

146 assert(Res == llvm::conversionOK);

147 assert(OriginalBegin < Begin);

148 assert(unsigned(Begin - OriginalBegin) == CharSize);

149

150 (*I) += (Begin - OriginalBegin);

151

152

153 if (llvm::sys::locale::isPrint(C))

154 return std::make_pair(SmallString<16>(OriginalBegin, End), true);

155

156

158 while (C) {

159 Str.insert(Str.begin() + 3, llvm::hexdigit(C % 16));

160 C /= 16;

161 }

162 while (Str.size() < 8)

163 Str.insert(Str.begin() + 3, llvm::hexdigit(0));

164 return std::make_pair(Str, false);

165 }

166

167

169 unsigned char Byte = SourceLine[*I];

170 ExpandedByte[1] = llvm::hexdigit(Byte / 16);

171 ExpandedByte[2] = llvm::hexdigit(Byte % 16);

172 ++(*I);

173 return std::make_pair(ExpandedByte, false);

174}

175

176static void expandTabs(std::string &SourceLine, unsigned TabStop) {

177 size_t I = SourceLine.size();

178 while (I > 0) {

179 I--;

180 if (SourceLine[I] != '\t')

181 continue;

182 size_t TmpI = I;

183 auto [Str, Printable] =

185 SourceLine.replace(I, 1, Str.c_str());

186 }

187}

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

228 assert(BytesOut.empty());

229 assert(ColumnsOut.empty());

230

231 if (SourceLine.empty()) {

232 BytesOut.resize(1u, 0);

233 ColumnsOut.resize(1u, 0);

234 return;

235 }

236

237 ColumnsOut.resize(SourceLine.size() + 1, -1);

238

239 int Columns = 0;

240 size_t I = 0;

241 while (I < SourceLine.size()) {

242 ColumnsOut[I] = Columns;

243 BytesOut.resize(Columns + 1, -1);

244 BytesOut.back() = I;

245 auto [Str, Printable] =

247 Columns += llvm::sys::locale::columnWidth(Str);

248 }

249

250 ColumnsOut.back() = Columns;

251 BytesOut.resize(Columns + 1, -1);

252 BytesOut.back() = I;

253}

254

255namespace {

256struct SourceColumnMap {

257 SourceColumnMap(StringRef SourceLine, unsigned TabStop)

258 : m_SourceLine(SourceLine) {

259

261

262 assert(m_byteToColumn.size()==SourceLine.size()+1);

263 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size());

264 assert(m_byteToColumn.size()

265 == static_cast<unsigned>(m_columnToByte.back()+1));

266 assert(static_cast<unsigned>(m_byteToColumn.back()+1)

267 == m_columnToByte.size());

268 }

269 int columns() const { return m_byteToColumn.back(); }

270 int bytes() const { return m_columnToByte.back(); }

271

272

273

274 int byteToColumn(int n) const {

275 assert(0<=n && n<static_cast<int>(m_byteToColumn.size()));

276 return m_byteToColumn[n];

277 }

278

279

280 int byteToContainingColumn(int N) const {

281 assert(0 <= N && N < static_cast<int>(m_byteToColumn.size()));

282 while (m_byteToColumn[N] == -1)

283 --N;

284 return m_byteToColumn[N];

285 }

286

287

288

289

290 int columnToByte(int n) const {

291 assert(0<=n && n<static_cast<int>(m_columnToByte.size()));

292 return m_columnToByte[n];

293 }

294

295

296 int startOfNextColumn(int N) const {

297 assert(0 <= N && N < static_cast<int>(m_byteToColumn.size() - 1));

298 while (byteToColumn(++N) == -1) {}

299 return N;

300 }

301

302

303 int startOfPreviousColumn(int N) const {

304 assert(0 < N && N < static_cast<int>(m_byteToColumn.size()));

305 while (byteToColumn(--N) == -1) {}

306 return N;

307 }

308

309 StringRef getSourceLine() const {

310 return m_SourceLine;

311 }

312

313private:

314 const std::string m_SourceLine;

317};

318}

319

320

321

323 std::string &CaretLine,

324 std::string &FixItInsertionLine,

325 unsigned Columns,

326 const SourceColumnMap &map) {

327 unsigned CaretColumns = CaretLine.size();

328 unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);

329 unsigned MaxColumns = std::max(static_cast<unsigned>(map.columns()),

330 std::max(CaretColumns, FixItColumns));

331

332 if (MaxColumns <= Columns)

333 return;

334

335

336 assert(llvm::none_of(CaretLine, [](char c) { return c < ' ' || '~' < c; }));

337

338

339

340 unsigned CaretStart = 0, CaretEnd = CaretLine.size();

341 for (; CaretStart != CaretEnd; ++CaretStart)

343 break;

344

345 for (; CaretEnd != CaretStart; --CaretEnd)

347 break;

348

349

350

351

352

353

354 if (!FixItInsertionLine.empty()) {

355 unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();

356 for (; FixItStart != FixItEnd; ++FixItStart)

357 if (isWhitespace(FixItInsertionLine[FixItStart]))

358 break;

359

360 for (; FixItEnd != FixItStart; --FixItEnd)

361 if (isWhitespace(FixItInsertionLine[FixItEnd - 1]))

362 break;

363

364

365

366

367 unsigned FixItStartCol = FixItStart;

368 unsigned FixItEndCol

369 = llvm::sys::locale::columnWidth(FixItInsertionLine.substr(0, FixItEnd));

370

371 CaretStart = std::min(FixItStartCol, CaretStart);

372 CaretEnd = std::max(FixItEndCol, CaretEnd);

373 }

374

375

376

377

378 while (static_cast<int>(CaretEnd) < map.columns() &&

379 -1 == map.columnToByte(CaretEnd))

380 ++CaretEnd;

381

382 assert((static_cast<int>(CaretStart) > map.columns() ||

383 -1!=map.columnToByte(CaretStart)) &&

384 "CaretStart must not point to a column in the middle of a source"

385 " line character");

386 assert((static_cast<int>(CaretEnd) > map.columns() ||

387 -1!=map.columnToByte(CaretEnd)) &&

388 "CaretEnd must not point to a column in the middle of a source line"

389 " character");

390

391

392

393

394

395

396 unsigned SourceStart = map.columnToByte(std::min(CaretStart,

397 map.columns()));

398 unsigned SourceEnd = map.columnToByte(std::min(CaretEnd,

399 map.columns()));

400

401 unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart

402 - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart));

403

404 char const *front_ellipse = " ...";

405 char const *front_space = " ";

406 char const *back_ellipse = "...";

407 unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse);

408

409 unsigned TargetColumns = Columns;

410

411

412 if (TargetColumns > ellipses_space+CaretColumnsOutsideSource)

413 TargetColumns -= ellipses_space+CaretColumnsOutsideSource;

414

415 while (SourceStart>0 || SourceEnd<SourceLine.size()) {

416 bool ExpandedRegion = false;

417

418 if (SourceStart>0) {

419 unsigned NewStart = map.startOfPreviousColumn(SourceStart);

420

421

422

423

424 while (NewStart && isWhitespace(SourceLine[NewStart]))

425 NewStart = map.startOfPreviousColumn(NewStart);

426

427

428 while (NewStart) {

429 unsigned Prev = map.startOfPreviousColumn(NewStart);

431 break;

432 NewStart = Prev;

433 }

434

435 assert(map.byteToColumn(NewStart) != -1);

436 unsigned NewColumns = map.byteToColumn(SourceEnd) -

437 map.byteToColumn(NewStart);

438 if (NewColumns <= TargetColumns) {

439 SourceStart = NewStart;

440 ExpandedRegion = true;

441 }

442 }

443

444 if (SourceEnd<SourceLine.size()) {

445 unsigned NewEnd = map.startOfNextColumn(SourceEnd);

446

447

448

449

450 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd]))

451 NewEnd = map.startOfNextColumn(NewEnd);

452

453

454 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd]))

455 NewEnd = map.startOfNextColumn(NewEnd);

456

457 assert(map.byteToColumn(NewEnd) != -1);

458 unsigned NewColumns = map.byteToColumn(NewEnd) -

459 map.byteToColumn(SourceStart);

460 if (NewColumns <= TargetColumns) {

461 SourceEnd = NewEnd;

462 ExpandedRegion = true;

463 }

464 }

465

466 if (!ExpandedRegion)

467 break;

468 }

469

470 CaretStart = map.byteToColumn(SourceStart);

471 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;

472

473

474

475 assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 &&

476 SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1);

477 assert(SourceStart <= SourceEnd);

478 assert(CaretStart <= CaretEnd);

479

480 unsigned BackColumnsRemoved

481 = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd);

482 unsigned FrontColumnsRemoved = CaretStart;

483 unsigned ColumnsKept = CaretEnd-CaretStart;

484

485

486 assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns);

487

488

489

490 if (BackColumnsRemoved > strlen(back_ellipse))

491 SourceLine.replace(SourceEnd, std:🧵:npos, back_ellipse);

492

493

494 if (FrontColumnsRemoved+ColumnsKept <= Columns)

495 return;

496

497

498 if (FrontColumnsRemoved > strlen(front_ellipse)) {

499 SourceLine.replace(0, SourceStart, front_ellipse);

500 CaretLine.replace(0, CaretStart, front_space);

501 if (!FixItInsertionLine.empty())

502 FixItInsertionLine.replace(0, CaretStart, front_space);

503 }

504}

505

506

507

508

509

510

511

512static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) {

513 while (Idx < Length && isWhitespace(Str[Idx]))

514 ++Idx;

515 return Idx;

516}

517

518

519

520

521

522

523

525 switch (c) {

526 case '\'': return '\'';

527 case '`': return '\'';

528 case '"': return '"';

529 case '(': return ')';

530 case '[': return ']';

531 case '{': return '}';

532 default: break;

533 }

534

535 return 0;

536}

537

538

539

540

541

542

544 unsigned Length, unsigned Column,

545 unsigned Columns) {

546 assert(Start < Str.size() && "Invalid start position!");

547 unsigned End = Start + 1;

548

549

550 if (End == Str.size())

551 return End;

552

553

554

556 if (!EndPunct) {

557

558 while (End < Length && isWhitespace(Str[End]))

559 ++End;

560 return End;

561 }

562

563

564

566 PunctuationEndStack.push_back(EndPunct);

567 while (End < Length && !PunctuationEndStack.empty()) {

568 if (Str[End] == PunctuationEndStack.back())

569 PunctuationEndStack.pop_back();

571 PunctuationEndStack.push_back(SubEndPunct);

572

573 ++End;

574 }

575

576

577 while (End < Length && isWhitespace(Str[End]))

578 ++End;

579

580 unsigned PunctWordLength = End - Start;

581 if (

582 Column + PunctWordLength <= Columns ||

583

584

585 PunctWordLength < Columns/3)

586 return End;

587

588

589

590

591

593}

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns,

609 unsigned Column, bool Bold) {

610 const unsigned Length = std::min(Str.find('\n'), Str.size());

611 bool TextNormal = true;

612

613 bool Wrapped = false;

614 for (unsigned WordStart = 0, WordEnd; WordStart < Length;

615 WordStart = WordEnd) {

616

618 if (WordStart == Length)

619 break;

620

621

623

624

625 unsigned WordLength = WordEnd - WordStart;

626 if (Column + WordLength < Columns) {

627

628 if (WordStart) {

629 OS << ' ';

631 }

633 TextNormal, Bold);

634 Column += WordLength;

635 continue;

636 }

637

638

639

640 OS << '\n';

643 TextNormal, Bold);

645 Wrapped = true;

646 }

647

648

650

651 assert(TextNormal && "Text highlighted at end of diagnostic message.");

652

653 return Wrapped;

654}

655

660

662

667 uint64_t StartOfLocationInfo = OS.tell();

668

669

672

674 OS.resetColor();

675

680 Message, OS.tell() - StartOfLocationInfo,

682}

683

684 void

689

690 switch (Level) {

692 llvm_unreachable("Invalid diagnostic type");

698 }

699 }

700

701 switch (Level) {

703 llvm_unreachable("Invalid diagnostic type");

709 }

710

712 OS.resetColor();

713}

714

715

717 bool IsSupplemental,

718 StringRef Message,

719 unsigned CurrentColumn,

721 bool Bold = false;

723

724

726 Bold = true;

727 }

728

729 if (Columns)

731 else {

734 assert(Normal && "Formatting should have returned to normal");

735 }

736

738 OS.resetColor();

739 OS << '\n';

740}

741

743#ifdef _WIN32

745#endif

746 if (DiagOpts->AbsolutePath) {

747 auto File = SM.getFileManager().getOptionalFileRef(Filename);

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763#ifdef _WIN32

764 TmpFilename = File->getName();

765 llvm::sys::fs::make_absolute(TmpFilename);

766 llvm::sys::path::native(TmpFilename);

767 llvm::sys::path::remove_dots(TmpFilename, true);

768 Filename = StringRef(TmpFilename.data(), TmpFilename.size());

769#else

770 Filename = SM.getFileManager().getCanonicalName(*File);

771#endif

772 }

773 }

774

776}

777

778

779

780

781

782

783

788

791 emitFilename(FE->getName(), Loc.getManager());

792 OS << ": ";

793 }

794 }

795 return;

796 }

797 unsigned LineNo = PLoc.getLine();

798

800 return;

801

804

806 switch (DiagOpts->getFormat()) {

810 OS << ':' << LineNo;

811 break;

814 }

815

817

818 if (unsigned ColNo = PLoc.getColumn()) {

820 OS << ',';

821

822 if (LangOpts.MSCompatibilityVersion &&

824 ColNo--;

825 } else

826 OS << ':';

827 OS << ColNo;

828 }

829 switch (DiagOpts->getFormat()) {

834

835

836 OS << ')';

837 if (LangOpts.MSCompatibilityVersion &&

839 OS << ' ';

840 OS << ':';

841 break;

842 }

843

844 if (DiagOpts->ShowSourceRanges && !Ranges.empty()) {

845 FileID CaretFileID = Loc.getExpansionLoc().getFileID();

846 bool PrintedRange = false;

848

849 for (const auto &R : Ranges) {

850

851 if (!R.isValid())

852 continue;

853

857

858

859

860 if (SM.getFileID(B) != CaretFileID || SM.getFileID(E) != CaretFileID)

861 continue;

862

863

864

865 unsigned TokSize = 0;

868

870 OS << '{'

871 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-'

873 << '}';

874 PrintedRange = true;

875 }

876

877 if (PrintedRange)

878 OS << ':';

879 }

880 OS << ' ';

881}

882

885 OS << "In file included from ";

887 OS << ':' << PLoc.getLine() << ":\n";

888 } else

889 OS << "In included file:\n";

890}

891

893 StringRef ModuleName) {

895 OS << "In module '" << ModuleName << "' imported from "

897 else

898 OS << "In module '" << ModuleName << "':\n";

899}

900

903 StringRef ModuleName) {

905 OS << "While building module '" << ModuleName << "' imported from "

907 else

908 OS << "While building module '" << ModuleName << "':\n";

909}

910

911

912static std::optional<std::pair<unsigned, unsigned>>

916 return std::nullopt;

917

920 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID)

921 return std::nullopt;

922

923 return std::make_pair(SM.getExpansionLineNumber(Begin),

924 SM.getExpansionLineNumber(End));

925}

926

927

928

929static std::pair<unsigned, unsigned>

930maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,

931 unsigned MaxRange) {

932

933 unsigned Slack = MaxRange - (A.second - A.first + 1);

934 if (Slack == 0)

935 return A;

936

937

938 unsigned Min = std::min(A.first, B.first);

939 unsigned Max = std::max(A.second, B.second);

940 if (Max - Min + 1 <= MaxRange)

942

943

944

945 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||

946 (B.second < A.second && A.second - B.second + 1 > MaxRange))

947 return A;

948

949

950

951

952

953

954

955 A.second = std::min(A.second + (Slack + 1) / 2, Max);

956 Slack = MaxRange - (A.second - A.first + 1);

957 A.first = std::max(Min + Slack, A.first) - Slack;

958 A.second = std::min(A.first + MaxRange - 1, Max);

959 return A;

960}

961

966};

967

968

970 std::string &CaretLine) {

971

972 unsigned StartColNo = R.StartCol;

973 while (StartColNo < Map.getSourceLine().size() &&

974 (Map.getSourceLine()[StartColNo] == ' ' ||

975 Map.getSourceLine()[StartColNo] == '\t'))

976 StartColNo = Map.startOfNextColumn(StartColNo);

977

978

979 unsigned EndColNo =

980 std::min(static_cast<size_t>(R.EndCol), Map.getSourceLine().size());

981 while (EndColNo && (Map.getSourceLine()[EndColNo - 1] == ' ' ||

982 Map.getSourceLine()[EndColNo - 1] == '\t'))

983 EndColNo = Map.startOfPreviousColumn(EndColNo);

984

985

986

987

988 if (StartColNo > EndColNo)

989 return;

990

991

992 StartColNo = Map.byteToContainingColumn(StartColNo);

993 EndColNo = Map.byteToContainingColumn(EndColNo);

994

995 assert(StartColNo <= EndColNo && "Invalid range!");

996 if (CaretLine.size() < EndColNo)

997 CaretLine.resize(EndColNo, ' ');

998 std::fill(CaretLine.begin() + StartColNo, CaretLine.begin() + EndColNo, '~');

999}

1000

1002 unsigned LineNo,

1003 const SourceColumnMap &map,

1007 std::string FixItInsertionLine;

1008 if (Hints.empty() || !DiagOpts->ShowFixits)

1009 return FixItInsertionLine;

1010 unsigned PrevHintEndCol = 0;

1011

1012 for (const auto &H : Hints) {

1013 if (H.CodeToInsert.empty())

1014 continue;

1015

1016

1017

1018 std::pair<FileID, unsigned> HintLocInfo =

1019 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin());

1020 if (FID == HintLocInfo.first &&

1021 LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&

1022 StringRef(H.CodeToInsert).find_first_of("\n\r") == StringRef::npos) {

1023

1024

1025

1026

1027

1028 unsigned HintByteOffset =

1029 SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;

1030

1031

1032 assert(HintByteOffset < static_cast<unsigned>(map.bytes()) + 1);

1033 unsigned HintCol = map.byteToContainingColumn(HintByteOffset);

1034

1035

1036

1037

1038

1039

1040

1041

1042 if (HintCol < PrevHintEndCol)

1043 HintCol = PrevHintEndCol + 1;

1044

1045

1046

1047 unsigned NewFixItLineSize = FixItInsertionLine.size() +

1048 (HintCol - PrevHintEndCol) +

1049 H.CodeToInsert.size();

1050 if (NewFixItLineSize > FixItInsertionLine.size())

1051 FixItInsertionLine.resize(NewFixItLineSize, ' ');

1052

1053 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),

1054 FixItInsertionLine.end() - H.CodeToInsert.size());

1055

1056 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);

1057 }

1058 }

1059

1060 expandTabs(FixItInsertionLine, DiagOpts->TabStop);

1061

1062 return FixItInsertionLine;

1063}

1064

1066 unsigned L = 1u, M = 10u;

1067 while (M <= N && ++L != std::numeric_limits::digits10 + 1)

1068 M *= 10u;

1069

1070 return L;

1071}

1072

1073

1074

1075

1076

1077

1081 const std::pair<unsigned, unsigned> &Lines, FileID FID,

1084

1086 if (R.isInvalid())

1087 continue;

1090

1091 unsigned StartLineNo = SM.getExpansionLineNumber(Begin);

1092 if (StartLineNo > Lines.second || SM.getFileID(Begin) != FID)

1093 continue;

1094

1095 unsigned EndLineNo = SM.getExpansionLineNumber(End);

1096 if (EndLineNo < Lines.first || SM.getFileID(End) != FID)

1097 continue;

1098

1099 unsigned StartColumn = SM.getExpansionColumnNumber(Begin);

1100 unsigned EndColumn = SM.getExpansionColumnNumber(End);

1101 if (R.isTokenRange())

1103

1104

1105 if (StartLineNo == EndLineNo) {

1106 LineRanges.push_back({StartLineNo, StartColumn - 1, EndColumn - 1});

1107 continue;

1108 }

1109

1110

1111 LineRanges.push_back({StartLineNo, StartColumn - 1, ~0u});

1112

1113

1114 for (unsigned S = StartLineNo + 1; S != EndLineNo; ++S)

1115 LineRanges.push_back({S, 0, ~0u});

1116

1117

1118 LineRanges.push_back({EndLineNo, 0, EndColumn - 1});

1119 }

1120

1121 return LineRanges;

1122}

1123

1124

1125

1126

1127

1128

1129

1130

1131static std::unique_ptr<llvm::SmallVectorTextDiagnostic::StyleRange[]>

1133 unsigned EndLineNumber, const Preprocessor *PP,

1136 assert(StartLineNumber <= EndLineNumber);

1137 auto SnippetRanges =

1138 std::make_unique<SmallVectorTextDiagnostic::StyleRange[]>(

1139 EndLineNumber - StartLineNumber + 1);

1140

1142 return SnippetRanges;

1143

1144

1146 return SnippetRanges;

1147

1148 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);

1149 Lexer L{FID, *Buff, SM, LangOpts};

1150 L.SetKeepWhitespaceMode(true);

1151

1152 const char *FirstLineStart =

1153 FileData.data() +

1154 SM.getDecomposedLoc(SM.translateLineCol(FID, StartLineNumber, 1)).second;

1155 if (const char *CheckPoint = PP->getCheckPoint(FID, FirstLineStart)) {

1156 assert(CheckPoint >= Buff->getBufferStart() &&

1157 CheckPoint <= Buff->getBufferEnd());

1158 assert(CheckPoint <= FirstLineStart);

1159 size_t Offset = CheckPoint - Buff->getBufferStart();

1160 L.seek(Offset, false);

1161 }

1162

1163

1164 auto appendStyle =

1166 const Token &T, unsigned Start, unsigned Length) -> void {

1167 if (T.is(tok::raw_identifier)) {

1168 StringRef RawIdent = T.getRawIdentifier();

1169

1170

1171

1172

1173 if (llvm::StringSwitch(RawIdent)

1174 .Case("true", true)

1175 .Case("false", true)

1176 .Case("nullptr", true)

1177 .Case("__func__", true)

1178 .Case("__objc_yes__", true)

1179 .Case("__objc_no__", true)

1180 .Case("__null", true)

1181 .Case("__FUNCDNAME__", true)

1182 .Case("__FUNCSIG__", true)

1183 .Case("__FUNCTION__", true)

1184 .Case("__FUNCSIG__", true)

1185 .Default(false)) {

1186 Vec.emplace_back(Start, Start + Length, LiteralColor);

1187 } else {

1189 assert(II);

1191 Vec.emplace_back(Start, Start + Length, KeywordColor);

1192 }

1194 Vec.emplace_back(Start, Start + Length, LiteralColor);

1195 } else {

1196 assert(T.is(tok::comment));

1197 Vec.emplace_back(Start, Start + Length, CommentColor);

1198 }

1199 };

1200

1201 bool Stop = false;

1202 while (!Stop) {

1204 Stop = L.LexFromRawLexer(T);

1205 if (T.is(tok::unknown))

1206 continue;

1207

1208

1209 if (T.is(tok::raw_identifier) && T.is(tok::comment) &&

1211 continue;

1212

1214 unsigned TokenEndLine = SM.getSpellingLineNumber(T.getEndLoc(), &Invalid);

1215 if (Invalid || TokenEndLine < StartLineNumber)

1216 continue;

1217

1218 assert(TokenEndLine >= StartLineNumber);

1219

1220 unsigned TokenStartLine =

1221 SM.getSpellingLineNumber(T.getLocation(), &Invalid);

1223 continue;

1224

1225 if (TokenStartLine > EndLineNumber)

1226 break;

1227

1228 unsigned StartCol =

1229 SM.getSpellingColumnNumber(T.getLocation(), &Invalid) - 1;

1231 continue;

1232

1233

1234 if (TokenStartLine == TokenEndLine) {

1236 SnippetRanges[TokenStartLine - StartLineNumber];

1237 appendStyle(LineRanges, T, StartCol, T.getLength());

1238 continue;

1239 }

1240 assert((TokenEndLine - TokenStartLine) >= 1);

1241

1242

1243

1244 unsigned EndCol = SM.getSpellingColumnNumber(T.getEndLoc(), &Invalid) - 1;

1246 continue;

1247

1249

1250 unsigned L = TokenStartLine;

1251 unsigned LineLength = 0;

1252 for (unsigned I = 0; I <= Spelling.size(); ++I) {

1253

1255 if (L >= StartLineNumber) {

1257 SnippetRanges[L - StartLineNumber];

1258

1259 if (L == TokenStartLine)

1260 appendStyle(LineRanges, T, StartCol, LineLength);

1261 else if (L == TokenEndLine)

1262 appendStyle(LineRanges, T, 0, EndCol);

1263 else

1264 appendStyle(LineRanges, T, 0, LineLength);

1265 }

1266

1267 ++L;

1268 if (L > EndLineNumber)

1269 break;

1270 LineLength = 0;

1271 continue;

1272 }

1273 ++LineLength;

1274 }

1275 }

1276

1277 return SnippetRanges;

1278}

1279

1280

1281

1282

1283

1284

1285

1286

1287void TextDiagnostic::emitSnippetAndCaret(

1290 assert(Loc.isValid() && "must have a valid source location here");

1291 assert(Loc.isFileID() && "must have a file location here");

1292

1293

1294

1295

1296

1297

1298

1300 return;

1301 if (Loc == LastLoc && Ranges.empty() && Hints.empty() &&

1303 return;

1304

1307

1308

1310 StringRef BufData = Loc.getBufferData(&Invalid);

1312 return;

1313 const char *BufStart = BufData.data();

1314 const char *BufEnd = BufStart + BufData.size();

1315

1316 unsigned CaretLineNo = Loc.getLineNumber();

1317 unsigned CaretColNo = Loc.getColumnNumber();

1318

1319

1320 static const size_t MaxLineLengthToPrint = 4096;

1321 if (CaretColNo > MaxLineLengthToPrint)

1322 return;

1323

1324

1325 const unsigned MaxLines = DiagOpts->SnippetLineLimit;

1326 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};

1327 unsigned DisplayLineNo = Loc.getPresumedLoc().getLine();

1328 for (const auto &I : Ranges) {

1330 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines);

1331

1332 DisplayLineNo =

1333 std::min(DisplayLineNo, SM.getPresumedLineNumber(I.getBegin()));

1334 }

1335

1336

1337

1338

1339

1340 unsigned MaxLineNoDisplayWidth =

1343 : 0;

1344 auto indentForLineNumbers = [&] {

1345 if (MaxLineNoDisplayWidth > 0)

1346 OS.indent(MaxLineNoDisplayWidth + 2) << "| ";

1347 };

1348

1349

1350

1351 std::unique_ptr<SmallVector[]> SourceStyles =

1354

1357

1358 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1;

1359 ++LineNo, ++DisplayLineNo) {

1360

1361 const char *LineStart =

1362 BufStart +

1363 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second;

1364 if (LineStart == BufEnd)

1365 break;

1366

1367

1368 const char *LineEnd = LineStart;

1369 while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd)

1370 ++LineEnd;

1371

1372

1373

1374 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint)

1375 return;

1376

1377

1378 std::string SourceLine(LineStart, LineEnd);

1379

1380 while (!SourceLine.empty() && SourceLine.back() == '\0' &&

1381 (LineNo != CaretLineNo || SourceLine.size() > CaretColNo))

1382 SourceLine.pop_back();

1383

1384

1385 const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop);

1386

1387 std::string CaretLine;

1388

1389 for (const auto &LR : LineRanges) {

1390 if (LR.LineNo == LineNo)

1392 }

1393

1394

1395 if (CaretLineNo == LineNo) {

1396 size_t Col = sourceColMap.byteToContainingColumn(CaretColNo - 1);

1397 CaretLine.resize(std::max(Col + 1, CaretLine.size()), ' ');

1398 CaretLine[Col] = '^';

1399 }

1400

1402 FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get());

1403

1404

1405

1406 unsigned Columns = DiagOpts->MessageLength;

1407 if (Columns)

1409 Columns, sourceColMap);

1410

1411

1412

1413

1414

1415 if (DiagOpts->ShowSourceRanges && !SourceLine.empty()) {

1416 SourceLine = ' ' + SourceLine;

1417 CaretLine = ' ' + CaretLine;

1418 }

1419

1420

1421 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,

1422 SourceStyles[LineNo - Lines.first]);

1423

1424 if (!CaretLine.empty()) {

1425 indentForLineNumbers();

1428 OS << CaretLine << '\n';

1430 OS.resetColor();

1431 }

1432

1433 if (!FixItInsertionLine.empty()) {

1434 indentForLineNumbers();

1436

1438 if (DiagOpts->ShowSourceRanges)

1439 OS << ' ';

1440 OS << FixItInsertionLine << '\n';

1442 OS.resetColor();

1443 }

1444 }

1445

1446

1447 emitParseableFixits(Hints, SM);

1448}

1449

1450void TextDiagnostic::emitSnippet(StringRef SourceLine,

1451 unsigned MaxLineNoDisplayWidth,

1452 unsigned LineNo, unsigned DisplayLineNo,

1454

1455 if (MaxLineNoDisplayWidth > 0) {

1457 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)

1458 << DisplayLineNo << " | ";

1459 }

1460

1461

1462 bool PrintReversed = false;

1463 std::optionalllvm::raw\_ostream::Colors CurrentColor;

1464 size_t I = 0;

1465 while (I < SourceLine.size()) {

1466 auto [Str, WasPrintable] =

1468

1469

1471 if (WasPrintable == PrintReversed) {

1472 PrintReversed = !PrintReversed;

1473 if (PrintReversed)

1474 OS.reverseColor();

1475 else {

1476 OS.resetColor();

1477 CurrentColor = std::nullopt;

1478 }

1479 }

1480

1481

1482 const auto *CharStyle = llvm::find_if(Styles, [I](const StyleRange &R) {

1483 return (R.Start < I && R.End >= I);

1484 });

1485

1486 if (CharStyle != Styles.end()) {

1487 if (!CurrentColor ||

1488 (CurrentColor && *CurrentColor != CharStyle->Color)) {

1489 OS.changeColor(CharStyle->Color, false);

1490 CurrentColor = CharStyle->Color;

1491 }

1492 } else if (CurrentColor) {

1493 OS.resetColor();

1494 CurrentColor = std::nullopt;

1495 }

1496 }

1497

1498 OS << Str;

1499 }

1500

1502 OS.resetColor();

1503

1504 OS << '\n';

1505}

1506

1509 if (DiagOpts->ShowParseableFixits)

1510 return;

1511

1512

1513

1514 for (const auto &H : Hints) {

1515 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||

1516 H.RemoveRange.getEnd().isMacroID())

1517 return;

1518 }

1519

1520 for (const auto &H : Hints) {

1523

1524 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);

1525 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);

1526

1527

1528 if (H.RemoveRange.isTokenRange())

1530

1531

1532

1535 break;

1536

1537 OS << "fix-it:\"";

1539 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)

1540 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)

1541 << '-' << SM.getLineNumber(EInfo.first, EInfo.second)

1542 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second)

1543 << "}:\"";

1544 OS.write_escaped(H.CodeToInsert);

1545 OS << "\"\n";

1546 }

1547}

static StringRef bytes(const std::vector< T, Allocator > &v)

Defines the clang::FileManager interface and associated types.

Defines the clang::Preprocessor interface.

Defines the SourceManager interface.

static enum raw_ostream::Colors caretColor

static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i)

static std::pair< unsigned, unsigned > maybeAddRange(std::pair< unsigned, unsigned > A, std::pair< unsigned, unsigned > B, unsigned MaxRange)

Add as much of range B into range A as possible without exceeding a maximum size of MaxRange.

static constexpr raw_ostream::Colors CommentColor

static constexpr raw_ostream::Colors LiteralColor

static enum raw_ostream::Colors fixitColor

static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, bool &Normal, bool Bold)

Add highlights to differences in template strings.

static enum raw_ostream::Colors savedColor

static enum raw_ostream::Colors errorColor

static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length)

Skip over whitespace in the string, starting at the given index.

static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns, unsigned Column, bool Bold)

Print the given string to a stream, word-wrapping it to some number of columns in the process.

static unsigned findEndOfWord(unsigned Start, StringRef Str, unsigned Length, unsigned Column, unsigned Columns)

Find the end of the word starting at the given offset within a string.

static std::pair< SmallString< 16 >, bool > printableTextForNextCharacter(StringRef SourceLine, size_t *I, unsigned TabStop)

returns a printable representation of first item from input range

static enum raw_ostream::Colors remarkColor

static enum raw_ostream::Colors fatalColor

static std::unique_ptr< llvm::SmallVector< TextDiagnostic::StyleRange >[]> highlightLines(StringRef FileData, unsigned StartLineNumber, unsigned EndLineNumber, const Preprocessor *PP, const LangOptions &LangOpts, bool ShowColors, FileID FID, const SourceManager &SM)

Creates syntax highlighting information in form of StyleRanges.

static constexpr raw_ostream::Colors KeywordColor

static std::optional< std::pair< unsigned, unsigned > > findLinesForRange(const CharSourceRange &R, FileID FID, const SourceManager &SM)

Find the suitable set of lines to show to include a set of ranges.

static void selectInterestingSourceRegion(std::string &SourceLine, std::string &CaretLine, std::string &FixItInsertionLine, unsigned Columns, const SourceColumnMap &map)

When the source code line we want to print is too long for the terminal, select the "interesting" reg...

static std::string buildFixItInsertionLine(FileID FID, unsigned LineNo, const SourceColumnMap &map, ArrayRef< FixItHint > Hints, const SourceManager &SM, const DiagnosticOptions *DiagOpts)

static enum raw_ostream::Colors warningColor

static char findMatchingPunctuation(char c)

If the given character is the start of some kind of balanced punctuation (e.g., quotes or parentheses...

static enum raw_ostream::Colors noteColor

static void expandTabs(std::string &SourceLine, unsigned TabStop)

static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop, SmallVectorImpl< int > &BytesOut, SmallVectorImpl< int > &ColumnsOut)

BytesOut: A mapping from columns to the byte of the source line that produced the character displayin...

static void highlightRange(const LineRange &R, const SourceColumnMap &Map, std::string &CaretLine)

Highlight R (with ~'s) on the current source line.

const unsigned WordWrapIndentation

Number of spaces to indent when word-wrapping.

static unsigned getNumDisplayWidth(unsigned N)

static enum raw_ostream::Colors templateColor

static SmallVector< LineRange > prepareAndFilterRanges(const SmallVectorImpl< CharSourceRange > &Ranges, const SourceManager &SM, const std::pair< unsigned, unsigned > &Lines, FileID FID, const LangOptions &LangOpts)

Filter out invalid ranges, ranges that don't fit into the window of source lines we will print,...

__device__ __2f16 float c

Represents a character-granular source range.

bool isTokenRange() const

Return true if the end of this range specifies the start of the last token.

SourceLocation getEnd() const

SourceLocation getBegin() const

Options for controlling the compiler diagnostics engine.

Class to encapsulate the logic for formatting a diagnostic message.

const LangOptions & LangOpts

SourceLocation LastLoc

The location of the previous diagnostic if known.

DiagnosticsEngine::Level LastLevel

The level of the last diagnostic emitted.

IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts

Level

The level of the diagnostic, after it has been through mapping.

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

A SourceLocation and its associated SourceManager.

unsigned getColumnNumber(bool *Invalid=nullptr) const

unsigned getLineNumber(bool *Invalid=nullptr) const

One of these records is kept for each identifier that is lexed.

bool isKeyword(const LangOptions &LangOpts) const

Return true if this token is a keyword in the specified language.

IdentifierInfoLookup * getExternalIdentifierLookup() const

Retrieve the external identifier lookup object, if any.

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

bool isCompatibleWithMSVC(MSVCMajorVersion MajorVersion) const

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

static unsigned getSpelling(const Token &Tok, const char *&Buffer, const SourceManager &SourceMgr, const LangOptions &LangOpts, bool *Invalid=nullptr)

getSpelling - This method is used to get the spelling of a token into a preallocated buffer,...

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

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

const char * getCheckPoint(FileID FID, const char *Start) const

Returns a pointer into the given file's buffer that's guaranteed to be between tokens.

IdentifierInfo * getIdentifierInfo(StringRef Name) const

Return information about the specified preprocessor identifier token.

IdentifierTable & getIdentifierTable()

Represents an unpacked "presumed" location which can be presented to the user.

unsigned getColumn() const

Return the presumed column number of this location.

const char * getFilename() const

Return the presumed filename of this location.

unsigned getLine() const

Return the presumed line number of this location.

bool isInvalid() const

Return true if this object is invalid or uninitialized.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

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

static void printDiagnosticMessage(raw_ostream &OS, bool IsSupplemental, StringRef Message, unsigned CurrentColumn, unsigned Columns, bool ShowColors)

Pretty-print a diagnostic message to a raw_ostream.

~TextDiagnostic() override

void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override

void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override

static void printDiagnosticLevel(raw_ostream &OS, DiagnosticsEngine::Level Level, bool ShowColors)

Print the diagonstic level to a raw_ostream.

TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, DiagnosticOptions *DiagOpts, const Preprocessor *PP=nullptr)

void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef< CharSourceRange > Ranges) override

Print out the file/line/column information and include trace.

void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, DiagOrStoredDiag D) override

void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override

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

bool isLiteral(TokenKind K)

Return true if this is a "literal" kind, like a numeric constant, string, etc.

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

static const TerminalColor CommentColor

LLVM_READONLY bool isVerticalWhitespace(unsigned char c)

Returns true if this character is vertical ASCII whitespace: '\n', '\r'.

llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag

LLVM_READONLY bool isWhitespace(unsigned char c)

Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...

const char ToggleHighlight

Special character that the diagnostic printer will use to toggle the bold attribute.

const FunctionProtoType * T