16#include "llvm/ADT/StringExtras.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/ErrorHandling.h"
19#include "llvm/Support/Locale.h"
20#include "llvm/Support/Path.h"
26static constexpr raw_ostream::Colors
NoteColor = raw_ostream::CYAN;
27static constexpr raw_ostream::Colors
RemarkColor = raw_ostream::BLUE;
28static constexpr raw_ostream::Colors
FixitColor = raw_ostream::GREEN;
29static constexpr raw_ostream::Colors
CaretColor = raw_ostream::GREEN;
30static constexpr raw_ostream::Colors
WarningColor = raw_ostream::MAGENTA;
32static constexpr raw_ostream::Colors
ErrorColor = raw_ostream::RED;
33static constexpr raw_ostream::Colors
FatalColor = raw_ostream::RED;
35static constexpr raw_ostream::Colors
SavedColor = raw_ostream::SAVEDCOLOR;
41static constexpr raw_ostream::Colors
CommentColor = raw_ostream::YELLOW;
42static constexpr raw_ostream::Colors
LiteralColor = raw_ostream::GREEN;
43static constexpr raw_ostream::Colors
KeywordColor = raw_ostream::BLUE;
46template <
typename Sub>
class ColumnsOrBytes {
49 ColumnsOrBytes(
int V) :
V(
V) {}
50 bool isValid()
const {
return V != -1; }
51 Sub next()
const {
return Sub(
V + 1); }
52 Sub prev()
const {
return Sub(
V - 1); }
54 bool operator>(Sub O)
const {
return V > O.V; }
55 bool operator<(Sub O)
const {
return V < O.V; }
59 Sub operator+(Sub B)
const {
return Sub(
V + B.V); }
62 return *
static_cast<Sub *
>(
this);
64 Sub operator-(Sub B)
const {
return Sub(
V - B.V); }
65 Sub &operator-=(Sub B) {
67 return *
static_cast<Sub *
>(
this);
71class Bytes final :
public ColumnsOrBytes<Bytes> {
73 Bytes(
int V) : ColumnsOrBytes(
V) {}
76class Columns final :
public ColumnsOrBytes<Columns> {
78 Columns(
int V) : ColumnsOrBytes(
V) {}
87 OS << Str.slice(0, Pos);
88 if (Pos == StringRef::npos)
91 Str = Str.substr(Pos + 1);
109 if (SourceLine[--i]==
'\t')
135static std::pair<SmallString<16>,
bool>
138 assert(I &&
"I must not be null");
139 assert(*I < SourceLine.size() &&
"must point to a valid index");
141 if (SourceLine[*I] ==
'\t') {
143 "Invalid -ftabstop value");
145 unsigned NumSpaces = TabStop - (LineBytes % TabStop);
146 assert(0 < NumSpaces && NumSpaces <= TabStop
147 &&
"Invalid computation of space amt");
151 ExpandedTab.assign(NumSpaces,
' ');
152 return std::make_pair(ExpandedTab,
true);
155 const unsigned char *Begin = SourceLine.bytes_begin() + *I;
158 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) {
162 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin);
163 const unsigned char *End = Begin + CharSize;
166 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) {
168 llvm::UTF32 *CPtr = &
C;
171 unsigned char const *OriginalBegin = Begin;
172 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
173 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);
175 assert(Res == llvm::conversionOK);
176 assert(OriginalBegin < Begin);
177 assert(
unsigned(Begin - OriginalBegin) == CharSize);
179 (*I) += (Begin - OriginalBegin);
182 if (llvm::sys::locale::isPrint(
C))
188 Str.insert(Str.begin() + 3, llvm::hexdigit(
C % 16));
191 while (Str.size() < 8)
192 Str.insert(Str.begin() + 3, llvm::hexdigit(0));
193 return std::make_pair(Str,
false);
198 unsigned char Byte = SourceLine[*I];
199 ExpandedByte[1] = llvm::hexdigit(Byte / 16);
200 ExpandedByte[2] = llvm::hexdigit(Byte % 16);
202 return std::make_pair(ExpandedByte,
false);
205static void expandTabs(std::string &SourceLine,
unsigned TabStop) {
206 size_t I = SourceLine.size();
209 if (SourceLine[I] !=
'\t')
212 auto [Str, Printable] =
214 SourceLine.replace(I, 1, Str.c_str());
257 assert(BytesOut.empty());
258 assert(ColumnsOut.empty());
260 if (SourceLine.empty()) {
261 BytesOut.resize(1u, Bytes(0));
262 ColumnsOut.resize(1u, Columns(0));
266 ColumnsOut.resize(SourceLine.size() + 1, -1);
268 Columns NumColumns = 0;
270 while (I < SourceLine.size()) {
271 ColumnsOut[I] = NumColumns;
272 BytesOut.resize(NumColumns.V + 1, -1);
273 BytesOut.back() = Bytes(I);
274 auto [Str, Printable] =
276 NumColumns += Columns(llvm::sys::locale::columnWidth(Str));
279 ColumnsOut.back() = NumColumns;
280 BytesOut.resize(NumColumns.V + 1, -1);
281 BytesOut.back() = Bytes(I);
285struct SourceColumnMap {
286 SourceColumnMap(StringRef SourceLine,
unsigned TabStop)
287 : SourceLine(SourceLine) {
291 assert(ByteToColumn.size() == SourceLine.size() + 1);
292 assert(0 < ByteToColumn.size() && 0 < ColumnToByte.size());
293 assert(ByteToColumn.size() ==
294 static_cast<unsigned>(ColumnToByte.back().V + 1));
295 assert(
static_cast<unsigned>(ByteToColumn.back().V + 1) ==
296 ColumnToByte.size());
298 Columns columns()
const {
return ByteToColumn.back(); }
299 Bytes
bytes()
const {
return ColumnToByte.back(); }
303 Columns byteToColumn(Bytes N)
const {
304 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size()));
305 return ByteToColumn[N.V];
309 Columns byteToContainingColumn(Bytes N)
const {
310 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size()));
311 while (!ByteToColumn[N.V].isValid())
313 return ByteToColumn[N.V];
319 Bytes columnToByte(Columns N)
const {
320 assert(0 <= N.V && N.V <
static_cast<int>(ColumnToByte.size()));
321 return ColumnToByte[N.V];
325 Bytes startOfNextColumn(Bytes N)
const {
326 assert(0 <= N.V && N.V <
static_cast<int>(ByteToColumn.size() - 1));
328 while (!byteToColumn(N).isValid())
334 Bytes startOfPreviousColumn(Bytes N)
const {
335 assert(0 < N.V && N.V <
static_cast<int>(ByteToColumn.size()));
337 while (!byteToColumn(N).isValid())
342 StringRef getSourceLine()
const {
return SourceLine; }
345 StringRef SourceLine;
346 SmallVector<Columns, 200> ByteToColumn;
347 SmallVector<Bytes, 200> ColumnToByte;
354 std::string &SourceLine, std::string &CaretLine,
355 std::string &FixItInsertionLine, Columns NonGutterColumns,
356 const SourceColumnMap &Map,
358 Columns CaretColumns = CaretLine.size();
359 Columns FixItColumns = 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 while (CaretStart != CaretEnd &&
isWhitespace(CaretLine[CaretStart.V]))
373 CaretStart = CaretStart.next();
375 while (CaretEnd != CaretStart &&
isWhitespace(CaretLine[CaretEnd.V]))
376 CaretEnd = CaretEnd.prev();
383 if (!FixItInsertionLine.empty()) {
387 Bytes FixItStart = 0;
388 Bytes FixItEnd = Bytes(FixItInsertionLine.size());
389 while (FixItStart != FixItEnd &&
391 FixItStart = FixItStart.next();
393 while (FixItEnd != FixItStart &&
395 FixItEnd = FixItEnd.prev();
397 Columns FixItStartCol = Columns(FixItStart.V);
398 Columns FixItEndCol = Columns(llvm::sys::locale::columnWidth(
399 FixItInsertionLine.substr(0, FixItEnd.V)));
401 CaretStart = std::min(FixItStartCol.V, CaretStart.V);
402 CaretEnd = std::max(FixItEndCol.V, CaretEnd.V);
408 while (CaretEnd < Map.columns() && !Map.columnToByte(CaretEnd).isValid())
409 CaretEnd = CaretEnd.next();
412 (CaretStart > Map.columns() || Map.columnToByte(CaretStart).isValid()) &&
413 "CaretStart must not point to a column in the middle of a source"
415 assert((CaretEnd > Map.columns() || Map.columnToByte(CaretEnd).isValid()) &&
416 "CaretEnd must not point to a column in the middle of a source line"
424 Bytes SourceStart = Map.columnToByte(std::min(CaretStart.V, Map.columns().V));
425 Bytes SourceEnd = Map.columnToByte(std::min(CaretEnd.V, Map.columns().V));
427 Columns CaretColumnsOutsideSource =
428 CaretEnd - CaretStart -
429 (Map.byteToColumn(SourceEnd) - Map.byteToColumn(SourceStart));
431 constexpr StringRef FrontEllipse =
" ...";
432 constexpr StringRef FrontSpace =
" ";
433 constexpr StringRef BackEllipse =
"...";
434 Columns EllipsesColumns = Columns(FrontEllipse.size() + BackEllipse.size());
436 Columns TargetColumns = NonGutterColumns;
439 if (TargetColumns > EllipsesColumns + CaretColumnsOutsideSource)
440 TargetColumns -= EllipsesColumns + CaretColumnsOutsideSource;
442 while (SourceStart > 0 || SourceEnd < SourceLine.size()) {
443 bool ExpandedRegion =
false;
445 if (SourceStart > 0) {
446 Bytes NewStart = Map.startOfPreviousColumn(SourceStart);
451 while (NewStart > 0 &&
isWhitespace(SourceLine[NewStart.V]))
452 NewStart = Map.startOfPreviousColumn(NewStart);
455 while (NewStart > 0) {
456 Bytes Prev = Map.startOfPreviousColumn(NewStart);
462 assert(Map.byteToColumn(NewStart).isValid());
464 Map.byteToColumn(SourceEnd) - Map.byteToColumn(NewStart);
465 if (NewColumns <= TargetColumns) {
466 SourceStart = NewStart;
467 ExpandedRegion =
true;
471 if (SourceEnd < SourceLine.size()) {
472 Bytes NewEnd = Map.startOfNextColumn(SourceEnd);
477 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
478 NewEnd = Map.startOfNextColumn(NewEnd);
481 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd.V]))
482 NewEnd = Map.startOfNextColumn(NewEnd);
484 assert(Map.byteToColumn(NewEnd).isValid());
486 Map.byteToColumn(NewEnd) - Map.byteToColumn(SourceStart);
487 if (NewColumns <= TargetColumns) {
489 ExpandedRegion =
true;
497 CaretStart = Map.byteToColumn(SourceStart);
498 CaretEnd = Map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
502 assert(CaretStart.isValid() && CaretEnd.isValid() && SourceStart.isValid() &&
503 SourceEnd.isValid());
504 assert(SourceStart <= SourceEnd);
505 assert(CaretStart <= CaretEnd);
507 Columns BackColumnsRemoved =
508 Map.byteToColumn(Bytes{
static_cast<int>(SourceLine.size())}) -
509 Map.byteToColumn(SourceEnd);
510 Columns FrontColumnsRemoved = CaretStart;
511 Columns ColumnsKept = CaretEnd - CaretStart;
514 assert(FrontColumnsRemoved + ColumnsKept + BackColumnsRemoved >
526 FrontColumnsRemoved > FrontEllipse.size()
527 ? (Map.columnToByte(FrontColumnsRemoved) - Bytes(FrontEllipse.size()))
530 CaretEnd < Map.columns() ? Map.columnToByte(CaretEnd.V) : CaretEnd.V;
533 if (R.Start >=
static_cast<unsigned>(CodeEnd.V) ||
534 R.End <
static_cast<unsigned>(BytesRemoved.V)) {
535 R.Start = R.End = std::numeric_limits<int>::max();
540 R.Start -= BytesRemoved.V;
541 R.End -= BytesRemoved.V;
544 if (R.Start <
static_cast<unsigned>(CodeEnd.V) &&
545 R.End >
static_cast<unsigned>(CodeEnd.V))
546 R.End = CodeEnd.V + 1;
551 if (BackColumnsRemoved > Columns(BackEllipse.size()))
552 SourceLine.replace(SourceEnd.V, std::string::npos, BackEllipse);
555 if (FrontColumnsRemoved + ColumnsKept <= NonGutterColumns)
559 if (FrontColumnsRemoved > Columns(FrontEllipse.size())) {
560 SourceLine.replace(0, SourceStart.V, FrontEllipse);
561 CaretLine.replace(0, CaretStart.V, FrontSpace);
562 if (!FixItInsertionLine.empty())
563 FixItInsertionLine.replace(0, CaretStart.V, FrontSpace);
587 case '\'':
return '\'';
588 case '`':
return '\'';
589 case '"':
return '"';
590 case '(':
return ')';
591 case '[':
return ']';
592 case '{':
return '}';
605 unsigned Length,
unsigned Column,
607 assert(Start < Str.size() &&
"Invalid start position!");
608 unsigned End = Start + 1;
611 if (End == Str.size())
627 PunctuationEndStack.push_back(EndPunct);
628 while (End < Length && !PunctuationEndStack.empty()) {
629 if (Str[End] == PunctuationEndStack.back())
630 PunctuationEndStack.pop_back();
632 PunctuationEndStack.push_back(SubEndPunct);
641 unsigned PunctWordLength = End - Start;
643 Column + PunctWordLength <= Columns ||
646 PunctWordLength < Columns/3)
670 unsigned Column,
bool Bold) {
671 const unsigned Length = std::min(Str.find(
'\n'), Str.size());
672 bool TextNormal =
true;
674 bool Wrapped =
false;
675 for (
unsigned WordStart = 0, WordEnd; WordStart < Length;
676 WordStart = WordEnd) {
679 if (WordStart == Length)
686 unsigned WordLength = WordEnd - WordStart;
687 if (
Column + WordLength < Columns) {
712 assert(TextNormal &&
"Text highlighted at end of diagnostic message.");
728 uint64_t StartOfLocationInfo = OS.getColumn();
734 if (
DiagOpts.showColors(OS.has_colors()))
741 Message, OS.getColumn() - StartOfLocationInfo,
743 DiagOpts.showColors(OS.has_colors()));
757 llvm_unreachable(
"Invalid diagnostic type");
778 llvm_unreachable(
"Invalid diagnostic type");
794 unsigned CurrentColumn,
795 unsigned Columns,
bool ShowColors) {
797 if (ShowColors && !IsSupplemental) {
809 assert(
Normal &&
"Formatting should have returned to normal");
817void TextDiagnostic::emitFilename(StringRef Filename,
const SourceManager &SM) {
839 TmpFilename =
File->getName();
841 llvm::sys::path::native(TmpFilename);
842 llvm::sys::path::remove_dots(TmpFilename,
true);
843 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
866 emitFilename(FE->getName(), Loc.
getManager());
872 unsigned LineNo = PLoc.
getLine();
877 if (
DiagOpts.showColors(OS.has_colors()))
897 if (
LangOpts.MSCompatibilityVersion &&
912 if (
LangOpts.MSCompatibilityVersion &&
919 if (
DiagOpts.ShowSourceRanges && !Ranges.empty()) {
921 bool PrintedRange =
false;
924 for (
const auto &R : Ranges) {
925 std::optional<CharSourceRange> FileRange =
935 unsigned TokSize = 0;
936 if (FileRange->isTokenRange())
941 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
955 OS <<
"In file included from ";
957 OS <<
':' << PLoc.
getLine() <<
":\n";
959 OS <<
"In included file:\n";
963 StringRef ModuleName) {
965 OS <<
"In module '" << ModuleName <<
"' imported from "
968 OS <<
"In module '" << ModuleName <<
"':\n";
973 StringRef ModuleName) {
975 OS <<
"While building module '" << ModuleName <<
"' imported from "
978 OS <<
"While building module '" << ModuleName <<
"':\n";
982static std::optional<std::pair<unsigned, unsigned>>
999static std::pair<unsigned, unsigned>
1001 unsigned MaxRange) {
1003 unsigned Slack = MaxRange - (A.second - A.first + 1);
1008 unsigned Min = std::min(A.first, B.first);
1009 unsigned Max = std::max(A.second, B.second);
1010 if (
Max -
Min + 1 <= MaxRange)
1015 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
1016 (B.second < A.second && A.second - B.second + 1 > MaxRange))
1025 A.second = std::min(A.second + (Slack + 1) / 2,
Max);
1026 Slack = MaxRange - (A.second - A.first + 1);
1027 A.first = std::max(
Min + Slack, A.first) - Slack;
1028 A.second = std::min(A.first + MaxRange - 1,
Max);
1040 std::string &CaretLine) {
1042 Bytes StartByte = R.StartByte;
1043 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] ==
' ' ||
1044 Map.getSourceLine()[StartByte.V] ==
'\t'))
1045 StartByte = Map.startOfNextColumn(StartByte);
1048 Bytes EndByte = std::min(R.EndByte.V, Map.bytes().V);
1049 while (EndByte.V != 0 && (Map.getSourceLine()[EndByte.V - 1] ==
' ' ||
1050 Map.getSourceLine()[EndByte.V - 1] ==
'\t'))
1051 EndByte = Map.startOfPreviousColumn(EndByte);
1056 if (StartByte > EndByte)
1059 assert(StartByte <= EndByte &&
"Invalid range!");
1061 Columns StartCol = Map.byteToContainingColumn(StartByte);
1062 Columns EndCol = Map.byteToContainingColumn(EndByte);
1064 if (CaretLine.size() <
static_cast<size_t>(EndCol.V))
1065 CaretLine.resize(EndCol.V,
' ');
1067 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V,
'~');
1071 const SourceColumnMap &map,
1075 std::string FixItInsertionLine;
1076 if (Hints.empty() || !DiagOpts.ShowFixits)
1077 return FixItInsertionLine;
1078 Columns PrevHintEndCol = 0;
1080 for (
const auto &H : Hints) {
1081 if (H.CodeToInsert.empty())
1088 if (FID == HintLocInfo.first &&
1089 LineNo == SM.
getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1090 StringRef(H.CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1096 Bytes HintByteOffset =
1101 assert(HintByteOffset < map.bytes().next());
1102 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1111 if (HintCol < PrevHintEndCol)
1112 HintCol = PrevHintEndCol + 1;
1116 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1117 (HintCol - PrevHintEndCol) +
1118 Columns(H.CodeToInsert.size());
1119 if (NewFixItLineSize > FixItInsertionLine.size())
1120 FixItInsertionLine.resize(NewFixItLineSize.V,
' ');
1122 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1123 FixItInsertionLine.end() - H.CodeToInsert.size());
1125 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1129 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1131 return FixItInsertionLine;
1135 unsigned L = 1u, M = 10u;
1136 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1150 const std::pair<unsigned, unsigned> &Lines,
FileID FID,
1161 if (StartLineNo > Lines.second || SM.
getFileID(Begin) != FID)
1165 if (EndLineNo < Lines.first || SM.
getFileID(End) != FID)
1170 assert(StartByte.V != 0 &&
"StartByte must be valid, 0 is invalid");
1171 assert(EndByte.V != 0 &&
"EndByte must be valid, 0 is invalid");
1172 if (R.isTokenRange())
1176 if (StartLineNo == EndLineNo) {
1177 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1182 LineRanges.push_back(
1183 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1186 for (
unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1187 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1190 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1203static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1208 assert(StartLineNumber <= EndLineNumber);
1209 auto SnippetRanges =
1210 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1211 EndLineNumber - StartLineNumber + 1);
1213 if (!PP || !ShowColors)
1214 return SnippetRanges;
1218 return SnippetRanges;
1220 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1221 Lexer L{FID, *Buff, SM, LangOpts};
1224 const char *FirstLineStart =
1227 if (
const char *CheckPoint = PP->
getCheckPoint(FID, FirstLineStart)) {
1228 assert(CheckPoint >= Buff->getBufferStart() &&
1229 CheckPoint <= Buff->getBufferEnd());
1230 assert(CheckPoint <= FirstLineStart);
1231 size_t Offset = CheckPoint - Buff->getBufferStart();
1232 L.
seek(Offset,
false);
1238 const Token &
T,
unsigned Start,
unsigned Length) ->
void {
1239 if (
T.is(tok::raw_identifier)) {
1240 StringRef RawIdent =
T.getRawIdentifier();
1245 if (llvm::StringSwitch<bool>(RawIdent)
1247 .Case(
"false",
true)
1248 .Case(
"nullptr",
true)
1249 .Case(
"__func__",
true)
1250 .Case(
"__objc_yes__",
true)
1251 .Case(
"__objc_no__",
true)
1252 .Case(
"__null",
true)
1253 .Case(
"__FUNCDNAME__",
true)
1254 .Case(
"__FUNCSIG__",
true)
1255 .Case(
"__FUNCTION__",
true)
1256 .Case(
"__FUNCSIG__",
true)
1268 assert(
T.is(tok::comment));
1277 if (
T.is(tok::unknown))
1281 if (!
T.is(tok::raw_identifier) && !
T.is(tok::comment) &&
1287 if (
Invalid || TokenEndLine < StartLineNumber)
1290 assert(TokenEndLine >= StartLineNumber);
1292 unsigned TokenStartLine =
1297 if (TokenStartLine > EndLineNumber)
1305 if (TokenStartLine == TokenEndLine) {
1307 SnippetRanges[TokenStartLine - StartLineNumber];
1308 appendStyle(LineRanges,
T, StartCol.V,
T.getLength());
1311 assert((TokenEndLine - TokenStartLine) >= 1);
1321 unsigned L = TokenStartLine;
1322 unsigned LineLength = 0;
1323 for (
unsigned I = 0; I <= Spelling.size(); ++I) {
1326 if (L >= StartLineNumber) {
1328 SnippetRanges[L - StartLineNumber];
1330 if (L == TokenStartLine)
1331 appendStyle(LineRanges,
T, StartCol.V, LineLength);
1332 else if (L == TokenEndLine)
1333 appendStyle(LineRanges,
T, 0, EndCol.V);
1335 appendStyle(LineRanges,
T, 0, LineLength);
1339 if (L > EndLineNumber)
1348 return SnippetRanges;
1358void TextDiagnostic::emitSnippetAndCaret(
1361 assert(Loc.
isValid() &&
"must have a valid source location here");
1362 assert(Loc.
isFileID() &&
"must have a file location here");
1372 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1384 const char *BufStart = BufData.data();
1385 const char *BufEnd = BufStart + BufData.size();
1391 static const size_t MaxLineLengthToPrint = 4096;
1392 if (CaretByte > MaxLineLengthToPrint)
1396 const unsigned MaxLines =
DiagOpts.SnippetLineLimit;
1397 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1399 for (
const auto &I : Ranges) {
1411 unsigned MaxLineNoDisplayWidth =
1415 auto indentForLineNumbers = [&] {
1416 if (MaxLineNoDisplayWidth > 0)
1417 OS.indent(MaxLineNoDisplayWidth + 2) <<
"| ";
1420 Columns MessageLength =
DiagOpts.MessageLength;
1422 if (MessageLength != 0 && MessageLength <= Columns(MaxLineNoDisplayWidth + 4))
1427 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1429 DiagOpts.showColors(OS.has_colors()), FID, SM);
1431 SmallVector<LineRange> LineRanges =
1434 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1435 ++LineNo, ++DisplayLineNo) {
1437 const char *LineStart =
1440 if (LineStart == BufEnd)
1444 const char *LineEnd = LineStart;
1445 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1450 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1454 std::string SourceLine(LineStart, LineEnd);
1456 while (!SourceLine.empty() && SourceLine.back() ==
'\0' &&
1457 (LineNo != CaretLineNo ||
1458 SourceLine.size() >
static_cast<size_t>(CaretByte.V)))
1459 SourceLine.pop_back();
1462 const SourceColumnMap SourceColMap(SourceLine,
DiagOpts.TabStop);
1464 std::string CaretLine;
1466 for (
const auto &LR : LineRanges) {
1467 if (LR.LineNo == LineNo)
1472 if (CaretLineNo == LineNo) {
1473 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1475 std::max(
static_cast<size_t>(Col.V) + 1, CaretLine.size()),
' ');
1476 CaretLine[Col.V] =
'^';
1479 std::string FixItInsertionLine =
1484 if (MessageLength != 0) {
1485 Columns NonGutterColumns = MessageLength;
1486 if (MaxLineNoDisplayWidth != 0)
1487 NonGutterColumns -= Columns(MaxLineNoDisplayWidth + 4);
1489 NonGutterColumns, SourceColMap,
1490 SourceStyles[LineNo - Lines.first]);
1497 if (
DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1498 SourceLine =
' ' + SourceLine;
1499 CaretLine =
' ' + CaretLine;
1503 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1504 SourceStyles[LineNo - Lines.first]);
1506 if (!CaretLine.empty()) {
1507 indentForLineNumbers();
1508 if (
DiagOpts.showColors(OS.has_colors()))
1510 OS << CaretLine <<
'\n';
1511 if (
DiagOpts.showColors(OS.has_colors()))
1515 if (!FixItInsertionLine.empty()) {
1516 indentForLineNumbers();
1517 if (
DiagOpts.showColors(OS.has_colors()))
1522 OS << FixItInsertionLine <<
'\n';
1523 if (
DiagOpts.showColors(OS.has_colors()))
1529 emitParseableFixits(Hints, SM);
1532void TextDiagnostic::emitSnippet(StringRef SourceLine,
1533 unsigned MaxLineNoDisplayWidth,
1534 unsigned LineNo,
unsigned DisplayLineNo,
1537 if (MaxLineNoDisplayWidth > 0) {
1539 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1540 << DisplayLineNo <<
" | ";
1544 bool PrintReversed =
false;
1545 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1547 while (I < SourceLine.size()) {
1548 auto [Str, WasPrintable] =
1552 if (
DiagOpts.showColors(OS.has_colors())) {
1553 if (WasPrintable == PrintReversed) {
1554 PrintReversed = !PrintReversed;
1559 CurrentColor = std::nullopt;
1564 const auto *CharStyle = llvm::find_if(Styles, [I](
const StyleRange &R) {
1565 return (
R.Start < I &&
R.End >= I);
1568 if (CharStyle != Styles.end()) {
1569 if (!CurrentColor ||
1570 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1571 OS.changeColor(CharStyle->Color);
1572 CurrentColor = CharStyle->Color;
1574 }
else if (CurrentColor) {
1576 CurrentColor = std::nullopt;
1583 if (
DiagOpts.showColors(OS.has_colors()))
1596 for (
const auto &H : Hints) {
1597 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1598 H.RemoveRange.getEnd().isMacroID())
1602 for (
const auto &H : Hints) {
1603 SourceLocation BLoc = H.RemoveRange.getBegin();
1604 SourceLocation ELoc = H.RemoveRange.getEnd();
1610 if (H.RemoveRange.isTokenRange())
1626 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.