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();
733 if (
DiagOpts.showColors(OS.has_colors()))
740 Message, OS.getColumn() - StartOfLocationInfo,
742 DiagOpts.showColors(OS.has_colors()));
756 llvm_unreachable(
"Invalid diagnostic type");
777 llvm_unreachable(
"Invalid diagnostic type");
793 unsigned CurrentColumn,
794 unsigned Columns,
bool ShowColors) {
796 if (ShowColors && !IsSupplemental) {
808 assert(
Normal &&
"Formatting should have returned to normal");
816void TextDiagnostic::emitFilename(StringRef Filename,
const SourceManager &
SM) {
821 auto File =
SM.getFileManager().getOptionalFileRef(Filename);
838 TmpFilename =
File->getName();
839 SM.getFileManager().makeAbsolutePath(TmpFilename);
840 llvm::sys::path::native(TmpFilename);
841 llvm::sys::path::remove_dots(TmpFilename,
true);
842 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
844 Filename =
SM.getFileManager().getCanonicalName(*
File);
865 emitFilename(FE->getName(), Loc.
getManager());
871 unsigned LineNo = PLoc.
getLine();
876 if (
DiagOpts.showColors(OS.has_colors()))
896 if (
LangOpts.MSCompatibilityVersion &&
911 if (
LangOpts.MSCompatibilityVersion &&
918 if (
DiagOpts.ShowSourceRanges && !Ranges.empty()) {
920 bool PrintedRange =
false;
923 for (
const auto &R : Ranges) {
934 if (
SM.getFileID(B) != CaretFileID ||
SM.getFileID(E) != CaretFileID)
939 unsigned TokSize = 0;
945 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
959 OS <<
"In file included from ";
961 OS <<
':' << PLoc.
getLine() <<
":\n";
963 OS <<
"In included file:\n";
967 StringRef ModuleName) {
969 OS <<
"In module '" << ModuleName <<
"' imported from "
972 OS <<
"In module '" << ModuleName <<
"':\n";
977 StringRef ModuleName) {
979 OS <<
"While building module '" << ModuleName <<
"' imported from "
982 OS <<
"While building module '" << ModuleName <<
"':\n";
986static std::optional<std::pair<unsigned, unsigned>>
994 if (
SM.getFileID(Begin) != FID ||
SM.getFileID(End) != FID)
997 return std::make_pair(
SM.getExpansionLineNumber(Begin),
998 SM.getExpansionLineNumber(End));
1003static std::pair<unsigned, unsigned>
1005 unsigned MaxRange) {
1007 unsigned Slack = MaxRange - (A.second - A.first + 1);
1012 unsigned Min = std::min(A.first, B.first);
1013 unsigned Max = std::max(A.second, B.second);
1014 if (
Max -
Min + 1 <= MaxRange)
1019 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
1020 (B.second < A.second && A.second - B.second + 1 > MaxRange))
1029 A.second = std::min(A.second + (Slack + 1) / 2,
Max);
1030 Slack = MaxRange - (A.second - A.first + 1);
1031 A.first = std::max(
Min + Slack, A.first) - Slack;
1032 A.second = std::min(A.first + MaxRange - 1,
Max);
1044 std::string &CaretLine) {
1046 Bytes StartByte = R.StartByte;
1047 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] ==
' ' ||
1048 Map.getSourceLine()[StartByte.V] ==
'\t'))
1049 StartByte = Map.startOfNextColumn(StartByte);
1052 Bytes EndByte = std::min(R.EndByte.V, Map.bytes().V);
1053 while (EndByte.V != 0 && (Map.getSourceLine()[EndByte.V - 1] ==
' ' ||
1054 Map.getSourceLine()[EndByte.V - 1] ==
'\t'))
1055 EndByte = Map.startOfPreviousColumn(EndByte);
1060 if (StartByte > EndByte)
1063 assert(StartByte <= EndByte &&
"Invalid range!");
1065 Columns StartCol = Map.byteToContainingColumn(StartByte);
1066 Columns EndCol = Map.byteToContainingColumn(EndByte);
1068 if (CaretLine.size() <
static_cast<size_t>(EndCol.V))
1069 CaretLine.resize(EndCol.V,
' ');
1071 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V,
'~');
1075 const SourceColumnMap &map,
1079 std::string FixItInsertionLine;
1080 if (Hints.empty() || !DiagOpts.ShowFixits)
1081 return FixItInsertionLine;
1082 Columns PrevHintEndCol = 0;
1084 for (
const auto &H : Hints) {
1085 if (H.CodeToInsert.empty())
1091 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin());
1092 if (FID == HintLocInfo.first &&
1093 LineNo ==
SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1094 StringRef(H.CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1100 Bytes HintByteOffset =
1101 Bytes(
SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second))
1105 assert(HintByteOffset < map.bytes().next());
1106 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1115 if (HintCol < PrevHintEndCol)
1116 HintCol = PrevHintEndCol + 1;
1120 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1121 (HintCol - PrevHintEndCol) +
1122 Columns(H.CodeToInsert.size());
1123 if (NewFixItLineSize > FixItInsertionLine.size())
1124 FixItInsertionLine.resize(NewFixItLineSize.V,
' ');
1126 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1127 FixItInsertionLine.end() - H.CodeToInsert.size());
1129 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1133 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1135 return FixItInsertionLine;
1139 unsigned L = 1u, M = 10u;
1140 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1154 const std::pair<unsigned, unsigned> &Lines,
FileID FID,
1164 unsigned StartLineNo =
SM.getExpansionLineNumber(Begin);
1165 if (StartLineNo > Lines.second ||
SM.getFileID(Begin) != FID)
1168 unsigned EndLineNo =
SM.getExpansionLineNumber(End);
1169 if (EndLineNo < Lines.first ||
SM.getFileID(End) != FID)
1172 Bytes StartByte =
SM.getExpansionColumnNumber(Begin);
1173 Bytes EndByte =
SM.getExpansionColumnNumber(End);
1174 assert(StartByte.V != 0 &&
"StartByte must be valid, 0 is invalid");
1175 assert(EndByte.V != 0 &&
"EndByte must be valid, 0 is invalid");
1176 if (R.isTokenRange())
1180 if (StartLineNo == EndLineNo) {
1181 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1186 LineRanges.push_back(
1187 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1190 for (
unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1191 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1194 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1207static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1212 assert(StartLineNumber <= EndLineNumber);
1213 auto SnippetRanges =
1214 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1215 EndLineNumber - StartLineNumber + 1);
1217 if (!PP || !ShowColors)
1218 return SnippetRanges;
1222 return SnippetRanges;
1224 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1225 Lexer L{FID, *Buff,
SM, LangOpts};
1228 const char *FirstLineStart =
1230 SM.getDecomposedLoc(
SM.translateLineCol(FID, StartLineNumber, 1)).second;
1231 if (
const char *CheckPoint = PP->
getCheckPoint(FID, FirstLineStart)) {
1232 assert(CheckPoint >= Buff->getBufferStart() &&
1233 CheckPoint <= Buff->getBufferEnd());
1234 assert(CheckPoint <= FirstLineStart);
1235 size_t Offset = CheckPoint - Buff->getBufferStart();
1236 L.
seek(Offset,
false);
1242 const Token &T,
unsigned Start,
unsigned Length) ->
void {
1243 if (T.is(tok::raw_identifier)) {
1244 StringRef RawIdent = T.getRawIdentifier();
1249 if (llvm::StringSwitch<bool>(RawIdent)
1251 .Case(
"false",
true)
1252 .Case(
"nullptr",
true)
1253 .Case(
"__func__",
true)
1254 .Case(
"__objc_yes__",
true)
1255 .Case(
"__objc_no__",
true)
1256 .Case(
"__null",
true)
1257 .Case(
"__FUNCDNAME__",
true)
1258 .Case(
"__FUNCSIG__",
true)
1259 .Case(
"__FUNCTION__",
true)
1260 .Case(
"__FUNCSIG__",
true)
1272 assert(T.is(tok::comment));
1281 if (T.is(tok::unknown))
1285 if (!T.is(tok::raw_identifier) && !T.is(tok::comment) &&
1290 unsigned TokenEndLine =
SM.getSpellingLineNumber(T.getEndLoc(), &
Invalid);
1291 if (
Invalid || TokenEndLine < StartLineNumber)
1294 assert(TokenEndLine >= StartLineNumber);
1296 unsigned TokenStartLine =
1297 SM.getSpellingLineNumber(T.getLocation(), &
Invalid);
1301 if (TokenStartLine > EndLineNumber)
1304 Bytes StartCol =
SM.getSpellingColumnNumber(T.getLocation(), &
Invalid) - 1;
1309 if (TokenStartLine == TokenEndLine) {
1311 SnippetRanges[TokenStartLine - StartLineNumber];
1312 appendStyle(LineRanges, T, StartCol.V, T.getLength());
1315 assert((TokenEndLine - TokenStartLine) >= 1);
1319 Bytes EndCol =
SM.getSpellingColumnNumber(T.getEndLoc(), &
Invalid) - 1;
1325 unsigned L = TokenStartLine;
1326 unsigned LineLength = 0;
1327 for (
unsigned I = 0; I <= Spelling.size(); ++I) {
1330 if (L >= StartLineNumber) {
1332 SnippetRanges[L - StartLineNumber];
1334 if (L == TokenStartLine)
1335 appendStyle(LineRanges, T, StartCol.V, LineLength);
1336 else if (L == TokenEndLine)
1337 appendStyle(LineRanges, T, 0, EndCol.V);
1339 appendStyle(LineRanges, T, 0, LineLength);
1343 if (L > EndLineNumber)
1352 return SnippetRanges;
1362void TextDiagnostic::emitSnippetAndCaret(
1365 assert(Loc.
isValid() &&
"must have a valid source location here");
1366 assert(Loc.
isFileID() &&
"must have a file location here");
1376 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1388 const char *BufStart = BufData.data();
1389 const char *BufEnd = BufStart + BufData.size();
1395 static const size_t MaxLineLengthToPrint = 4096;
1396 if (CaretByte > MaxLineLengthToPrint)
1400 const unsigned MaxLines =
DiagOpts.SnippetLineLimit;
1401 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1403 for (
const auto &I : Ranges) {
1408 std::min(DisplayLineNo,
SM.getPresumedLineNumber(I.getBegin()));
1415 unsigned MaxLineNoDisplayWidth =
1419 auto indentForLineNumbers = [&] {
1420 if (MaxLineNoDisplayWidth > 0)
1421 OS.indent(MaxLineNoDisplayWidth + 2) <<
"| ";
1424 Columns MessageLength =
DiagOpts.MessageLength;
1426 if (MessageLength != 0 && MessageLength <= Columns(MaxLineNoDisplayWidth + 4))
1431 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1433 DiagOpts.showColors(OS.has_colors()), FID,
SM);
1435 SmallVector<LineRange> LineRanges =
1438 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1439 ++LineNo, ++DisplayLineNo) {
1441 const char *LineStart =
1443 SM.getDecomposedLoc(
SM.translateLineCol(FID, LineNo, 1)).second;
1444 if (LineStart == BufEnd)
1448 const char *LineEnd = LineStart;
1449 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1454 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1458 std::string SourceLine(LineStart, LineEnd);
1460 while (!SourceLine.empty() && SourceLine.back() ==
'\0' &&
1461 (LineNo != CaretLineNo ||
1462 SourceLine.size() >
static_cast<size_t>(CaretByte.V)))
1463 SourceLine.pop_back();
1466 const SourceColumnMap SourceColMap(SourceLine,
DiagOpts.TabStop);
1468 std::string CaretLine;
1470 for (
const auto &LR : LineRanges) {
1471 if (LR.LineNo == LineNo)
1476 if (CaretLineNo == LineNo) {
1477 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1479 std::max(
static_cast<size_t>(Col.V) + 1, CaretLine.size()),
' ');
1480 CaretLine[Col.V] =
'^';
1483 std::string FixItInsertionLine =
1488 if (MessageLength != 0) {
1489 Columns NonGutterColumns = MessageLength;
1490 if (MaxLineNoDisplayWidth != 0)
1491 NonGutterColumns -= Columns(MaxLineNoDisplayWidth + 4);
1493 NonGutterColumns, SourceColMap,
1494 SourceStyles[LineNo - Lines.first]);
1501 if (
DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1502 SourceLine =
' ' + SourceLine;
1503 CaretLine =
' ' + CaretLine;
1507 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1508 SourceStyles[LineNo - Lines.first]);
1510 if (!CaretLine.empty()) {
1511 indentForLineNumbers();
1512 if (
DiagOpts.showColors(OS.has_colors()))
1514 OS << CaretLine <<
'\n';
1515 if (
DiagOpts.showColors(OS.has_colors()))
1519 if (!FixItInsertionLine.empty()) {
1520 indentForLineNumbers();
1521 if (
DiagOpts.showColors(OS.has_colors()))
1526 OS << FixItInsertionLine <<
'\n';
1527 if (
DiagOpts.showColors(OS.has_colors()))
1533 emitParseableFixits(Hints,
SM);
1536void TextDiagnostic::emitSnippet(StringRef SourceLine,
1537 unsigned MaxLineNoDisplayWidth,
1538 unsigned LineNo,
unsigned DisplayLineNo,
1541 if (MaxLineNoDisplayWidth > 0) {
1543 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1544 << DisplayLineNo <<
" | ";
1548 bool PrintReversed =
false;
1549 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1551 while (I < SourceLine.size()) {
1552 auto [Str, WasPrintable] =
1556 if (
DiagOpts.showColors(OS.has_colors())) {
1557 if (WasPrintable == PrintReversed) {
1558 PrintReversed = !PrintReversed;
1563 CurrentColor = std::nullopt;
1568 const auto *CharStyle = llvm::find_if(Styles, [I](
const StyleRange &R) {
1569 return (
R.Start < I &&
R.End >= I);
1572 if (CharStyle != Styles.end()) {
1573 if (!CurrentColor ||
1574 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1575 OS.changeColor(CharStyle->Color);
1576 CurrentColor = CharStyle->Color;
1578 }
else if (CurrentColor) {
1580 CurrentColor = std::nullopt;
1587 if (
DiagOpts.showColors(OS.has_colors()))
1600 for (
const auto &H : Hints) {
1601 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1602 H.RemoveRange.getEnd().isMacroID())
1606 for (
const auto &H : Hints) {
1607 SourceLocation BLoc = H.RemoveRange.getBegin();
1608 SourceLocation ELoc = H.RemoveRange.getEnd();
1614 if (H.RemoveRange.isTokenRange())
1619 PresumedLoc PLoc =
SM.getPresumedLoc(BLoc);
1625 OS <<
"\":{" <<
SM.getLineNumber(BInfo.first, BInfo.second)
1626 <<
':' <<
SM.getColumnNumber(BInfo.first, BInfo.second)
1627 <<
'-' <<
SM.getLineNumber(EInfo.first, EInfo.second)
1628 <<
':' <<
SM.getColumnNumber(EInfo.first, EInfo.second)
1630 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 CommentColor
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,...
Represents a byte-granular source range.
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
SourceLocation getEnd() 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.
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.
@ Default
Set to the current date and time.
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.