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 ((FixItInsertionLine[FixItStart]))
358 break;
359
360 for (; FixItEnd != FixItStart; --FixItEnd)
361 if ((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 && (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 && (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 (.is(tok::raw_identifier) &&
.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 (->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