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 &CaretLine,
354 std::string &FixItInsertionLine,
355 Columns NonGutterColumns,
356 const SourceColumnMap &Map) {
357 Columns CaretColumns = Columns(CaretLine.size());
358 Columns FixItColumns =
359 Columns(llvm::sys::locale::columnWidth(FixItInsertionLine));
361 std::max({Map.columns().V, CaretColumns.V, FixItColumns.V});
363 if (MaxColumns <= NonGutterColumns)
367 assert(llvm::none_of(CaretLine, [](
char c) {
return c <
' ' ||
'~' <
c; }));
371 Columns CaretStart = 0, CaretEnd = CaretLine.size();
372 for (; CaretStart != CaretEnd; CaretStart = CaretStart.next())
376 for (; CaretEnd != CaretStart; CaretEnd = CaretEnd.prev())
385 if (!FixItInsertionLine.empty()) {
389 Bytes FixItStart = 0;
390 Bytes FixItEnd = Bytes(FixItInsertionLine.size());
391 while (FixItStart != FixItEnd &&
393 FixItStart = FixItStart.next();
395 while (FixItEnd != FixItStart &&
397 FixItEnd = FixItEnd.prev();
399 Columns FixItStartCol = Columns(FixItStart.V);
400 Columns FixItEndCol = Columns(llvm::sys::locale::columnWidth(
401 FixItInsertionLine.substr(0, FixItEnd.V)));
403 CaretStart = std::min(FixItStartCol.V, CaretStart.V);
404 CaretEnd = std::max(FixItEndCol.V, CaretEnd.V);
410 while (CaretEnd < Map.columns() && !Map.columnToByte(CaretEnd).isValid())
411 CaretEnd = CaretEnd.next();
414 (CaretStart > Map.columns() || Map.columnToByte(CaretStart).isValid()) &&
415 "CaretStart must not point to a column in the middle of a source"
417 assert((CaretEnd > Map.columns() || Map.columnToByte(CaretEnd).isValid()) &&
418 "CaretEnd must not point to a column in the middle of a source line"
426 Bytes SourceStart = Map.columnToByte(std::min(CaretStart.V, Map.columns().V));
427 Bytes SourceEnd = Map.columnToByte(std::min(CaretEnd.V, Map.columns().V));
429 Columns CaretColumnsOutsideSource =
430 CaretEnd - CaretStart -
431 (Map.byteToColumn(SourceEnd) - Map.byteToColumn(SourceStart));
433 constexpr StringRef FrontEllipse =
" ...";
434 constexpr StringRef FrontSpace =
" ";
435 constexpr StringRef BackEllipse =
"...";
436 Columns EllipsesColumns = Columns(FrontEllipse.size() + BackEllipse.size());
438 Columns TargetColumns = NonGutterColumns;
441 if (TargetColumns > EllipsesColumns + CaretColumnsOutsideSource)
442 TargetColumns -= EllipsesColumns + CaretColumnsOutsideSource;
444 while (SourceStart > 0 || SourceEnd < SourceLine.size()) {
445 bool ExpandedRegion =
false;
447 if (SourceStart > 0) {
448 Bytes NewStart = Map.startOfPreviousColumn(SourceStart);
453 while (NewStart > 0 &&
isWhitespace(SourceLine[NewStart.V]))
454 NewStart = Map.startOfPreviousColumn(NewStart);
457 while (NewStart > 0) {
458 Bytes Prev = Map.startOfPreviousColumn(NewStart);
464 assert(Map.byteToColumn(NewStart).isValid());
466 Map.byteToColumn(SourceEnd) - Map.byteToColumn(NewStart);
467 if (NewColumns <= TargetColumns) {
468 SourceStart = NewStart;
469 ExpandedRegion =
true;
473 if (SourceEnd < SourceLine.size()) {
474 Bytes NewEnd = Map.startOfNextColumn(SourceEnd);
479 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
480 NewEnd = Map.startOfNextColumn(NewEnd);
483 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
484 NewEnd = Map.startOfNextColumn(NewEnd);
486 assert(Map.byteToColumn(NewEnd).isValid());
488 Map.byteToColumn(NewEnd) - Map.byteToColumn(SourceStart);
489 if (NewColumns <= TargetColumns) {
491 ExpandedRegion =
true;
499 CaretStart = Map.byteToColumn(SourceStart);
500 CaretEnd = Map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
504 assert(CaretStart.isValid() && CaretEnd.isValid() && SourceStart.isValid() &&
505 SourceEnd.isValid());
506 assert(SourceStart <= SourceEnd);
507 assert(CaretStart <= CaretEnd);
509 Columns BackColumnsRemoved =
510 Map.byteToColumn(Bytes{
static_cast<int>(SourceLine.size())}) -
511 Map.byteToColumn(SourceEnd);
512 Columns FrontColumnsRemoved = CaretStart;
513 Columns ColumnsKept = CaretEnd - CaretStart;
516 assert(FrontColumnsRemoved + ColumnsKept + BackColumnsRemoved >
521 if (BackColumnsRemoved > Columns(BackEllipse.size()))
522 SourceLine.replace(SourceEnd.V, std::string::npos, BackEllipse);
525 if (FrontColumnsRemoved + ColumnsKept <= Columns(NonGutterColumns))
529 if (FrontColumnsRemoved > Columns(FrontEllipse.size())) {
530 SourceLine.replace(0, SourceStart.V, FrontEllipse);
531 CaretLine.replace(0, CaretStart.V, FrontSpace);
532 if (!FixItInsertionLine.empty())
533 FixItInsertionLine.replace(0, CaretStart.V, FrontSpace);
557 case '\'':
return '\'';
558 case '`':
return '\'';
559 case '"':
return '"';
560 case '(':
return ')';
561 case '[':
return ']';
562 case '{':
return '}';
575 unsigned Length,
unsigned Column,
577 assert(Start < Str.size() &&
"Invalid start position!");
578 unsigned End = Start + 1;
581 if (End == Str.size())
597 PunctuationEndStack.push_back(EndPunct);
598 while (End < Length && !PunctuationEndStack.empty()) {
599 if (Str[End] == PunctuationEndStack.back())
600 PunctuationEndStack.pop_back();
602 PunctuationEndStack.push_back(SubEndPunct);
611 unsigned PunctWordLength = End - Start;
613 Column + PunctWordLength <= Columns ||
616 PunctWordLength < Columns/3)
640 unsigned Column,
bool Bold) {
641 const unsigned Length = std::min(Str.find(
'\n'), Str.size());
642 bool TextNormal =
true;
644 bool Wrapped =
false;
645 for (
unsigned WordStart = 0, WordEnd; WordStart < Length;
646 WordStart = WordEnd) {
649 if (WordStart == Length)
656 unsigned WordLength = WordEnd - WordStart;
657 if (
Column + WordLength < Columns) {
682 assert(TextNormal &&
"Text highlighted at end of diagnostic message.");
698 uint64_t StartOfLocationInfo = OS.getColumn();
711 Message, OS.getColumn() - StartOfLocationInfo,
726 llvm_unreachable(
"Invalid diagnostic type");
747 llvm_unreachable(
"Invalid diagnostic type");
763 unsigned CurrentColumn,
764 unsigned Columns,
bool ShowColors) {
766 if (ShowColors && !IsSupplemental) {
778 assert(
Normal &&
"Formatting should have returned to normal");
786void TextDiagnostic::emitFilename(StringRef Filename,
const SourceManager &
SM) {
791 auto File =
SM.getFileManager().getOptionalFileRef(Filename);
808 TmpFilename =
File->getName();
809 llvm::sys::fs::make_absolute(TmpFilename);
810 llvm::sys::path::native(TmpFilename);
811 llvm::sys::path::remove_dots(TmpFilename,
true);
812 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
814 Filename =
SM.getFileManager().getCanonicalName(*
File);
835 emitFilename(FE->getName(), Loc.
getManager());
841 unsigned LineNo = PLoc.
getLine();
866 if (
LangOpts.MSCompatibilityVersion &&
881 if (
LangOpts.MSCompatibilityVersion &&
888 if (
DiagOpts.ShowSourceRanges && !Ranges.empty()) {
890 bool PrintedRange =
false;
893 for (
const auto &R : Ranges) {
904 if (
SM.getFileID(B) != CaretFileID ||
SM.getFileID(E) != CaretFileID)
909 unsigned TokSize = 0;
915 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
929 OS <<
"In file included from ";
931 OS <<
':' << PLoc.
getLine() <<
":\n";
933 OS <<
"In included file:\n";
937 StringRef ModuleName) {
939 OS <<
"In module '" << ModuleName <<
"' imported from "
942 OS <<
"In module '" << ModuleName <<
"':\n";
947 StringRef ModuleName) {
949 OS <<
"While building module '" << ModuleName <<
"' imported from "
952 OS <<
"While building module '" << ModuleName <<
"':\n";
956static std::optional<std::pair<unsigned, unsigned>>
964 if (
SM.getFileID(Begin) != FID ||
SM.getFileID(End) != FID)
967 return std::make_pair(
SM.getExpansionLineNumber(Begin),
968 SM.getExpansionLineNumber(End));
973static std::pair<unsigned, unsigned>
974maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
977 unsigned Slack = MaxRange - (A.second - A.first + 1);
982 unsigned Min = std::min(A.first, B.first);
983 unsigned Max = std::max(A.second, B.second);
984 if (
Max -
Min + 1 <= MaxRange)
989 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
990 (B.second < A.second && A.second - B.second + 1 > MaxRange))
999 A.second = std::min(A.second + (Slack + 1) / 2,
Max);
1000 Slack = MaxRange - (A.second - A.first + 1);
1001 A.first = std::max(
Min + Slack, A.first) - Slack;
1002 A.second = std::min(A.first + MaxRange - 1,
Max);
1014 std::string &CaretLine) {
1017 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] ==
' ' ||
1018 Map.getSourceLine()[StartByte.V] ==
'\t'))
1019 StartByte = Map.startOfNextColumn(StartByte);
1022 Bytes EndByte = std::min(R.
EndByte.V, Map.bytes().V);
1023 while (EndByte.V != 0 && (Map.getSourceLine()[EndByte.V - 1] ==
' ' ||
1024 Map.getSourceLine()[EndByte.V - 1] ==
'\t'))
1025 EndByte = Map.startOfPreviousColumn(EndByte);
1030 if (StartByte > EndByte)
1033 assert(StartByte <= EndByte &&
"Invalid range!");
1035 Columns StartCol = Map.byteToContainingColumn(StartByte);
1036 Columns EndCol = Map.byteToContainingColumn(EndByte);
1038 if (CaretLine.size() <
static_cast<size_t>(EndCol.V))
1039 CaretLine.resize(EndCol.V,
' ');
1041 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V,
'~');
1045 const SourceColumnMap &map,
1049 std::string FixItInsertionLine;
1050 if (Hints.empty() || !DiagOpts.ShowFixits)
1051 return FixItInsertionLine;
1052 Columns PrevHintEndCol = 0;
1054 for (
const auto &H : Hints) {
1055 if (H.CodeToInsert.empty())
1061 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin());
1062 if (FID == HintLocInfo.first &&
1063 LineNo ==
SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1064 StringRef(H.CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1070 Bytes HintByteOffset =
1071 Bytes(
SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second))
1075 assert(HintByteOffset < map.bytes().next());
1076 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1085 if (HintCol < PrevHintEndCol)
1086 HintCol = PrevHintEndCol + 1;
1090 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1091 (HintCol - PrevHintEndCol) +
1092 Columns(H.CodeToInsert.size());
1093 if (NewFixItLineSize > FixItInsertionLine.size())
1094 FixItInsertionLine.resize(NewFixItLineSize.V,
' ');
1096 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1097 FixItInsertionLine.end() - H.CodeToInsert.size());
1099 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1103 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1105 return FixItInsertionLine;
1109 unsigned L = 1u, M = 10u;
1110 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1124 const std::pair<unsigned, unsigned> &Lines,
FileID FID,
1134 unsigned StartLineNo =
SM.getExpansionLineNumber(Begin);
1135 if (StartLineNo > Lines.second ||
SM.getFileID(Begin) != FID)
1138 unsigned EndLineNo =
SM.getExpansionLineNumber(End);
1139 if (EndLineNo < Lines.first ||
SM.getFileID(End) != FID)
1142 Bytes StartByte =
SM.getExpansionColumnNumber(Begin);
1143 Bytes EndByte =
SM.getExpansionColumnNumber(End);
1144 assert(StartByte.V != 0 &&
"StartByte must be valid, 0 is invalid");
1145 assert(EndByte.V != 0 &&
"EndByte must be valid, 0 is invalid");
1146 if (R.isTokenRange())
1150 if (StartLineNo == EndLineNo) {
1151 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1156 LineRanges.push_back(
1157 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1160 for (
unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1161 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1164 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1177static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1182 assert(StartLineNumber <= EndLineNumber);
1183 auto SnippetRanges =
1184 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1185 EndLineNumber - StartLineNumber + 1);
1187 if (!PP || !ShowColors)
1188 return SnippetRanges;
1192 return SnippetRanges;
1194 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1195 Lexer L{FID, *Buff,
SM, LangOpts};
1198 const char *FirstLineStart =
1200 SM.getDecomposedLoc(
SM.translateLineCol(FID, StartLineNumber, 1)).second;
1201 if (
const char *CheckPoint = PP->
getCheckPoint(FID, FirstLineStart)) {
1202 assert(CheckPoint >= Buff->getBufferStart() &&
1203 CheckPoint <= Buff->getBufferEnd());
1204 assert(CheckPoint <= FirstLineStart);
1205 size_t Offset = CheckPoint - Buff->getBufferStart();
1206 L.
seek(Offset,
false);
1212 const Token &
T,
unsigned Start,
unsigned Length) ->
void {
1213 if (
T.is(tok::raw_identifier)) {
1214 StringRef RawIdent =
T.getRawIdentifier();
1219 if (llvm::StringSwitch<bool>(RawIdent)
1221 .Case(
"false",
true)
1222 .Case(
"nullptr",
true)
1223 .Case(
"__func__",
true)
1224 .Case(
"__objc_yes__",
true)
1225 .Case(
"__objc_no__",
true)
1226 .Case(
"__null",
true)
1227 .Case(
"__FUNCDNAME__",
true)
1228 .Case(
"__FUNCSIG__",
true)
1229 .Case(
"__FUNCTION__",
true)
1230 .Case(
"__FUNCSIG__",
true)
1242 assert(
T.is(tok::comment));
1251 if (
T.is(tok::unknown))
1255 if (!
T.is(tok::raw_identifier) && !
T.is(tok::comment) &&
1260 unsigned TokenEndLine =
SM.getSpellingLineNumber(
T.getEndLoc(), &
Invalid);
1261 if (
Invalid || TokenEndLine < StartLineNumber)
1264 assert(TokenEndLine >= StartLineNumber);
1266 unsigned TokenStartLine =
1267 SM.getSpellingLineNumber(
T.getLocation(), &
Invalid);
1271 if (TokenStartLine > EndLineNumber)
1274 Bytes StartCol =
SM.getSpellingColumnNumber(
T.getLocation(), &
Invalid) - 1;
1279 if (TokenStartLine == TokenEndLine) {
1281 SnippetRanges[TokenStartLine - StartLineNumber];
1282 appendStyle(LineRanges,
T, StartCol.V,
T.getLength());
1285 assert((TokenEndLine - TokenStartLine) >= 1);
1289 Bytes EndCol =
SM.getSpellingColumnNumber(
T.getEndLoc(), &
Invalid) - 1;
1295 unsigned L = TokenStartLine;
1296 unsigned LineLength = 0;
1297 for (
unsigned I = 0; I <= Spelling.size(); ++I) {
1300 if (L >= StartLineNumber) {
1302 SnippetRanges[L - StartLineNumber];
1304 if (L == TokenStartLine)
1305 appendStyle(LineRanges,
T, StartCol.V, LineLength);
1306 else if (L == TokenEndLine)
1307 appendStyle(LineRanges,
T, 0, EndCol.V);
1309 appendStyle(LineRanges,
T, 0, LineLength);
1313 if (L > EndLineNumber)
1322 return SnippetRanges;
1332void TextDiagnostic::emitSnippetAndCaret(
1335 assert(Loc.
isValid() &&
"must have a valid source location here");
1336 assert(Loc.
isFileID() &&
"must have a file location here");
1346 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1358 const char *BufStart = BufData.data();
1359 const char *BufEnd = BufStart + BufData.size();
1365 static const size_t MaxLineLengthToPrint = 4096;
1366 if (CaretByte > MaxLineLengthToPrint)
1370 const unsigned MaxLines =
DiagOpts.SnippetLineLimit;
1371 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1373 for (
const auto &I : Ranges) {
1378 std::min(DisplayLineNo,
SM.getPresumedLineNumber(I.getBegin()));
1385 unsigned MaxLineNoDisplayWidth =
1389 auto indentForLineNumbers = [&] {
1390 if (MaxLineNoDisplayWidth > 0)
1391 OS.indent(MaxLineNoDisplayWidth + 2) <<
"| ";
1396 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1400 SmallVector<LineRange> LineRanges =
1403 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1404 ++LineNo, ++DisplayLineNo) {
1406 const char *LineStart =
1408 SM.getDecomposedLoc(
SM.translateLineCol(FID, LineNo, 1)).second;
1409 if (LineStart == BufEnd)
1413 const char *LineEnd = LineStart;
1414 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1419 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1423 std::string SourceLine(LineStart, LineEnd);
1425 while (!SourceLine.empty() && SourceLine.back() ==
'\0' &&
1426 (LineNo != CaretLineNo ||
1427 SourceLine.size() >
static_cast<size_t>(CaretByte.V)))
1428 SourceLine.pop_back();
1431 const SourceColumnMap SourceColMap(SourceLine,
DiagOpts.TabStop);
1433 std::string CaretLine;
1435 for (
const auto &LR : LineRanges) {
1436 if (LR.LineNo == LineNo)
1441 if (CaretLineNo == LineNo) {
1442 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1444 std::max(
static_cast<size_t>(Col.V) + 1, CaretLine.size()),
' ');
1445 CaretLine[Col.V] =
'^';
1448 std::string FixItInsertionLine =
1453 Columns MessageLength =
DiagOpts.MessageLength;
1454 if (MessageLength.V != 0)
1456 MessageLength, SourceColMap);
1462 if (
DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1463 SourceLine =
' ' + SourceLine;
1464 CaretLine =
' ' + CaretLine;
1468 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1469 SourceStyles[LineNo - Lines.first]);
1471 if (!CaretLine.empty()) {
1472 indentForLineNumbers();
1475 OS << CaretLine <<
'\n';
1480 if (!FixItInsertionLine.empty()) {
1481 indentForLineNumbers();
1487 OS << FixItInsertionLine <<
'\n';
1494 emitParseableFixits(Hints,
SM);
1497void TextDiagnostic::emitSnippet(StringRef SourceLine,
1498 unsigned MaxLineNoDisplayWidth,
1499 unsigned LineNo,
unsigned DisplayLineNo,
1502 if (MaxLineNoDisplayWidth > 0) {
1504 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1505 << DisplayLineNo <<
" | ";
1509 bool PrintReversed =
false;
1510 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1512 while (I < SourceLine.size()) {
1513 auto [Str, WasPrintable] =
1518 if (WasPrintable == PrintReversed) {
1519 PrintReversed = !PrintReversed;
1524 CurrentColor = std::nullopt;
1529 const auto *CharStyle = llvm::find_if(Styles, [I](
const StyleRange &R) {
1530 return (R.Start < I && R.End >= I);
1533 if (CharStyle != Styles.end()) {
1534 if (!CurrentColor ||
1535 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1536 OS.changeColor(CharStyle->Color);
1537 CurrentColor = CharStyle->Color;
1539 }
else if (CurrentColor) {
1541 CurrentColor = std::nullopt;
1561 for (
const auto &H : Hints) {
1562 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1563 H.RemoveRange.getEnd().isMacroID())
1567 for (
const auto &H : Hints) {
1568 SourceLocation BLoc = H.RemoveRange.getBegin();
1569 SourceLocation ELoc = H.RemoveRange.getEnd();
1575 if (H.RemoveRange.isTokenRange())
1580 PresumedLoc PLoc =
SM.getPresumedLoc(BLoc);
1586 OS <<
"\":{" <<
SM.getLineNumber(BInfo.first, BInfo.second)
1587 <<
':' <<
SM.getColumnNumber(BInfo.first, BInfo.second)
1588 <<
'-' <<
SM.getLineNumber(EInfo.first, EInfo.second)
1589 <<
':' <<
SM.getColumnNumber(EInfo.first, EInfo.second)
1591 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)
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.