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) {
838 TmpFilename =
File->getName();
840 llvm::sys::path::native(TmpFilename);
841 llvm::sys::path::remove_dots(TmpFilename,
true);
842 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
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) {
924 std::optional<CharSourceRange> FileRange =
934 unsigned TokSize = 0;
935 if (FileRange->isTokenRange())
940 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
954 OS <<
"In file included from ";
956 OS <<
':' << PLoc.
getLine() <<
":\n";
958 OS <<
"In included file:\n";
962 StringRef ModuleName) {
964 OS <<
"In module '" << ModuleName <<
"' imported from "
967 OS <<
"In module '" << ModuleName <<
"':\n";
972 StringRef ModuleName) {
974 OS <<
"While building module '" << ModuleName <<
"' imported from "
977 OS <<
"While building module '" << ModuleName <<
"':\n";
981static std::optional<std::pair<unsigned, unsigned>>
998static std::pair<unsigned, unsigned>
999maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
1000 unsigned MaxRange) {
1002 unsigned Slack = MaxRange - (A.second - A.first + 1);
1007 unsigned Min = std::min(A.first, B.first);
1008 unsigned Max = std::max(A.second, B.second);
1009 if (
Max -
Min + 1 <= MaxRange)
1014 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
1015 (B.second < A.second && A.second - B.second + 1 > MaxRange))
1024 A.second = std::min(A.second + (Slack + 1) / 2,
Max);
1025 Slack = MaxRange - (A.second - A.first + 1);
1026 A.first = std::max(
Min + Slack, A.first) - Slack;
1027 A.second = std::min(A.first + MaxRange - 1,
Max);
1039 std::string &CaretLine) {
1041 Bytes StartByte = R.StartByte;
1042 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] ==
' ' ||
1043 Map.getSourceLine()[StartByte.V] ==
'\t'))
1044 StartByte = Map.startOfNextColumn(StartByte);
1047 Bytes EndByte = std::min(R.EndByte.V, Map.bytes().V);
1048 while (EndByte.V != 0 && (Map.getSourceLine()[EndByte.V - 1] ==
' ' ||
1049 Map.getSourceLine()[EndByte.V - 1] ==
'\t'))
1050 EndByte = Map.startOfPreviousColumn(EndByte);
1055 if (StartByte > EndByte)
1058 assert(StartByte <= EndByte &&
"Invalid range!");
1060 Columns StartCol = Map.byteToContainingColumn(StartByte);
1061 Columns EndCol = Map.byteToContainingColumn(EndByte);
1063 if (CaretLine.size() <
static_cast<size_t>(EndCol.V))
1064 CaretLine.resize(EndCol.V,
' ');
1066 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V,
'~');
1070 const SourceColumnMap &map,
1074 std::string FixItInsertionLine;
1075 if (Hints.empty() || !DiagOpts.ShowFixits)
1076 return FixItInsertionLine;
1077 Columns PrevHintEndCol = 0;
1079 for (
const auto &H : Hints) {
1080 if (H.CodeToInsert.empty())
1087 if (FID == HintLocInfo.first &&
1088 LineNo == SM.
getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1089 StringRef(H.CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1095 Bytes HintByteOffset =
1100 assert(HintByteOffset < map.bytes().next());
1101 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1110 if (HintCol < PrevHintEndCol)
1111 HintCol = PrevHintEndCol + 1;
1115 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1116 (HintCol - PrevHintEndCol) +
1117 Columns(H.CodeToInsert.size());
1118 if (NewFixItLineSize > FixItInsertionLine.size())
1119 FixItInsertionLine.resize(NewFixItLineSize.V,
' ');
1121 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1122 FixItInsertionLine.end() - H.CodeToInsert.size());
1124 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1128 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1130 return FixItInsertionLine;
1134 unsigned L = 1u, M = 10u;
1135 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1149 const std::pair<unsigned, unsigned> &Lines,
FileID FID,
1160 if (StartLineNo > Lines.second || SM.
getFileID(Begin) != FID)
1164 if (EndLineNo < Lines.first || SM.
getFileID(End) != FID)
1169 assert(StartByte.V != 0 &&
"StartByte must be valid, 0 is invalid");
1170 assert(EndByte.V != 0 &&
"EndByte must be valid, 0 is invalid");
1171 if (R.isTokenRange())
1175 if (StartLineNo == EndLineNo) {
1176 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1181 LineRanges.push_back(
1182 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1185 for (
unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1186 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1189 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1202static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1207 assert(StartLineNumber <= EndLineNumber);
1208 auto SnippetRanges =
1209 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1210 EndLineNumber - StartLineNumber + 1);
1212 if (!PP || !ShowColors)
1213 return SnippetRanges;
1217 return SnippetRanges;
1219 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1220 Lexer L{FID, *Buff, SM, LangOpts};
1223 const char *FirstLineStart =
1226 if (
const char *CheckPoint = PP->
getCheckPoint(FID, FirstLineStart)) {
1227 assert(CheckPoint >= Buff->getBufferStart() &&
1228 CheckPoint <= Buff->getBufferEnd());
1229 assert(CheckPoint <= FirstLineStart);
1230 size_t Offset = CheckPoint - Buff->getBufferStart();
1231 L.
seek(Offset,
false);
1237 const Token &
T,
unsigned Start,
unsigned Length) ->
void {
1238 if (
T.is(tok::raw_identifier)) {
1239 StringRef RawIdent =
T.getRawIdentifier();
1244 if (llvm::StringSwitch<bool>(RawIdent)
1246 .Case(
"false",
true)
1247 .Case(
"nullptr",
true)
1248 .Case(
"__func__",
true)
1249 .Case(
"__objc_yes__",
true)
1250 .Case(
"__objc_no__",
true)
1251 .Case(
"__null",
true)
1252 .Case(
"__FUNCDNAME__",
true)
1253 .Case(
"__FUNCSIG__",
true)
1254 .Case(
"__FUNCTION__",
true)
1255 .Case(
"__FUNCSIG__",
true)
1267 assert(
T.is(tok::comment));
1276 if (
T.is(tok::unknown))
1280 if (!
T.is(tok::raw_identifier) && !
T.is(tok::comment) &&
1286 if (
Invalid || TokenEndLine < StartLineNumber)
1289 assert(TokenEndLine >= StartLineNumber);
1291 unsigned TokenStartLine =
1296 if (TokenStartLine > EndLineNumber)
1304 if (TokenStartLine == TokenEndLine) {
1306 SnippetRanges[TokenStartLine - StartLineNumber];
1307 appendStyle(LineRanges,
T, StartCol.V,
T.getLength());
1310 assert((TokenEndLine - TokenStartLine) >= 1);
1320 unsigned L = TokenStartLine;
1321 unsigned LineLength = 0;
1322 for (
unsigned I = 0; I <= Spelling.size(); ++I) {
1325 if (L >= StartLineNumber) {
1327 SnippetRanges[L - StartLineNumber];
1329 if (L == TokenStartLine)
1330 appendStyle(LineRanges,
T, StartCol.V, LineLength);
1331 else if (L == TokenEndLine)
1332 appendStyle(LineRanges,
T, 0, EndCol.V);
1334 appendStyle(LineRanges,
T, 0, LineLength);
1338 if (L > EndLineNumber)
1347 return SnippetRanges;
1357void TextDiagnostic::emitSnippetAndCaret(
1360 assert(Loc.
isValid() &&
"must have a valid source location here");
1361 assert(Loc.
isFileID() &&
"must have a file location here");
1371 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1383 const char *BufStart = BufData.data();
1384 const char *BufEnd = BufStart + BufData.size();
1390 static const size_t MaxLineLengthToPrint = 4096;
1391 if (CaretByte > MaxLineLengthToPrint)
1395 const unsigned MaxLines =
DiagOpts.SnippetLineLimit;
1396 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1398 for (
const auto &I : Ranges) {
1410 unsigned MaxLineNoDisplayWidth =
1414 auto indentForLineNumbers = [&] {
1415 if (MaxLineNoDisplayWidth > 0)
1416 OS.indent(MaxLineNoDisplayWidth + 2) <<
"| ";
1419 Columns MessageLength =
DiagOpts.MessageLength;
1421 if (MessageLength != 0 && MessageLength <= Columns(MaxLineNoDisplayWidth + 4))
1426 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1428 DiagOpts.showColors(OS.has_colors()), FID, SM);
1430 SmallVector<LineRange> LineRanges =
1433 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1434 ++LineNo, ++DisplayLineNo) {
1436 const char *LineStart =
1439 if (LineStart == BufEnd)
1443 const char *LineEnd = LineStart;
1444 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1449 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1453 std::string SourceLine(LineStart, LineEnd);
1455 while (!SourceLine.empty() && SourceLine.back() ==
'\0' &&
1456 (LineNo != CaretLineNo ||
1457 SourceLine.size() >
static_cast<size_t>(CaretByte.V)))
1458 SourceLine.pop_back();
1461 const SourceColumnMap SourceColMap(SourceLine,
DiagOpts.TabStop);
1463 std::string CaretLine;
1465 for (
const auto &LR : LineRanges) {
1466 if (LR.LineNo == LineNo)
1471 if (CaretLineNo == LineNo) {
1472 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1474 std::max(
static_cast<size_t>(Col.V) + 1, CaretLine.size()),
' ');
1475 CaretLine[Col.V] =
'^';
1478 std::string FixItInsertionLine =
1483 if (MessageLength != 0) {
1484 Columns NonGutterColumns = MessageLength;
1485 if (MaxLineNoDisplayWidth != 0)
1486 NonGutterColumns -= Columns(MaxLineNoDisplayWidth + 4);
1488 NonGutterColumns, SourceColMap,
1489 SourceStyles[LineNo - Lines.first]);
1496 if (
DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1497 SourceLine =
' ' + SourceLine;
1498 CaretLine =
' ' + CaretLine;
1502 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1503 SourceStyles[LineNo - Lines.first]);
1505 if (!CaretLine.empty()) {
1506 indentForLineNumbers();
1507 if (
DiagOpts.showColors(OS.has_colors()))
1509 OS << CaretLine <<
'\n';
1510 if (
DiagOpts.showColors(OS.has_colors()))
1514 if (!FixItInsertionLine.empty()) {
1515 indentForLineNumbers();
1516 if (
DiagOpts.showColors(OS.has_colors()))
1521 OS << FixItInsertionLine <<
'\n';
1522 if (
DiagOpts.showColors(OS.has_colors()))
1528 emitParseableFixits(Hints, SM);
1531void TextDiagnostic::emitSnippet(StringRef SourceLine,
1532 unsigned MaxLineNoDisplayWidth,
1533 unsigned LineNo,
unsigned DisplayLineNo,
1536 if (MaxLineNoDisplayWidth > 0) {
1538 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1539 << DisplayLineNo <<
" | ";
1543 bool PrintReversed =
false;
1544 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1546 while (I < SourceLine.size()) {
1547 auto [Str, WasPrintable] =
1551 if (
DiagOpts.showColors(OS.has_colors())) {
1552 if (WasPrintable == PrintReversed) {
1553 PrintReversed = !PrintReversed;
1558 CurrentColor = std::nullopt;
1563 const auto *CharStyle = llvm::find_if(Styles, [I](
const StyleRange &R) {
1564 return (
R.Start < I &&
R.End >= I);
1567 if (CharStyle != Styles.end()) {
1568 if (!CurrentColor ||
1569 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1570 OS.changeColor(CharStyle->Color);
1571 CurrentColor = CharStyle->Color;
1573 }
else if (CurrentColor) {
1575 CurrentColor = std::nullopt;
1582 if (
DiagOpts.showColors(OS.has_colors()))
1595 for (
const auto &H : Hints) {
1596 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1597 H.RemoveRange.getEnd().isMacroID())
1601 for (
const auto &H : Hints) {
1602 SourceLocation BLoc = H.RemoveRange.getBegin();
1603 SourceLocation ELoc = H.RemoveRange.getEnd();
1609 if (H.RemoveRange.isTokenRange())
1625 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.
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...
StringRef getCanonicalName(DirectoryEntryRef Dir)
Retrieve the canonical name for a given directory.
bool makeAbsolutePath(SmallVectorImpl< char > &Path, bool Canonicalize=false) const
Makes Path absolute taking into account FileSystemOptions and the working directory option,...
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true, bool IsText=true)
Get a FileEntryRef if it exists, without doing anything on error.
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.
FileIDAndOffset getDecomposedExpansionLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
FileIDAndOffset getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
unsigned getColumnNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Return the column # for the specified file position.
PresumedLoc getPresumedLoc(SourceLocation Loc, bool UseLineDirectives=true) const
Returns the "presumed" location of a SourceLocation specifies.
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
unsigned getExpansionColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
SourceLocation translateLineCol(FileID FID, unsigned Line, unsigned Col) const
Get the source location in FID for the given line:col.
unsigned getSpellingColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
FileManager & getFileManager() const
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Given a SourceLocation, return the spelling line number for the position indicated.
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
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.
Top level wrappers for InstallAPI frontend operations.
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.
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.
std::optional< CharSourceRange > getExpansionRangeInFile(CharSourceRange Range, FileID FID, const SourceManager &SM)
Maps both endpoints of Range to their macro expansion, so that the range can be shown to a user.