16#include "llvm/ADT/StringExtras.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/ErrorHandling.h"
19#include "llvm/Support/Locale.h"
25static constexpr raw_ostream::Colors
NoteColor = raw_ostream::CYAN;
26static constexpr raw_ostream::Colors
RemarkColor = raw_ostream::BLUE;
27static constexpr raw_ostream::Colors
FixitColor = raw_ostream::GREEN;
28static constexpr raw_ostream::Colors
CaretColor = raw_ostream::GREEN;
29static constexpr raw_ostream::Colors
WarningColor = raw_ostream::MAGENTA;
31static constexpr raw_ostream::Colors
ErrorColor = raw_ostream::RED;
32static constexpr raw_ostream::Colors
FatalColor = raw_ostream::RED;
34static constexpr raw_ostream::Colors
SavedColor = raw_ostream::SAVEDCOLOR;
40static constexpr raw_ostream::Colors
CommentColor = raw_ostream::YELLOW;
41static constexpr raw_ostream::Colors
LiteralColor = raw_ostream::GREEN;
42static constexpr raw_ostream::Colors
KeywordColor = raw_ostream::BLUE;
45template <
typename Sub>
class ColumnsOrBytes {
48 ColumnsOrBytes(
int V) :
V(
V) {}
49 bool isValid()
const {
return V != -1; }
50 Sub next()
const {
return Sub(
V + 1); }
51 Sub prev()
const {
return Sub(
V - 1); }
53 bool operator>(Sub O)
const {
return V > O.V; }
54 bool operator<(Sub O)
const {
return V < O.V; }
58 Sub operator+(Sub B)
const {
return Sub(
V + B.V); }
61 return *
static_cast<Sub *
>(
this);
63 Sub operator-(Sub B)
const {
return Sub(
V - B.V); }
64 Sub &operator-=(Sub B) {
66 return *
static_cast<Sub *
>(
this);
70class Bytes final :
public ColumnsOrBytes<Bytes> {
72 Bytes(
int V) : ColumnsOrBytes(
V) {}
75class Columns final :
public ColumnsOrBytes<Columns> {
77 Columns(
int V) : ColumnsOrBytes(
V) {}
86 OS << Str.slice(0, Pos);
87 if (Pos == StringRef::npos)
90 Str = Str.substr(Pos + 1);
108 if (SourceLine[--i]==
'\t')
134static std::pair<SmallString<16>,
bool>
137 assert(I &&
"I must not be null");
138 assert(*I < SourceLine.size() &&
"must point to a valid index");
140 if (SourceLine[*I] ==
'\t') {
142 "Invalid -ftabstop value");
144 unsigned NumSpaces = TabStop - (LineBytes % TabStop);
145 assert(0 < NumSpaces && NumSpaces <= TabStop
146 &&
"Invalid computation of space amt");
150 ExpandedTab.assign(NumSpaces,
' ');
151 return std::make_pair(ExpandedTab,
true);
154 const unsigned char *Begin = SourceLine.bytes_begin() + *I;
157 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) {
161 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin);
162 const unsigned char *End = Begin + CharSize;
165 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) {
167 llvm::UTF32 *CPtr = &
C;
170 unsigned char const *OriginalBegin = Begin;
171 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
172 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);
174 assert(Res == llvm::conversionOK);
175 assert(OriginalBegin < Begin);
176 assert(
unsigned(Begin - OriginalBegin) == CharSize);
178 (*I) += (Begin - OriginalBegin);
181 if (llvm::sys::locale::isPrint(
C))
187 Str.insert(Str.begin() + 3, llvm::hexdigit(
C % 16));
190 while (Str.size() < 8)
191 Str.insert(Str.begin() + 3, llvm::hexdigit(0));
192 return std::make_pair(Str,
false);
197 unsigned char Byte = SourceLine[*I];
198 ExpandedByte[1] = llvm::hexdigit(Byte / 16);
199 ExpandedByte[2] = llvm::hexdigit(Byte % 16);
201 return std::make_pair(ExpandedByte,
false);
204static void expandTabs(std::string &SourceLine,
unsigned TabStop) {
205 size_t I = SourceLine.size();
208 if (SourceLine[I] !=
'\t')
211 auto [Str, Printable] =
213 SourceLine.replace(I, 1, Str.c_str());
256 assert(BytesOut.empty());
257 assert(ColumnsOut.empty());
259 if (SourceLine.empty()) {
260 BytesOut.resize(1u, Bytes(0));
261 ColumnsOut.resize(1u, Columns(0));
265 ColumnsOut.resize(SourceLine.size() + 1, -1);
267 Columns NumColumns = 0;
269 while (I < SourceLine.size()) {
270 ColumnsOut[I] = NumColumns;
271 BytesOut.resize(NumColumns.V + 1, -1);
272 BytesOut.back() = Bytes(I);
273 auto [Str, Printable] =
275 NumColumns += Columns(llvm::sys::locale::columnWidth(Str));
278 ColumnsOut.back() = NumColumns;
279 BytesOut.resize(NumColumns.V + 1, -1);
280 BytesOut.back() = Bytes(I);
284struct SourceColumnMap {
285 SourceColumnMap(StringRef SourceLine,
unsigned TabStop)
286 : SourceLine(SourceLine) {
290 assert(ByteToColumn.size() == SourceLine.size() + 1);
291 assert(0 < ByteToColumn.size() && 0 < ColumnToByte.size());
292 assert(ByteToColumn.size() ==
293 static_cast<unsigned>(ColumnToByte.back().V + 1));
294 assert(
static_cast<unsigned>(ByteToColumn.back().V + 1) ==
295 ColumnToByte.size());
297 Columns columns()
const {
return ByteToColumn.back(); }
298 Bytes
bytes()
const {
return ColumnToByte.back(); }
302 Columns byteToColumn(Bytes N)
const {
303 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size()));
304 return ByteToColumn[N.V];
308 Columns byteToContainingColumn(Bytes N)
const {
309 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size()));
310 while (!ByteToColumn[N.V].isValid())
312 return ByteToColumn[N.V];
318 Bytes columnToByte(Columns N)
const {
319 assert(0 <= N.V && N.V <
static_cast<int>(ColumnToByte.size()));
320 return ColumnToByte[N.V];
324 Bytes startOfNextColumn(Bytes N)
const {
325 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size() - 1));
327 while (!byteToColumn(N).isValid())
333 Bytes startOfPreviousColumn(Bytes N)
const {
334 assert(0 < N.V && N.V <
static_cast<int>(ByteToColumn.size()));
336 while (!byteToColumn(N).isValid())
341 StringRef getSourceLine()
const {
return SourceLine; }
344 StringRef SourceLine;
345 SmallVector<Columns, 200> ByteToColumn;
346 SmallVector<Bytes, 200> ColumnToByte;
353 std::string &SourceLine, std::string &CaretLine,
354 std::string &FixItInsertionLine, Columns NonGutterColumns,
355 const SourceColumnMap &Map,
357 Columns CaretColumns = CaretLine.size();
358 Columns FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);
360 std::max({Map.columns().V, CaretColumns.V, FixItColumns.V});
362 if (MaxColumns <= NonGutterColumns)
366 assert(llvm::none_of(CaretLine, [](
char c) {
return c <
' ' ||
'~' <
c; }));
370 Columns CaretStart = 0, CaretEnd = CaretLine.size();
371 while (CaretStart != CaretEnd &&
isWhitespace(CaretLine[CaretStart.V]))
372 CaretStart = CaretStart.next();
374 while (CaretEnd != CaretStart &&
isWhitespace(CaretLine[CaretEnd.V]))
375 CaretEnd = CaretEnd.prev();
382 if (!FixItInsertionLine.empty()) {
386 Bytes FixItStart = 0;
387 Bytes FixItEnd = Bytes(FixItInsertionLine.size());
388 while (FixItStart != FixItEnd &&
390 FixItStart = FixItStart.next();
392 while (FixItEnd != FixItStart &&
394 FixItEnd = FixItEnd.prev();
396 Columns FixItStartCol = Columns(FixItStart.V);
397 Columns FixItEndCol = Columns(llvm::sys::locale::columnWidth(
398 FixItInsertionLine.substr(0, FixItEnd.V)));
400 CaretStart = std::min(FixItStartCol.V, CaretStart.V);
401 CaretEnd = std::max(FixItEndCol.V, CaretEnd.V);
407 while (CaretEnd < Map.columns() && !Map.columnToByte(CaretEnd).isValid())
408 CaretEnd = CaretEnd.next();
411 (CaretStart > Map.columns() || Map.columnToByte(CaretStart).isValid()) &&
412 "CaretStart must not point to a column in the middle of a source"
414 assert((CaretEnd > Map.columns() || Map.columnToByte(CaretEnd).isValid()) &&
415 "CaretEnd must not point to a column in the middle of a source line"
423 Bytes SourceStart = Map.columnToByte(std::min(CaretStart.V, Map.columns().V));
424 Bytes SourceEnd = Map.columnToByte(std::min(CaretEnd.V, Map.columns().V));
426 Columns CaretColumnsOutsideSource =
427 CaretEnd - CaretStart -
428 (Map.byteToColumn(SourceEnd) - Map.byteToColumn(SourceStart));
430 constexpr StringRef FrontEllipse =
" ...";
431 constexpr StringRef FrontSpace =
" ";
432 constexpr StringRef BackEllipse =
"...";
433 Columns EllipsesColumns = Columns(FrontEllipse.size() + BackEllipse.size());
435 Columns TargetColumns = NonGutterColumns;
438 if (TargetColumns > EllipsesColumns + CaretColumnsOutsideSource)
439 TargetColumns -= EllipsesColumns + CaretColumnsOutsideSource;
441 while (SourceStart > 0 || SourceEnd < SourceLine.size()) {
442 bool ExpandedRegion =
false;
444 if (SourceStart > 0) {
445 Bytes NewStart = Map.startOfPreviousColumn(SourceStart);
450 while (NewStart > 0 &&
isWhitespace(SourceLine[NewStart.V]))
451 NewStart = Map.startOfPreviousColumn(NewStart);
454 while (NewStart > 0) {
455 Bytes Prev = Map.startOfPreviousColumn(NewStart);
461 assert(Map.byteToColumn(NewStart).isValid());
463 Map.byteToColumn(SourceEnd) - Map.byteToColumn(NewStart);
464 if (NewColumns <= TargetColumns) {
465 SourceStart = NewStart;
466 ExpandedRegion =
true;
470 if (SourceEnd < SourceLine.size()) {
471 Bytes NewEnd = Map.startOfNextColumn(SourceEnd);
476 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
477 NewEnd = Map.startOfNextColumn(NewEnd);
480 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
481 NewEnd = Map.startOfNextColumn(NewEnd);
483 assert(Map.byteToColumn(NewEnd).isValid());
485 Map.byteToColumn(NewEnd) - Map.byteToColumn(SourceStart);
486 if (NewColumns <= TargetColumns) {
488 ExpandedRegion =
true;
496 CaretStart = Map.byteToColumn(SourceStart);
497 CaretEnd = Map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
501 assert(CaretStart.isValid() && CaretEnd.isValid() && SourceStart.isValid() &&
502 SourceEnd.isValid());
503 assert(SourceStart <= SourceEnd);
504 assert(CaretStart <= CaretEnd);
506 Columns BackColumnsRemoved =
507 Map.byteToColumn(Bytes{
static_cast<int>(SourceLine.size())}) -
508 Map.byteToColumn(SourceEnd);
509 Columns FrontColumnsRemoved = CaretStart;
510 Columns ColumnsKept = CaretEnd - CaretStart;
513 assert(FrontColumnsRemoved + ColumnsKept + BackColumnsRemoved >
525 FrontColumnsRemoved > FrontEllipse.size()
526 ? (Map.columnToByte(FrontColumnsRemoved) - Bytes(FrontEllipse.size()))
529 CaretEnd < Map.columns() ? Map.columnToByte(CaretEnd.V) : CaretEnd.V;
532 if (R.Start >=
static_cast<unsigned>(CodeEnd.V) ||
533 R.End <
static_cast<unsigned>(BytesRemoved.V)) {
534 R.Start = R.End = std::numeric_limits<int>::max();
539 R.Start -= BytesRemoved.V;
540 R.End -= BytesRemoved.V;
543 if (R.Start <
static_cast<unsigned>(CodeEnd.V) &&
544 R.End >
static_cast<unsigned>(CodeEnd.V))
545 R.End = CodeEnd.V + 1;
550 if (BackColumnsRemoved > Columns(BackEllipse.size()))
551 SourceLine.replace(SourceEnd.V, std::string::npos, BackEllipse);
554 if (FrontColumnsRemoved + ColumnsKept <= NonGutterColumns)
558 if (FrontColumnsRemoved > Columns(FrontEllipse.size())) {
559 SourceLine.replace(0, SourceStart.V, FrontEllipse);
560 CaretLine.replace(0, CaretStart.V, FrontSpace);
561 if (!FixItInsertionLine.empty())
562 FixItInsertionLine.replace(0, CaretStart.V, FrontSpace);
586 case '\'':
return '\'';
587 case '`':
return '\'';
588 case '"':
return '"';
589 case '(':
return ')';
590 case '[':
return ']';
591 case '{':
return '}';
604 unsigned Length,
unsigned Column,
606 assert(Start < Str.size() &&
"Invalid start position!");
607 unsigned End = Start + 1;
610 if (End == Str.size())
626 PunctuationEndStack.push_back(EndPunct);
627 while (End < Length && !PunctuationEndStack.empty()) {
628 if (Str[End] == PunctuationEndStack.back())
629 PunctuationEndStack.pop_back();
631 PunctuationEndStack.push_back(SubEndPunct);
640 unsigned PunctWordLength = End - Start;
642 Column + PunctWordLength <= Columns ||
645 PunctWordLength < Columns/3)
669 unsigned Column,
bool Bold) {
670 const unsigned Length = std::min(Str.find(
'\n'), Str.size());
671 bool TextNormal =
true;
673 bool Wrapped =
false;
674 for (
unsigned WordStart = 0, WordEnd; WordStart < Length;
675 WordStart = WordEnd) {
678 if (WordStart == Length)
685 unsigned WordLength = WordEnd - WordStart;
686 if (
Column + WordLength < Columns) {
711 assert(TextNormal &&
"Text highlighted at end of diagnostic message.");
727 uint64_t StartOfLocationInfo = OS.getColumn();
740 Message, OS.getColumn() - StartOfLocationInfo,
755 llvm_unreachable(
"Invalid diagnostic type");
776 llvm_unreachable(
"Invalid diagnostic type");
792 unsigned CurrentColumn,
793 unsigned Columns,
bool ShowColors) {
795 if (ShowColors && !IsSupplemental) {
807 assert(
Normal &&
"Formatting should have returned to normal");
815void TextDiagnostic::emitFilename(StringRef Filename,
const SourceManager &
SM) {
820 auto File =
SM.getFileManager().getOptionalFileRef(Filename);
837 TmpFilename =
File->getName();
838 llvm::sys::fs::make_absolute(TmpFilename);
839 llvm::sys::path::native(TmpFilename);
840 llvm::sys::path::remove_dots(TmpFilename,
true);
841 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
843 Filename =
SM.getFileManager().getCanonicalName(*
File);
864 emitFilename(FE->getName(), Loc.
getManager());
870 unsigned LineNo = PLoc.
getLine();
895 if (
LangOpts.MSCompatibilityVersion &&
910 if (
LangOpts.MSCompatibilityVersion &&
917 if (
DiagOpts.ShowSourceRanges && !Ranges.empty()) {
919 bool PrintedRange =
false;
922 for (
const auto &R : Ranges) {
933 if (
SM.getFileID(B) != CaretFileID ||
SM.getFileID(E) != CaretFileID)
938 unsigned TokSize = 0;
944 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
958 OS <<
"In file included from ";
960 OS <<
':' << PLoc.
getLine() <<
":\n";
962 OS <<
"In included file:\n";
966 StringRef ModuleName) {
968 OS <<
"In module '" << ModuleName <<
"' imported from "
971 OS <<
"In module '" << ModuleName <<
"':\n";
976 StringRef ModuleName) {
978 OS <<
"While building module '" << ModuleName <<
"' imported from "
981 OS <<
"While building module '" << ModuleName <<
"':\n";
985static std::optional<std::pair<unsigned, unsigned>>
993 if (
SM.getFileID(Begin) != FID ||
SM.getFileID(End) != FID)
996 return std::make_pair(
SM.getExpansionLineNumber(Begin),
997 SM.getExpansionLineNumber(End));
1002static std::pair<unsigned, unsigned>
1004 unsigned MaxRange) {
1006 unsigned Slack = MaxRange - (A.second - A.first + 1);
1011 unsigned Min = std::min(A.first, B.first);
1012 unsigned Max = std::max(A.second, B.second);
1013 if (
Max -
Min + 1 <= MaxRange)
1018 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
1019 (B.second < A.second && A.second - B.second + 1 > MaxRange))
1028 A.second = std::min(A.second + (Slack + 1) / 2,
Max);
1029 Slack = MaxRange - (A.second - A.first + 1);
1030 A.first = std::max(
Min + Slack, A.first) - Slack;
1031 A.second = std::min(A.first + MaxRange - 1,
Max);
1043 std::string &CaretLine) {
1046 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] ==
' ' ||
1047 Map.getSourceLine()[StartByte.V] ==
'\t'))
1048 StartByte = Map.startOfNextColumn(StartByte);
1051 Bytes EndByte = std::min(R.
EndByte.V, Map.bytes().V);
1052 while (EndByte.V != 0 && (Map.getSourceLine()[EndByte.V - 1] ==
' ' ||
1053 Map.getSourceLine()[EndByte.V - 1] ==
'\t'))
1054 EndByte = Map.startOfPreviousColumn(EndByte);
1059 if (StartByte > EndByte)
1062 assert(StartByte <= EndByte &&
"Invalid range!");
1064 Columns StartCol = Map.byteToContainingColumn(StartByte);
1065 Columns EndCol = Map.byteToContainingColumn(EndByte);
1067 if (CaretLine.size() <
static_cast<size_t>(EndCol.V))
1068 CaretLine.resize(EndCol.V,
' ');
1070 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V,
'~');
1074 const SourceColumnMap &map,
1078 std::string FixItInsertionLine;
1079 if (Hints.empty() || !DiagOpts.ShowFixits)
1080 return FixItInsertionLine;
1081 Columns PrevHintEndCol = 0;
1083 for (
const auto &H : Hints) {
1084 if (H.CodeToInsert.empty())
1090 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin());
1091 if (FID == HintLocInfo.first &&
1092 LineNo ==
SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1093 StringRef(H.CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1099 Bytes HintByteOffset =
1100 Bytes(
SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second))
1104 assert(HintByteOffset < map.bytes().next());
1105 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1114 if (HintCol < PrevHintEndCol)
1115 HintCol = PrevHintEndCol + 1;
1119 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1120 (HintCol - PrevHintEndCol) +
1121 Columns(H.CodeToInsert.size());
1122 if (NewFixItLineSize > FixItInsertionLine.size())
1123 FixItInsertionLine.resize(NewFixItLineSize.V,
' ');
1125 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1126 FixItInsertionLine.end() - H.CodeToInsert.size());
1128 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1132 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1134 return FixItInsertionLine;
1138 unsigned L = 1u, M = 10u;
1139 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1153 const std::pair<unsigned, unsigned> &Lines,
FileID FID,
1163 unsigned StartLineNo =
SM.getExpansionLineNumber(Begin);
1164 if (StartLineNo > Lines.second ||
SM.getFileID(Begin) != FID)
1167 unsigned EndLineNo =
SM.getExpansionLineNumber(End);
1168 if (EndLineNo < Lines.first ||
SM.getFileID(End) != FID)
1171 Bytes StartByte =
SM.getExpansionColumnNumber(Begin);
1172 Bytes EndByte =
SM.getExpansionColumnNumber(End);
1173 assert(StartByte.V != 0 &&
"StartByte must be valid, 0 is invalid");
1174 assert(EndByte.V != 0 &&
"EndByte must be valid, 0 is invalid");
1175 if (R.isTokenRange())
1179 if (StartLineNo == EndLineNo) {
1180 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1185 LineRanges.push_back(
1186 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1189 for (
unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1190 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1193 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1206static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1211 assert(StartLineNumber <= EndLineNumber);
1212 auto SnippetRanges =
1213 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1214 EndLineNumber - StartLineNumber + 1);
1216 if (!PP || !ShowColors)
1217 return SnippetRanges;
1221 return SnippetRanges;
1223 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1224 Lexer L{FID, *Buff,
SM, LangOpts};
1227 const char *FirstLineStart =
1229 SM.getDecomposedLoc(
SM.translateLineCol(FID, StartLineNumber, 1)).second;
1230 if (
const char *CheckPoint = PP->
getCheckPoint(FID, FirstLineStart)) {
1231 assert(CheckPoint >= Buff->getBufferStart() &&
1232 CheckPoint <= Buff->getBufferEnd());
1233 assert(CheckPoint <= FirstLineStart);
1234 size_t Offset = CheckPoint - Buff->getBufferStart();
1235 L.
seek(Offset,
false);
1241 const Token &
T,
unsigned Start,
unsigned Length) ->
void {
1242 if (
T.is(tok::raw_identifier)) {
1243 StringRef RawIdent =
T.getRawIdentifier();
1248 if (llvm::StringSwitch<bool>(RawIdent)
1250 .Case(
"false",
true)
1251 .Case(
"nullptr",
true)
1252 .Case(
"__func__",
true)
1253 .Case(
"__objc_yes__",
true)
1254 .Case(
"__objc_no__",
true)
1255 .Case(
"__null",
true)
1256 .Case(
"__FUNCDNAME__",
true)
1257 .Case(
"__FUNCSIG__",
true)
1258 .Case(
"__FUNCTION__",
true)
1259 .Case(
"__FUNCSIG__",
true)
1271 assert(
T.is(tok::comment));
1280 if (
T.is(tok::unknown))
1284 if (!
T.is(tok::raw_identifier) && !
T.is(tok::comment) &&
1289 unsigned TokenEndLine =
SM.getSpellingLineNumber(
T.getEndLoc(), &
Invalid);
1290 if (
Invalid || TokenEndLine < StartLineNumber)
1293 assert(TokenEndLine >= StartLineNumber);
1295 unsigned TokenStartLine =
1296 SM.getSpellingLineNumber(
T.getLocation(), &
Invalid);
1300 if (TokenStartLine > EndLineNumber)
1303 Bytes StartCol =
SM.getSpellingColumnNumber(
T.getLocation(), &
Invalid) - 1;
1308 if (TokenStartLine == TokenEndLine) {
1310 SnippetRanges[TokenStartLine - StartLineNumber];
1311 appendStyle(LineRanges,
T, StartCol.V,
T.getLength());
1314 assert((TokenEndLine - TokenStartLine) >= 1);
1318 Bytes EndCol =
SM.getSpellingColumnNumber(
T.getEndLoc(), &
Invalid) - 1;
1324 unsigned L = TokenStartLine;
1325 unsigned LineLength = 0;
1326 for (
unsigned I = 0; I <= Spelling.size(); ++I) {
1329 if (L >= StartLineNumber) {
1331 SnippetRanges[L - StartLineNumber];
1333 if (L == TokenStartLine)
1334 appendStyle(LineRanges,
T, StartCol.V, LineLength);
1335 else if (L == TokenEndLine)
1336 appendStyle(LineRanges,
T, 0, EndCol.V);
1338 appendStyle(LineRanges,
T, 0, LineLength);
1342 if (L > EndLineNumber)
1351 return SnippetRanges;
1361void TextDiagnostic::emitSnippetAndCaret(
1364 assert(Loc.
isValid() &&
"must have a valid source location here");
1365 assert(Loc.
isFileID() &&
"must have a file location here");
1375 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1387 const char *BufStart = BufData.data();
1388 const char *BufEnd = BufStart + BufData.size();
1394 static const size_t MaxLineLengthToPrint = 4096;
1395 if (CaretByte > MaxLineLengthToPrint)
1399 const unsigned MaxLines =
DiagOpts.SnippetLineLimit;
1400 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1402 for (
const auto &I : Ranges) {
1407 std::min(DisplayLineNo,
SM.getPresumedLineNumber(I.getBegin()));
1414 unsigned MaxLineNoDisplayWidth =
1418 auto indentForLineNumbers = [&] {
1419 if (MaxLineNoDisplayWidth > 0)
1420 OS.indent(MaxLineNoDisplayWidth + 2) <<
"| ";
1423 Columns MessageLength =
DiagOpts.MessageLength;
1425 if (MessageLength != 0 && MessageLength <= Columns(MaxLineNoDisplayWidth + 4))
1430 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1434 SmallVector<LineRange> LineRanges =
1437 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1438 ++LineNo, ++DisplayLineNo) {
1440 const char *LineStart =
1442 SM.getDecomposedLoc(
SM.translateLineCol(FID, LineNo, 1)).second;
1443 if (LineStart == BufEnd)
1447 const char *LineEnd = LineStart;
1448 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1453 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1457 std::string SourceLine(LineStart, LineEnd);
1459 while (!SourceLine.empty() && SourceLine.back() ==
'\0' &&
1460 (LineNo != CaretLineNo ||
1461 SourceLine.size() >
static_cast<size_t>(CaretByte.V)))
1462 SourceLine.pop_back();
1465 const SourceColumnMap SourceColMap(SourceLine,
DiagOpts.TabStop);
1467 std::string CaretLine;
1469 for (
const auto &LR : LineRanges) {
1470 if (LR.LineNo == LineNo)
1475 if (CaretLineNo == LineNo) {
1476 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1478 std::max(
static_cast<size_t>(Col.V) + 1, CaretLine.size()),
' ');
1479 CaretLine[Col.V] =
'^';
1482 std::string FixItInsertionLine =
1487 if (MessageLength != 0) {
1488 Columns NonGutterColumns = MessageLength;
1489 if (MaxLineNoDisplayWidth != 0)
1490 NonGutterColumns -= Columns(MaxLineNoDisplayWidth + 4);
1492 NonGutterColumns, SourceColMap,
1493 SourceStyles[LineNo - Lines.first]);
1500 if (
DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1501 SourceLine =
' ' + SourceLine;
1502 CaretLine =
' ' + CaretLine;
1506 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1507 SourceStyles[LineNo - Lines.first]);
1509 if (!CaretLine.empty()) {
1510 indentForLineNumbers();
1513 OS << CaretLine <<
'\n';
1518 if (!FixItInsertionLine.empty()) {
1519 indentForLineNumbers();
1525 OS << FixItInsertionLine <<
'\n';
1532 emitParseableFixits(Hints,
SM);
1535void TextDiagnostic::emitSnippet(StringRef SourceLine,
1536 unsigned MaxLineNoDisplayWidth,
1537 unsigned LineNo,
unsigned DisplayLineNo,
1540 if (MaxLineNoDisplayWidth > 0) {
1542 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1543 << DisplayLineNo <<
" | ";
1547 bool PrintReversed =
false;
1548 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1550 while (I < SourceLine.size()) {
1551 auto [Str, WasPrintable] =
1556 if (WasPrintable == PrintReversed) {
1557 PrintReversed = !PrintReversed;
1562 CurrentColor = std::nullopt;
1567 const auto *CharStyle = llvm::find_if(Styles, [I](
const StyleRange &R) {
1568 return (R.Start < I && R.End >= I);
1571 if (CharStyle != Styles.end()) {
1572 if (!CurrentColor ||
1573 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1574 OS.changeColor(CharStyle->Color);
1575 CurrentColor = CharStyle->Color;
1577 }
else if (CurrentColor) {
1579 CurrentColor = std::nullopt;
1599 for (
const auto &H : Hints) {
1600 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1601 H.RemoveRange.getEnd().isMacroID())
1605 for (
const auto &H : Hints) {
1606 SourceLocation BLoc = H.RemoveRange.getBegin();
1607 SourceLocation ELoc = H.RemoveRange.getEnd();
1613 if (H.RemoveRange.isTokenRange())
1618 PresumedLoc PLoc =
SM.getPresumedLoc(BLoc);
1624 OS <<
"\":{" <<
SM.getLineNumber(BInfo.first, BInfo.second)
1625 <<
':' <<
SM.getColumnNumber(BInfo.first, BInfo.second)
1626 <<
'-' <<
SM.getLineNumber(EInfo.first, EInfo.second)
1627 <<
':' <<
SM.getColumnNumber(EInfo.first, EInfo.second)
1629 OS.write_escaped(H.CodeToInsert);
static StringRef bytes(const std::vector< T, Allocator > &v)
static size_t getNumDisplayWidth(size_t N)
Defines the clang::FileManager interface and associated types.
static SmallVectorImpl< char > & operator+=(SmallVectorImpl< char > &Includes, StringRef RHS)
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i)
static constexpr raw_ostream::Colors SavedColor
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 void genColumnByteMapping(StringRef SourceLine, unsigned TabStop, SmallVectorImpl< Bytes > &BytesOut, SmallVectorImpl< Columns > &ColumnsOut)
BytesOut: A mapping from columns to the byte of the source line that produced the character displayin...
static void selectInterestingSourceRegion(std::string &SourceLine, std::string &CaretLine, std::string &FixItInsertionLine, Columns NonGutterColumns, const SourceColumnMap &Map, SmallVectorImpl< clang::TextDiagnostic::StyleRange > &Styles)
When the source code line we want to print is too long for the terminal, select the "interesting" reg...
static constexpr raw_ostream::Colors LiteralColor
static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, bool &Normal, bool Bold)
Add highlights to differences in template strings.
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 constexpr raw_ostream::Colors TemplateColor
static constexpr raw_ostream::Colors ErrorColor
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 FatalColor
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 char findMatchingPunctuation(char c)
If the given character is the start of some kind of balanced punctuation (e.g., quotes or parentheses...
static constexpr raw_ostream::Colors CaretColor
static constexpr raw_ostream::Colors FixitColor
static void expandTabs(std::string &SourceLine, unsigned TabStop)
static constexpr raw_ostream::Colors WarningColor
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 std::string buildFixItInsertionLine(FileID FID, unsigned LineNo, const SourceColumnMap &map, ArrayRef< FixItHint > Hints, const SourceManager &SM, const DiagnosticOptions &DiagOpts)
static constexpr raw_ostream::Colors RemarkColor
static constexpr raw_ostream::Colors NoteColor
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.
const LangOptions & LangOpts
SourceLocation LastLoc
The location of the previous diagnostic if known.
DiagnosticOptions & DiagOpts
DiagnosticsEngine::Level LastLevel
The level of the last diagnostic emitted.
DiagnosticRenderer(const LangOptions &LangOpts, 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
FullSourceLoc getExpansionLoc() const
unsigned getLineNumber(bool *Invalid=nullptr) const
OptionalFileEntryRef getFileEntryRef() const
StringRef getBufferData(bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
PresumedLoc getPresumedLoc(bool UseLineDirectives=true) const
const SourceManager & getManager() 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...
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
void SetKeepWhitespaceMode(bool Val)
SetKeepWhitespaceMode - This method lets clients enable or disable whitespace retention mode.
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
void seek(unsigned Offset, bool IsAtStartOfLine)
Set the lexer's buffer pointer to Offset.
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
TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, DiagnosticOptions &DiagOpts, const Preprocessor *PP=nullptr)
static void printDiagnosticLevel(raw_ostream &OS, DiagnosticsEngine::Level Level, bool ShowColors)
Print the diagonstic level to a raw_ostream.
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'.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag
std::pair< FileID, unsigned > FileIDAndOffset
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
const FunctionProtoType * T
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.
bool operator!=(CanQual< T > x, CanQual< U > y)
bool operator<=(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
bool operator>(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.