clang 22.0.0git
TextDiagnostic.cpp
Go to the documentation of this file.
1//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
14#include "clang/Lex/Lexer.h"
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 <algorithm>
21#include <optional>
22
23using namespace clang;
24
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;
30static constexpr raw_ostream::Colors TemplateColor = raw_ostream::CYAN;
31static constexpr raw_ostream::Colors ErrorColor = raw_ostream::RED;
32static constexpr raw_ostream::Colors FatalColor = raw_ostream::RED;
33// Used for changing only the bold attribute.
34static constexpr raw_ostream::Colors SavedColor = raw_ostream::SAVEDCOLOR;
35
36// Magenta is taken for 'warning'. Red is already 'error' and 'cyan'
37// is already taken for 'note'. Green is already used to underline
38// source ranges. White and black are bad because of the usual
39// terminal backgrounds. Which leaves us only with TWO options.
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;
43
44namespace {
45template <typename Sub> class ColumnsOrBytes {
46public:
47 int V = 0;
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); }
52
53 bool operator>(Sub O) const { return V > O.V; }
54 bool operator<(Sub O) const { return V < O.V; }
55 bool operator<=(Sub B) const { return V <= B.V; }
56 bool operator!=(Sub C) const { return C.V != V; }
57
58 Sub operator+(Sub B) const { return Sub(V + B.V); }
59 Sub &operator+=(Sub B) {
60 V += B.V;
61 return *static_cast<Sub *>(this);
62 }
63 Sub operator-(Sub B) const { return Sub(V - B.V); }
64 Sub &operator-=(Sub B) {
65 V -= B.V;
66 return *static_cast<Sub *>(this);
67 }
68};
69
70class Bytes final : public ColumnsOrBytes<Bytes> {
71public:
72 Bytes(int V) : ColumnsOrBytes(V) {}
73};
74
75class Columns final : public ColumnsOrBytes<Columns> {
76public:
77 Columns(int V) : ColumnsOrBytes(V) {}
78};
79} // namespace
80
81/// Add highlights to differences in template strings.
82static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
83 bool &Normal, bool Bold) {
84 while (true) {
85 size_t Pos = Str.find(ToggleHighlight);
86 OS << Str.slice(0, Pos);
87 if (Pos == StringRef::npos)
88 break;
89
90 Str = Str.substr(Pos + 1);
91 if (Normal)
92 OS.changeColor(TemplateColor, true);
93 else {
94 OS.resetColor();
95 if (Bold)
96 OS.changeColor(SavedColor, true);
97 }
98 Normal = !Normal;
99 }
100}
101
102/// Number of spaces to indent when word-wrapping.
103const unsigned WordWrapIndentation = 6;
104
105static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
106 int bytes = 0;
107 while (0<i) {
108 if (SourceLine[--i]=='\t')
109 break;
110 ++bytes;
111 }
112 return bytes;
113}
114
115/// returns a printable representation of first item from input range
116///
117/// This function returns a printable representation of the next item in a line
118/// of source. If the next byte begins a valid and printable character, that
119/// character is returned along with 'true'.
120///
121/// Otherwise, if the next byte begins a valid, but unprintable character, a
122/// printable, escaped representation of the character is returned, along with
123/// 'false'. Otherwise a printable, escaped representation of the next byte
124/// is returned along with 'false'.
125///
126/// \note The index is updated to be used with a subsequent call to
127/// printableTextForNextCharacter.
128///
129/// \param SourceLine The line of source
130/// \param I Pointer to byte index,
131/// \param TabStop used to expand tabs
132/// \return pair(printable text, 'true' iff original text was printable)
133///
134static std::pair<SmallString<16>, bool>
135printableTextForNextCharacter(StringRef SourceLine, size_t *I,
136 unsigned TabStop) {
137 assert(I && "I must not be null");
138 assert(*I < SourceLine.size() && "must point to a valid index");
139
140 if (SourceLine[*I] == '\t') {
141 assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
142 "Invalid -ftabstop value");
143 unsigned LineBytes = bytesSincePreviousTabOrLineBegin(SourceLine, *I);
144 unsigned NumSpaces = TabStop - (LineBytes % TabStop);
145 assert(0 < NumSpaces && NumSpaces <= TabStop
146 && "Invalid computation of space amt");
147 ++(*I);
148
149 SmallString<16> ExpandedTab;
150 ExpandedTab.assign(NumSpaces, ' ');
151 return std::make_pair(ExpandedTab, true);
152 }
153
154 const unsigned char *Begin = SourceLine.bytes_begin() + *I;
155
156 // Fast path for the common ASCII case.
157 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) {
158 ++(*I);
159 return std::make_pair(SmallString<16>(Begin, Begin + 1), true);
160 }
161 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin);
162 const unsigned char *End = Begin + CharSize;
163
164 // Convert it to UTF32 and check if it's printable.
165 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) {
166 llvm::UTF32 C;
167 llvm::UTF32 *CPtr = &C;
168
169 // Begin and end before conversion.
170 unsigned char const *OriginalBegin = Begin;
171 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
172 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);
173 (void)Res;
174 assert(Res == llvm::conversionOK);
175 assert(OriginalBegin < Begin);
176 assert(unsigned(Begin - OriginalBegin) == CharSize);
177
178 (*I) += (Begin - OriginalBegin);
179
180 // Valid, multi-byte, printable UTF8 character.
181 if (llvm::sys::locale::isPrint(C))
182 return std::make_pair(SmallString<16>(OriginalBegin, End), true);
183
184 // Valid but not printable.
185 SmallString<16> Str("<U+>");
186 while (C) {
187 Str.insert(Str.begin() + 3, llvm::hexdigit(C % 16));
188 C /= 16;
189 }
190 while (Str.size() < 8)
191 Str.insert(Str.begin() + 3, llvm::hexdigit(0));
192 return std::make_pair(Str, false);
193 }
194
195 // Otherwise, not printable since it's not valid UTF8.
196 SmallString<16> ExpandedByte("<XX>");
197 unsigned char Byte = SourceLine[*I];
198 ExpandedByte[1] = llvm::hexdigit(Byte / 16);
199 ExpandedByte[2] = llvm::hexdigit(Byte % 16);
200 ++(*I);
201 return std::make_pair(ExpandedByte, false);
202}
203
204static void expandTabs(std::string &SourceLine, unsigned TabStop) {
205 size_t I = SourceLine.size();
206 while (I > 0) {
207 I--;
208 if (SourceLine[I] != '\t')
209 continue;
210 size_t TmpI = I;
211 auto [Str, Printable] =
212 printableTextForNextCharacter(SourceLine, &TmpI, TabStop);
213 SourceLine.replace(I, 1, Str.c_str());
214 }
215}
216
217/// \p BytesOut:
218/// A mapping from columns to the byte of the source line that produced the
219/// character displaying at that column. This is the inverse of \p ColumnsOut.
220///
221/// The last element in the array is the number of bytes in the source string.
222///
223/// example: (given a tabstop of 8)
224///
225/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
226///
227/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
228/// display)
229///
230/// \p ColumnsOut:
231/// A mapping from the bytes
232/// of the printable representation of the line to the columns those printable
233/// characters will appear at (numbering the first column as 0).
234///
235/// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab
236/// character) then the array will map that byte to the first column the
237/// tab appears at and the next value in the map will have been incremented
238/// more than once.
239///
240/// If a byte is the first in a sequence of bytes that together map to a single
241/// entity in the output, then the array will map that byte to the appropriate
242/// column while the subsequent bytes will be -1.
243///
244/// The last element in the array does not correspond to any byte in the input
245/// and instead is the number of columns needed to display the source
246///
247/// example: (given a tabstop of 8)
248///
249/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
250///
251/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
252/// display)
253static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop,
254 SmallVectorImpl<Bytes> &BytesOut,
255 SmallVectorImpl<Columns> &ColumnsOut) {
256 assert(BytesOut.empty());
257 assert(ColumnsOut.empty());
258
259 if (SourceLine.empty()) {
260 BytesOut.resize(1u, Bytes(0));
261 ColumnsOut.resize(1u, Columns(0));
262 return;
263 }
264
265 ColumnsOut.resize(SourceLine.size() + 1, -1);
266
267 Columns NumColumns = 0;
268 size_t I = 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] =
274 printableTextForNextCharacter(SourceLine, &I, TabStop);
275 NumColumns += Columns(llvm::sys::locale::columnWidth(Str));
276 }
277
278 ColumnsOut.back() = NumColumns;
279 BytesOut.resize(NumColumns.V + 1, -1);
280 BytesOut.back() = Bytes(I);
281}
282
283namespace {
284struct SourceColumnMap {
285 SourceColumnMap(StringRef SourceLine, unsigned TabStop)
286 : SourceLine(SourceLine) {
287
288 genColumnByteMapping(SourceLine, TabStop, ColumnToByte, ByteToColumn);
289
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());
296 }
297 Columns columns() const { return ByteToColumn.back(); }
298 Bytes bytes() const { return ColumnToByte.back(); }
299
300 /// Map a byte to the column which it is at the start of, or return -1
301 /// if it is not at the start of a column (for a UTF-8 trailing byte).
302 Columns byteToColumn(Bytes N) const {
303 assert(0 <= N.V && N.V < static_cast<int>(ByteToColumn.size()));
304 return ByteToColumn[N.V];
305 }
306
307 /// Map a byte to the first column which contains it.
308 Columns byteToContainingColumn(Bytes N) const {
309 assert(0 <= N.V && N.V < static_cast<int>(ByteToColumn.size()));
310 while (!ByteToColumn[N.V].isValid())
311 --N.V;
312 return ByteToColumn[N.V];
313 }
314
315 /// Map a column to the byte which starts the column, or return -1 if
316 /// the column the second or subsequent column of an expanded tab or similar
317 /// multi-column entity.
318 Bytes columnToByte(Columns N) const {
319 assert(0 <= N.V && N.V < static_cast<int>(ColumnToByte.size()));
320 return ColumnToByte[N.V];
321 }
322
323 /// Map from a byte index to the next byte which starts a column.
324 Bytes startOfNextColumn(Bytes N) const {
325 assert(0 <= N.V && N.V < static_cast<int>(ByteToColumn.size() - 1));
326 N = N.next();
327 while (!byteToColumn(N).isValid())
328 N = N.next();
329 return N;
330 }
331
332 /// Map from a byte index to the previous byte which starts a column.
333 Bytes startOfPreviousColumn(Bytes N) const {
334 assert(0 < N.V && N.V < static_cast<int>(ByteToColumn.size()));
335 N = N.prev();
336 while (!byteToColumn(N).isValid())
337 N = N.prev();
338 return N;
339 }
340
341 StringRef getSourceLine() const { return SourceLine; }
342
343private:
344 StringRef SourceLine;
345 SmallVector<Columns, 200> ByteToColumn;
346 SmallVector<Bytes, 200> ColumnToByte;
347};
348} // end anonymous namespace
349
350/// When the source code line we want to print is too long for
351/// the terminal, select the "interesting" region.
352static void selectInterestingSourceRegion(std::string &SourceLine,
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));
360 Columns MaxColumns =
361 std::max({Map.columns().V, CaretColumns.V, FixItColumns.V});
362 // if the number of columns is less than the desired number we're done
363 if (MaxColumns <= NonGutterColumns)
364 return;
365
366 // No special characters are allowed in CaretLine.
367 assert(llvm::none_of(CaretLine, [](char c) { return c < ' ' || '~' < c; }));
368
369 // Find the slice that we need to display the full caret line
370 // correctly.
371 Columns CaretStart = 0, CaretEnd = CaretLine.size();
372 for (; CaretStart != CaretEnd; CaretStart = CaretStart.next())
373 if (!isWhitespace(CaretLine[CaretStart.V]))
374 break;
375
376 for (; CaretEnd != CaretStart; CaretEnd = CaretEnd.prev())
377 if (!isWhitespace(CaretLine[CaretEnd.V - 1]))
378 break;
379
380 // caret has already been inserted into CaretLine so the above whitespace
381 // check is guaranteed to include the caret
382
383 // If we have a fix-it line, make sure the slice includes all of the
384 // fix-it information.
385 if (!FixItInsertionLine.empty()) {
386 // We can safely use the byte offset FixItStart as the column offset
387 // because the characters up until FixItStart are all ASCII whitespace
388 // characters.
389 Bytes FixItStart = 0;
390 Bytes FixItEnd = Bytes(FixItInsertionLine.size());
391 while (FixItStart != FixItEnd &&
392 isWhitespace(FixItInsertionLine[FixItStart.V]))
393 FixItStart = FixItStart.next();
394
395 while (FixItEnd != FixItStart &&
396 isWhitespace(FixItInsertionLine[FixItEnd.V - 1]))
397 FixItEnd = FixItEnd.prev();
398
399 Columns FixItStartCol = Columns(FixItStart.V);
400 Columns FixItEndCol = Columns(llvm::sys::locale::columnWidth(
401 FixItInsertionLine.substr(0, FixItEnd.V)));
402
403 CaretStart = std::min(FixItStartCol.V, CaretStart.V);
404 CaretEnd = std::max(FixItEndCol.V, CaretEnd.V);
405 }
406
407 // CaretEnd may have been set at the middle of a character
408 // If it's not at a character's first column then advance it past the current
409 // character.
410 while (CaretEnd < Map.columns() && !Map.columnToByte(CaretEnd).isValid())
411 CaretEnd = CaretEnd.next();
412
413 assert(
414 (CaretStart > Map.columns() || Map.columnToByte(CaretStart).isValid()) &&
415 "CaretStart must not point to a column in the middle of a source"
416 " line character");
417 assert((CaretEnd > Map.columns() || Map.columnToByte(CaretEnd).isValid()) &&
418 "CaretEnd must not point to a column in the middle of a source line"
419 " character");
420
421 // CaretLine[CaretStart, CaretEnd) contains all of the interesting
422 // parts of the caret line. While this slice is smaller than the
423 // number of columns we have, try to grow the slice to encompass
424 // more context.
425
426 Bytes SourceStart = Map.columnToByte(std::min(CaretStart.V, Map.columns().V));
427 Bytes SourceEnd = Map.columnToByte(std::min(CaretEnd.V, Map.columns().V));
428
429 Columns CaretColumnsOutsideSource =
430 CaretEnd - CaretStart -
431 (Map.byteToColumn(SourceEnd) - Map.byteToColumn(SourceStart));
432
433 constexpr StringRef FrontEllipse = " ...";
434 constexpr StringRef FrontSpace = " ";
435 constexpr StringRef BackEllipse = "...";
436 Columns EllipsesColumns = Columns(FrontEllipse.size() + BackEllipse.size());
437
438 Columns TargetColumns = NonGutterColumns;
439 // Give us extra room for the ellipses
440 // and any of the caret line that extends past the source
441 if (TargetColumns > EllipsesColumns + CaretColumnsOutsideSource)
442 TargetColumns -= EllipsesColumns + CaretColumnsOutsideSource;
443
444 while (SourceStart > 0 || SourceEnd < SourceLine.size()) {
445 bool ExpandedRegion = false;
446
447 if (SourceStart > 0) {
448 Bytes NewStart = Map.startOfPreviousColumn(SourceStart);
449
450 // Skip over any whitespace we see here; we're looking for
451 // another bit of interesting text.
452 // FIXME: Detect non-ASCII whitespace characters too.
453 while (NewStart > 0 && isWhitespace(SourceLine[NewStart.V]))
454 NewStart = Map.startOfPreviousColumn(NewStart);
455
456 // Skip over this bit of "interesting" text.
457 while (NewStart > 0) {
458 Bytes Prev = Map.startOfPreviousColumn(NewStart);
459 if (isWhitespace(SourceLine[Prev.V]))
460 break;
461 NewStart = Prev;
462 }
463
464 assert(Map.byteToColumn(NewStart).isValid());
465 Columns NewColumns =
466 Map.byteToColumn(SourceEnd) - Map.byteToColumn(NewStart);
467 if (NewColumns <= TargetColumns) {
468 SourceStart = NewStart;
469 ExpandedRegion = true;
470 }
471 }
472
473 if (SourceEnd < SourceLine.size()) {
474 Bytes NewEnd = Map.startOfNextColumn(SourceEnd);
475
476 // Skip over any whitespace we see here; we're looking for
477 // another bit of interesting text.
478 // FIXME: Detect non-ASCII whitespace characters too.
479 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd.V]))
480 NewEnd = Map.startOfNextColumn(NewEnd);
481
482 // Skip over this bit of "interesting" text.
483 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd.V]))
484 NewEnd = Map.startOfNextColumn(NewEnd);
485
486 assert(Map.byteToColumn(NewEnd).isValid());
487 Columns NewColumns =
488 Map.byteToColumn(NewEnd) - Map.byteToColumn(SourceStart);
489 if (NewColumns <= TargetColumns) {
490 SourceEnd = NewEnd;
491 ExpandedRegion = true;
492 }
493 }
494
495 if (!ExpandedRegion)
496 break;
497 }
498
499 CaretStart = Map.byteToColumn(SourceStart);
500 CaretEnd = Map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
501
502 // [CaretStart, CaretEnd) is the slice we want. Update the various
503 // output lines to show only this slice.
504 assert(CaretStart.isValid() && CaretEnd.isValid() && SourceStart.isValid() &&
505 SourceEnd.isValid());
506 assert(SourceStart <= SourceEnd);
507 assert(CaretStart <= CaretEnd);
508
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;
514
515 // We checked up front that the line needed truncation
516 assert(FrontColumnsRemoved + ColumnsKept + BackColumnsRemoved >
517 NonGutterColumns);
518
519 // The line needs some truncation, and we'd prefer to keep the front
520 // if possible, so remove the back
521 if (BackColumnsRemoved > Columns(BackEllipse.size()))
522 SourceLine.replace(SourceEnd.V, std::string::npos, BackEllipse);
523
524 // If that's enough then we're done
525 if (FrontColumnsRemoved + ColumnsKept <= Columns(NonGutterColumns))
526 return;
527
528 // Otherwise remove the front as well
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);
534 }
535}
536
537/// Skip over whitespace in the string, starting at the given
538/// index.
539///
540/// \returns The index of the first non-whitespace character that is
541/// greater than or equal to Idx or, if no such character exists,
542/// returns the end of the string.
543static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) {
544 while (Idx < Length && isWhitespace(Str[Idx]))
545 ++Idx;
546 return Idx;
547}
548
549/// If the given character is the start of some kind of
550/// balanced punctuation (e.g., quotes or parentheses), return the
551/// character that will terminate the punctuation.
552///
553/// \returns The ending punctuation character, if any, or the NULL
554/// character if the input character does not start any punctuation.
555static inline char findMatchingPunctuation(char c) {
556 switch (c) {
557 case '\'': return '\'';
558 case '`': return '\'';
559 case '"': return '"';
560 case '(': return ')';
561 case '[': return ']';
562 case '{': return '}';
563 default: break;
564 }
565
566 return 0;
567}
568
569/// Find the end of the word starting at the given offset
570/// within a string.
571///
572/// \returns the index pointing one character past the end of the
573/// word.
574static unsigned findEndOfWord(unsigned Start, StringRef Str,
575 unsigned Length, unsigned Column,
576 unsigned Columns) {
577 assert(Start < Str.size() && "Invalid start position!");
578 unsigned End = Start + 1;
579
580 // If we are already at the end of the string, take that as the word.
581 if (End == Str.size())
582 return End;
583
584 // Determine if the start of the string is actually opening
585 // punctuation, e.g., a quote or parentheses.
586 char EndPunct = findMatchingPunctuation(Str[Start]);
587 if (!EndPunct) {
588 // This is a normal word. Just find the first space character.
589 while (End < Length && !isWhitespace(Str[End]))
590 ++End;
591 return End;
592 }
593
594 // We have the start of a balanced punctuation sequence (quotes,
595 // parentheses, etc.). Determine the full sequence is.
596 SmallString<16> PunctuationEndStack;
597 PunctuationEndStack.push_back(EndPunct);
598 while (End < Length && !PunctuationEndStack.empty()) {
599 if (Str[End] == PunctuationEndStack.back())
600 PunctuationEndStack.pop_back();
601 else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
602 PunctuationEndStack.push_back(SubEndPunct);
603
604 ++End;
605 }
606
607 // Find the first space character after the punctuation ended.
608 while (End < Length && !isWhitespace(Str[End]))
609 ++End;
610
611 unsigned PunctWordLength = End - Start;
612 if (// If the word fits on this line
613 Column + PunctWordLength <= Columns ||
614 // ... or the word is "short enough" to take up the next line
615 // without too much ugly white space
616 PunctWordLength < Columns/3)
617 return End; // Take the whole thing as a single "word".
618
619 // The whole quoted/parenthesized string is too long to print as a
620 // single "word". Instead, find the "word" that starts just after
621 // the punctuation and use that end-point instead. This will recurse
622 // until it finds something small enough to consider a word.
623 return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
624}
625
626/// Print the given string to a stream, word-wrapping it to
627/// some number of columns in the process.
628///
629/// \param OS the stream to which the word-wrapping string will be
630/// emitted.
631/// \param Str the string to word-wrap and output.
632/// \param Columns the number of columns to word-wrap to.
633/// \param Column the column number at which the first character of \p
634/// Str will be printed. This will be non-zero when part of the first
635/// line has already been printed.
636/// \param Bold if the current text should be bold
637/// \returns true if word-wrapping was required, or false if the
638/// string fit on the first line.
639static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns,
640 unsigned Column, bool Bold) {
641 const unsigned Length = std::min(Str.find('\n'), Str.size());
642 bool TextNormal = true;
643
644 bool Wrapped = false;
645 for (unsigned WordStart = 0, WordEnd; WordStart < Length;
646 WordStart = WordEnd) {
647 // Find the beginning of the next word.
648 WordStart = skipWhitespace(WordStart, Str, Length);
649 if (WordStart == Length)
650 break;
651
652 // Find the end of this word.
653 WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
654
655 // Does this word fit on the current line?
656 unsigned WordLength = WordEnd - WordStart;
657 if (Column + WordLength < Columns) {
658 // This word fits on the current line; print it there.
659 if (WordStart) {
660 OS << ' ';
661 Column += 1;
662 }
663 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
664 TextNormal, Bold);
665 Column += WordLength;
666 continue;
667 }
668
669 // This word does not fit on the current line, so wrap to the next
670 // line.
671 OS << '\n';
672 OS.indent(WordWrapIndentation);
673 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
674 TextNormal, Bold);
675 Column = WordWrapIndentation + WordLength;
676 Wrapped = true;
677 }
678
679 // Append any remaning text from the message with its existing formatting.
680 applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold);
681
682 assert(TextNormal && "Text highlighted at end of diagnostic message.");
683
684 return Wrapped;
685}
686
691
693
696 StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
698 uint64_t StartOfLocationInfo = OS.getColumn();
699
700 // Emit the location of this particular diagnostic.
701 if (Loc.isValid())
702 emitDiagnosticLoc(Loc, PLoc, Level, Ranges);
703
704 if (DiagOpts.ShowColors)
705 OS.resetColor();
706
707 if (DiagOpts.ShowLevel)
708 printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
710 /*IsSupplemental*/ Level == DiagnosticsEngine::Note,
711 Message, OS.getColumn() - StartOfLocationInfo,
712 DiagOpts.MessageLength, DiagOpts.ShowColors);
713 // We use a formatted ostream, which does its own buffering. Flush here
714 // so we keep the proper order of output.
715 OS.flush();
716}
717
718/*static*/ void
721 bool ShowColors) {
722 if (ShowColors) {
723 // Print diagnostic category in bold and color
724 switch (Level) {
726 llvm_unreachable("Invalid diagnostic type");
728 OS.changeColor(NoteColor, true);
729 break;
731 OS.changeColor(RemarkColor, true);
732 break;
734 OS.changeColor(WarningColor, true);
735 break;
737 OS.changeColor(ErrorColor, true);
738 break;
740 OS.changeColor(FatalColor, true);
741 break;
742 }
743 }
744
745 switch (Level) {
747 llvm_unreachable("Invalid diagnostic type");
748 case DiagnosticsEngine::Note: OS << "note: "; break;
749 case DiagnosticsEngine::Remark: OS << "remark: "; break;
750 case DiagnosticsEngine::Warning: OS << "warning: "; break;
751 case DiagnosticsEngine::Error: OS << "error: "; break;
752 case DiagnosticsEngine::Fatal: OS << "fatal error: "; break;
753 }
754
755 if (ShowColors)
756 OS.resetColor();
757}
758
759/*static*/
761 bool IsSupplemental,
762 StringRef Message,
763 unsigned CurrentColumn,
764 unsigned Columns, bool ShowColors) {
765 bool Bold = false;
766 if (ShowColors && !IsSupplemental) {
767 // Print primary diagnostic messages in bold and without color, to visually
768 // indicate the transition from continuation notes and other output.
769 OS.changeColor(SavedColor, true);
770 Bold = true;
771 }
772
773 if (Columns)
774 printWordWrapped(OS, Message, Columns, CurrentColumn, Bold);
775 else {
776 bool Normal = true;
777 applyTemplateHighlighting(OS, Message, Normal, Bold);
778 assert(Normal && "Formatting should have returned to normal");
779 }
780
781 if (ShowColors)
782 OS.resetColor();
783 OS << '\n';
784}
785
786void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) {
787#ifdef _WIN32
788 SmallString<4096> TmpFilename;
789#endif
790 if (DiagOpts.AbsolutePath) {
791 auto File = SM.getFileManager().getOptionalFileRef(Filename);
792 if (File) {
793 // We want to print a simplified absolute path, i. e. without "dots".
794 //
795 // The hardest part here are the paths like "<part1>/<link>/../<part2>".
796 // On Unix-like systems, we cannot just collapse "<link>/..", because
797 // paths are resolved sequentially, and, thereby, the path
798 // "<part1>/<part2>" may point to a different location. That is why
799 // we use FileManager::getCanonicalName(), which expands all indirections
800 // with llvm::sys::fs::real_path() and caches the result.
801 //
802 // On the other hand, it would be better to preserve as much of the
803 // original path as possible, because that helps a user to recognize it.
804 // real_path() expands all links, which sometimes too much. Luckily,
805 // on Windows we can just use llvm::sys::path::remove_dots(), because,
806 // on that system, both aforementioned paths point to the same place.
807#ifdef _WIN32
808 TmpFilename = File->getName();
809 llvm::sys::fs::make_absolute(TmpFilename);
810 llvm::sys::path::native(TmpFilename);
811 llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
812 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
813#else
814 Filename = SM.getFileManager().getCanonicalName(*File);
815#endif
816 }
817 }
818
819 OS << Filename;
820}
821
822/// Print out the file/line/column information and include trace.
823///
824/// This method handles the emission of the diagnostic location information.
825/// This includes extracting as much location information as is present for
826/// the diagnostic and printing it, as well as any include stack or source
827/// ranges necessary.
831 if (PLoc.isInvalid()) {
832 // At least print the file name if available:
833 if (FileID FID = Loc.getFileID(); FID.isValid()) {
834 if (OptionalFileEntryRef FE = Loc.getFileEntryRef()) {
835 emitFilename(FE->getName(), Loc.getManager());
836 OS << ": ";
837 }
838 }
839 return;
840 }
841 unsigned LineNo = PLoc.getLine();
842
843 if (!DiagOpts.ShowLocation)
844 return;
845
846 if (DiagOpts.ShowColors)
847 OS.changeColor(SavedColor, true);
848
849 emitFilename(PLoc.getFilename(), Loc.getManager());
850 switch (DiagOpts.getFormat()) {
853 if (DiagOpts.ShowLine)
854 OS << ':' << LineNo;
855 break;
856 case DiagnosticOptions::MSVC: OS << '(' << LineNo; break;
857 case DiagnosticOptions::Vi: OS << " +" << LineNo; break;
858 }
859
860 if (DiagOpts.ShowColumn)
861 // Compute the column number.
862 if (unsigned ColNo = PLoc.getColumn()) {
863 if (DiagOpts.getFormat() == DiagnosticOptions::MSVC) {
864 OS << ',';
865 // Visual Studio 2010 or earlier expects column number to be off by one
866 if (LangOpts.MSCompatibilityVersion &&
867 !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
868 ColNo--;
869 } else
870 OS << ':';
871 OS << ColNo;
872 }
873 switch (DiagOpts.getFormat()) {
876 case DiagnosticOptions::Vi: OS << ':'; break;
878 // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
879 // space and prints 'file(4): error'.
880 OS << ')';
881 if (LangOpts.MSCompatibilityVersion &&
882 !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
883 OS << ' ';
884 OS << ':';
885 break;
886 }
887
888 if (DiagOpts.ShowSourceRanges && !Ranges.empty()) {
889 FileID CaretFileID = Loc.getExpansionLoc().getFileID();
890 bool PrintedRange = false;
891 const SourceManager &SM = Loc.getManager();
892
893 for (const auto &R : Ranges) {
894 // Ignore invalid ranges.
895 if (!R.isValid())
896 continue;
897
898 SourceLocation B = SM.getExpansionLoc(R.getBegin());
899 CharSourceRange ERange = SM.getExpansionRange(R.getEnd());
900 SourceLocation E = ERange.getEnd();
901
902 // If the start or end of the range is in another file, just
903 // discard it.
904 if (SM.getFileID(B) != CaretFileID || SM.getFileID(E) != CaretFileID)
905 continue;
906
907 // Add in the length of the token, so that we cover multi-char
908 // tokens.
909 unsigned TokSize = 0;
910 if (ERange.isTokenRange())
912
913 FullSourceLoc BF(B, SM), EF(E, SM);
914 OS << '{'
915 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-'
916 << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize)
917 << '}';
918 PrintedRange = true;
919 }
920
921 if (PrintedRange)
922 OS << ':';
923 }
924 OS << ' ';
925}
926
928 if (DiagOpts.ShowLocation && PLoc.isValid()) {
929 OS << "In file included from ";
930 emitFilename(PLoc.getFilename(), Loc.getManager());
931 OS << ':' << PLoc.getLine() << ":\n";
932 } else
933 OS << "In included file:\n";
934}
935
937 StringRef ModuleName) {
938 if (DiagOpts.ShowLocation && PLoc.isValid())
939 OS << "In module '" << ModuleName << "' imported from "
940 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
941 else
942 OS << "In module '" << ModuleName << "':\n";
943}
944
946 PresumedLoc PLoc,
947 StringRef ModuleName) {
948 if (DiagOpts.ShowLocation && PLoc.isValid())
949 OS << "While building module '" << ModuleName << "' imported from "
950 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
951 else
952 OS << "While building module '" << ModuleName << "':\n";
953}
954
955/// Find the suitable set of lines to show to include a set of ranges.
956static std::optional<std::pair<unsigned, unsigned>>
958 const SourceManager &SM) {
959 if (!R.isValid())
960 return std::nullopt;
961
962 SourceLocation Begin = R.getBegin();
963 SourceLocation End = R.getEnd();
964 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID)
965 return std::nullopt;
966
967 return std::make_pair(SM.getExpansionLineNumber(Begin),
968 SM.getExpansionLineNumber(End));
969}
970
971/// Add as much of range B into range A as possible without exceeding a maximum
972/// size of MaxRange. Ranges are inclusive.
973static std::pair<unsigned, unsigned>
974maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
975 unsigned MaxRange) {
976 // If A is already the maximum size, we're done.
977 unsigned Slack = MaxRange - (A.second - A.first + 1);
978 if (Slack == 0)
979 return A;
980
981 // Easy case: merge succeeds within MaxRange.
982 unsigned Min = std::min(A.first, B.first);
983 unsigned Max = std::max(A.second, B.second);
984 if (Max - Min + 1 <= MaxRange)
985 return {Min, Max};
986
987 // If we can't reach B from A within MaxRange, there's nothing to do.
988 // Don't add lines to the range that contain nothing interesting.
989 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
990 (B.second < A.second && A.second - B.second + 1 > MaxRange))
991 return A;
992
993 // Otherwise, expand A towards B to produce a range of size MaxRange. We
994 // attempt to expand by the same amount in both directions if B strictly
995 // contains A.
996
997 // Expand downwards by up to half the available amount, then upwards as
998 // much as possible, then downwards as much as possible.
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);
1003 return A;
1004}
1005
1007 unsigned LineNo;
1009 Bytes EndByte;
1010};
1011
1012/// Highlight \p R (with ~'s) on the current source line.
1013static void highlightRange(const LineRange &R, const SourceColumnMap &Map,
1014 std::string &CaretLine) {
1015 // Pick the first non-whitespace column.
1016 Bytes StartByte = R.StartByte;
1017 while (StartByte < Map.bytes() && (Map.getSourceLine()[StartByte.V] == ' ' ||
1018 Map.getSourceLine()[StartByte.V] == '\t'))
1019 StartByte = Map.startOfNextColumn(StartByte);
1020
1021 // Pick the last non-whitespace column.
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);
1026
1027 // If the start/end passed each other, then we are trying to highlight a
1028 // range that just exists in whitespace. That most likely means we have
1029 // a multi-line highlighting range that covers a blank line.
1030 if (StartByte > EndByte)
1031 return;
1032
1033 assert(StartByte <= EndByte && "Invalid range!");
1034 // Fill the range with ~'s.
1035 Columns StartCol = Map.byteToContainingColumn(StartByte);
1036 Columns EndCol = Map.byteToContainingColumn(EndByte);
1037
1038 if (CaretLine.size() < static_cast<size_t>(EndCol.V))
1039 CaretLine.resize(EndCol.V, ' ');
1040
1041 std::fill(CaretLine.begin() + StartCol.V, CaretLine.begin() + EndCol.V, '~');
1042}
1043
1044static std::string buildFixItInsertionLine(FileID FID, unsigned LineNo,
1045 const SourceColumnMap &map,
1046 ArrayRef<FixItHint> Hints,
1047 const SourceManager &SM,
1048 const DiagnosticOptions &DiagOpts) {
1049 std::string FixItInsertionLine;
1050 if (Hints.empty() || !DiagOpts.ShowFixits)
1051 return FixItInsertionLine;
1052 Columns PrevHintEndCol = 0;
1053
1054 for (const auto &H : Hints) {
1055 if (H.CodeToInsert.empty())
1056 continue;
1057
1058 // We have an insertion hint. Determine whether the inserted
1059 // code contains no newlines and is on the same line as the caret.
1060 FileIDAndOffset HintLocInfo =
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) {
1065 // Insert the new code into the line just below the code
1066 // that the user wrote.
1067 // Note: When modifying this function, be very careful about what is a
1068 // "column" (printed width, platform-dependent) and what is a
1069 // "byte offset" (SourceManager "column").
1070 Bytes HintByteOffset =
1071 Bytes(SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second))
1072 .prev();
1073
1074 // The hint must start inside the source or right at the end
1075 assert(HintByteOffset < map.bytes().next());
1076 Columns HintCol = map.byteToContainingColumn(HintByteOffset);
1077
1078 // If we inserted a long previous hint, push this one forwards, and add
1079 // an extra space to show that this is not part of the previous
1080 // completion. This is sort of the best we can do when two hints appear
1081 // to overlap.
1082 //
1083 // Note that if this hint is located immediately after the previous
1084 // hint, no space will be added, since the location is more important.
1085 if (HintCol < PrevHintEndCol)
1086 HintCol = PrevHintEndCol + 1;
1087
1088 // This should NOT use HintByteOffset, because the source might have
1089 // Unicode characters in earlier columns.
1090 Columns NewFixItLineSize = Columns(FixItInsertionLine.size()) +
1091 (HintCol - PrevHintEndCol) +
1092 Columns(H.CodeToInsert.size());
1093 if (NewFixItLineSize > FixItInsertionLine.size())
1094 FixItInsertionLine.resize(NewFixItLineSize.V, ' ');
1095
1096 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1097 FixItInsertionLine.end() - H.CodeToInsert.size());
1098
1099 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1100 }
1101 }
1102
1103 expandTabs(FixItInsertionLine, DiagOpts.TabStop);
1104
1105 return FixItInsertionLine;
1106}
1107
1108static unsigned getNumDisplayWidth(unsigned N) {
1109 unsigned L = 1u, M = 10u;
1110 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1111 M *= 10u;
1112
1113 return L;
1114}
1115
1116/// Filter out invalid ranges, ranges that don't fit into the window of
1117/// source lines we will print, and ranges from other files.
1118///
1119/// For the remaining ranges, convert them to simple LineRange structs,
1120/// which only cover one line at a time.
1123 const SourceManager &SM,
1124 const std::pair<unsigned, unsigned> &Lines, FileID FID,
1125 const LangOptions &LangOpts) {
1126 SmallVector<LineRange> LineRanges;
1127
1128 for (const CharSourceRange &R : Ranges) {
1129 if (R.isInvalid())
1130 continue;
1131 SourceLocation Begin = R.getBegin();
1132 SourceLocation End = R.getEnd();
1133
1134 unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
1135 if (StartLineNo > Lines.second || SM.getFileID(Begin) != FID)
1136 continue;
1137
1138 unsigned EndLineNo = SM.getExpansionLineNumber(End);
1139 if (EndLineNo < Lines.first || SM.getFileID(End) != FID)
1140 continue;
1141
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())
1147 EndByte += Bytes(Lexer::MeasureTokenLength(End, SM, LangOpts));
1148
1149 // Only a single line.
1150 if (StartLineNo == EndLineNo) {
1151 LineRanges.push_back({StartLineNo, StartByte.prev(), EndByte.prev()});
1152 continue;
1153 }
1154
1155 // Start line.
1156 LineRanges.push_back(
1157 {StartLineNo, StartByte.prev(), std::numeric_limits<int>::max()});
1158
1159 // Middle lines.
1160 for (unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1161 LineRanges.push_back({S, 0, std::numeric_limits<int>::max()});
1162
1163 // End line.
1164 LineRanges.push_back({EndLineNo, 0, EndByte.prev()});
1165 }
1166
1167 return LineRanges;
1168}
1169
1170/// Creates syntax highlighting information in form of StyleRanges.
1171///
1172/// The returned unique ptr has always exactly size
1173/// (\p EndLineNumber - \p StartLineNumber + 1). Each SmallVector in there
1174/// corresponds to syntax highlighting information in one line. In each line,
1175/// the StyleRanges are non-overlapping and sorted from start to end of the
1176/// line.
1177static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]>
1178highlightLines(StringRef FileData, unsigned StartLineNumber,
1179 unsigned EndLineNumber, const Preprocessor *PP,
1180 const LangOptions &LangOpts, bool ShowColors, FileID FID,
1181 const SourceManager &SM) {
1182 assert(StartLineNumber <= EndLineNumber);
1183 auto SnippetRanges =
1184 std::make_unique<SmallVector<TextDiagnostic::StyleRange>[]>(
1185 EndLineNumber - StartLineNumber + 1);
1186
1187 if (!PP || !ShowColors)
1188 return SnippetRanges;
1189
1190 // Might cause emission of another diagnostic.
1192 return SnippetRanges;
1193
1194 auto Buff = llvm::MemoryBuffer::getMemBuffer(FileData);
1195 Lexer L{FID, *Buff, SM, LangOpts};
1196 L.SetKeepWhitespaceMode(true);
1197
1198 const char *FirstLineStart =
1199 FileData.data() +
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, /*IsAtStartOfLine=*/false);
1207 }
1208
1209 // Classify the given token and append it to the given vector.
1210 auto appendStyle =
1211 [PP, &LangOpts](SmallVector<TextDiagnostic::StyleRange> &Vec,
1212 const Token &T, unsigned Start, unsigned Length) -> void {
1213 if (T.is(tok::raw_identifier)) {
1214 StringRef RawIdent = T.getRawIdentifier();
1215 // Special case true/false/nullptr/... literals, since they will otherwise
1216 // be treated as keywords.
1217 // FIXME: It would be good to have a programmatic way of getting this
1218 // list.
1219 if (llvm::StringSwitch<bool>(RawIdent)
1220 .Case("true", true)
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)
1231 .Default(false)) {
1232 Vec.emplace_back(Start, Start + Length, LiteralColor);
1233 } else {
1234 const IdentifierInfo *II = PP->getIdentifierInfo(RawIdent);
1235 assert(II);
1236 if (II->isKeyword(LangOpts))
1237 Vec.emplace_back(Start, Start + Length, KeywordColor);
1238 }
1239 } else if (tok::isLiteral(T.getKind())) {
1240 Vec.emplace_back(Start, Start + Length, LiteralColor);
1241 } else {
1242 assert(T.is(tok::comment));
1243 Vec.emplace_back(Start, Start + Length, CommentColor);
1244 }
1245 };
1246
1247 bool Stop = false;
1248 while (!Stop) {
1249 Token T;
1250 Stop = L.LexFromRawLexer(T);
1251 if (T.is(tok::unknown))
1252 continue;
1253
1254 // We are only interested in identifiers, literals and comments.
1255 if (!T.is(tok::raw_identifier) && !T.is(tok::comment) &&
1256 !tok::isLiteral(T.getKind()))
1257 continue;
1258
1259 bool Invalid = false;
1260 unsigned TokenEndLine = SM.getSpellingLineNumber(T.getEndLoc(), &Invalid);
1261 if (Invalid || TokenEndLine < StartLineNumber)
1262 continue;
1263
1264 assert(TokenEndLine >= StartLineNumber);
1265
1266 unsigned TokenStartLine =
1267 SM.getSpellingLineNumber(T.getLocation(), &Invalid);
1268 if (Invalid)
1269 continue;
1270 // If this happens, we're done.
1271 if (TokenStartLine > EndLineNumber)
1272 break;
1273
1274 Bytes StartCol = SM.getSpellingColumnNumber(T.getLocation(), &Invalid) - 1;
1275 if (Invalid)
1276 continue;
1277
1278 // Simple tokens.
1279 if (TokenStartLine == TokenEndLine) {
1281 SnippetRanges[TokenStartLine - StartLineNumber];
1282 appendStyle(LineRanges, T, StartCol.V, T.getLength());
1283 continue;
1284 }
1285 assert((TokenEndLine - TokenStartLine) >= 1);
1286
1287 // For tokens that span multiple lines (think multiline comments), we
1288 // divide them into multiple StyleRanges.
1289 Bytes EndCol = SM.getSpellingColumnNumber(T.getEndLoc(), &Invalid) - 1;
1290 if (Invalid)
1291 continue;
1292
1293 std::string Spelling = Lexer::getSpelling(T, SM, LangOpts);
1294
1295 unsigned L = TokenStartLine;
1296 unsigned LineLength = 0;
1297 for (unsigned I = 0; I <= Spelling.size(); ++I) {
1298 // This line is done.
1299 if (I == Spelling.size() || isVerticalWhitespace(Spelling[I])) {
1300 if (L >= StartLineNumber) {
1302 SnippetRanges[L - StartLineNumber];
1303
1304 if (L == TokenStartLine) // First line
1305 appendStyle(LineRanges, T, StartCol.V, LineLength);
1306 else if (L == TokenEndLine) // Last line
1307 appendStyle(LineRanges, T, 0, EndCol.V);
1308 else
1309 appendStyle(LineRanges, T, 0, LineLength);
1310 }
1311
1312 ++L;
1313 if (L > EndLineNumber)
1314 break;
1315 LineLength = 0;
1316 continue;
1317 }
1318 ++LineLength;
1319 }
1320 }
1321
1322 return SnippetRanges;
1323}
1324
1325/// Emit a code snippet and caret line.
1326///
1327/// This routine emits a single line's code snippet and caret line..
1328///
1329/// \param Loc The location for the caret.
1330/// \param Ranges The underlined ranges for this code snippet.
1331/// \param Hints The FixIt hints active for this diagnostic.
1332void TextDiagnostic::emitSnippetAndCaret(
1335 assert(Loc.isValid() && "must have a valid source location here");
1336 assert(Loc.isFileID() && "must have a file location here");
1337
1338 // If caret diagnostics are enabled and we have location, we want to
1339 // emit the caret. However, we only do this if the location moved
1340 // from the last diagnostic, if the last diagnostic was a note that
1341 // was part of a different warning or error diagnostic, or if the
1342 // diagnostic has ranges. We don't want to emit the same caret
1343 // multiple times if one loc has multiple diagnostics.
1344 if (!DiagOpts.ShowCarets)
1345 return;
1346 if (Loc == LastLoc && Ranges.empty() && Hints.empty() &&
1348 return;
1349
1350 FileID FID = Loc.getFileID();
1351 const SourceManager &SM = Loc.getManager();
1352
1353 // Get information about the buffer it points into.
1354 bool Invalid = false;
1355 StringRef BufData = Loc.getBufferData(&Invalid);
1356 if (Invalid)
1357 return;
1358 const char *BufStart = BufData.data();
1359 const char *BufEnd = BufStart + BufData.size();
1360
1361 unsigned CaretLineNo = Loc.getLineNumber();
1362 Bytes CaretByte = Loc.getColumnNumber();
1363
1364 // Arbitrarily stop showing snippets when the line is too long.
1365 static const size_t MaxLineLengthToPrint = 4096;
1366 if (CaretByte > MaxLineLengthToPrint)
1367 return;
1368
1369 // Find the set of lines to include.
1370 const unsigned MaxLines = DiagOpts.SnippetLineLimit;
1371 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1372 unsigned DisplayLineNo = Loc.getPresumedLoc().getLine();
1373 for (const auto &I : Ranges) {
1374 if (auto OptionalRange = findLinesForRange(I, FID, SM))
1375 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines);
1376
1377 DisplayLineNo =
1378 std::min(DisplayLineNo, SM.getPresumedLineNumber(I.getBegin()));
1379 }
1380
1381 // Our line numbers look like:
1382 // " [number] | "
1383 // Where [number] is MaxLineNoDisplayWidth columns
1384 // and the full thing is therefore MaxLineNoDisplayWidth + 4 columns.
1385 unsigned MaxLineNoDisplayWidth =
1386 DiagOpts.ShowLineNumbers
1387 ? std::max(4u, getNumDisplayWidth(DisplayLineNo + MaxLines))
1388 : 0;
1389 auto indentForLineNumbers = [&] {
1390 if (MaxLineNoDisplayWidth > 0)
1391 OS.indent(MaxLineNoDisplayWidth + 2) << "| ";
1392 };
1393
1394 // Prepare source highlighting information for the lines we're about to
1395 // emit, starting from the first line.
1396 std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
1397 highlightLines(BufData, Lines.first, Lines.second, PP, LangOpts,
1398 DiagOpts.ShowColors, FID, SM);
1399
1400 SmallVector<LineRange> LineRanges =
1401 prepareAndFilterRanges(Ranges, SM, Lines, FID, LangOpts);
1402
1403 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1404 ++LineNo, ++DisplayLineNo) {
1405 // Rewind from the current position to the start of the line.
1406 const char *LineStart =
1407 BufStart +
1408 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second;
1409 if (LineStart == BufEnd)
1410 break;
1411
1412 // Compute the line end.
1413 const char *LineEnd = LineStart;
1414 while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd)
1415 ++LineEnd;
1416
1417 // Arbitrarily stop showing snippets when the line is too long.
1418 // FIXME: Don't print any lines in this case.
1419 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1420 return;
1421
1422 // Copy the line of code into an std::string for ease of manipulation.
1423 std::string SourceLine(LineStart, LineEnd);
1424 // Remove trailing null bytes.
1425 while (!SourceLine.empty() && SourceLine.back() == '\0' &&
1426 (LineNo != CaretLineNo ||
1427 SourceLine.size() > static_cast<size_t>(CaretByte.V)))
1428 SourceLine.pop_back();
1429
1430 // Build the byte to column map.
1431 const SourceColumnMap SourceColMap(SourceLine, DiagOpts.TabStop);
1432
1433 std::string CaretLine;
1434 // Highlight all of the characters covered by Ranges with ~ characters.
1435 for (const auto &LR : LineRanges) {
1436 if (LR.LineNo == LineNo)
1437 highlightRange(LR, SourceColMap, CaretLine);
1438 }
1439
1440 // Next, insert the caret itself.
1441 if (CaretLineNo == LineNo) {
1442 Columns Col = SourceColMap.byteToContainingColumn(CaretByte.prev());
1443 CaretLine.resize(
1444 std::max(static_cast<size_t>(Col.V) + 1, CaretLine.size()), ' ');
1445 CaretLine[Col.V] = '^';
1446 }
1447
1448 std::string FixItInsertionLine =
1449 buildFixItInsertionLine(FID, LineNo, SourceColMap, Hints, SM, DiagOpts);
1450
1451 // If the source line is too long for our terminal, select only the
1452 // "interesting" source region within that line.
1453 Columns MessageLength = DiagOpts.MessageLength;
1454 if (MessageLength.V != 0)
1455 selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
1456 MessageLength, SourceColMap);
1457
1458 // If we are in -fdiagnostics-print-source-range-info mode, we are trying
1459 // to produce easily machine parsable output. Add a space before the
1460 // source line and the caret to make it trivial to tell the main diagnostic
1461 // line from what the user is intended to see.
1462 if (DiagOpts.ShowSourceRanges && !SourceLine.empty()) {
1463 SourceLine = ' ' + SourceLine;
1464 CaretLine = ' ' + CaretLine;
1465 }
1466
1467 // Emit what we have computed.
1468 emitSnippet(SourceLine, MaxLineNoDisplayWidth, LineNo, DisplayLineNo,
1469 SourceStyles[LineNo - Lines.first]);
1470
1471 if (!CaretLine.empty()) {
1472 indentForLineNumbers();
1473 if (DiagOpts.ShowColors)
1474 OS.changeColor(CaretColor, true);
1475 OS << CaretLine << '\n';
1476 if (DiagOpts.ShowColors)
1477 OS.resetColor();
1478 }
1479
1480 if (!FixItInsertionLine.empty()) {
1481 indentForLineNumbers();
1482 if (DiagOpts.ShowColors)
1483 // Print fixit line in color
1484 OS.changeColor(FixitColor, false);
1485 if (DiagOpts.ShowSourceRanges)
1486 OS << ' ';
1487 OS << FixItInsertionLine << '\n';
1488 if (DiagOpts.ShowColors)
1489 OS.resetColor();
1490 }
1491 }
1492
1493 // Print out any parseable fixit information requested by the options.
1494 emitParseableFixits(Hints, SM);
1495}
1496
1497void TextDiagnostic::emitSnippet(StringRef SourceLine,
1498 unsigned MaxLineNoDisplayWidth,
1499 unsigned LineNo, unsigned DisplayLineNo,
1500 ArrayRef<StyleRange> Styles) {
1501 // Emit line number.
1502 if (MaxLineNoDisplayWidth > 0) {
1503 unsigned LineNoDisplayWidth = getNumDisplayWidth(DisplayLineNo);
1504 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1505 << DisplayLineNo << " | ";
1506 }
1507
1508 // Print the source line one character at a time.
1509 bool PrintReversed = false;
1510 std::optional<llvm::raw_ostream::Colors> CurrentColor;
1511 size_t I = 0;
1512 while (I < SourceLine.size()) {
1513 auto [Str, WasPrintable] =
1514 printableTextForNextCharacter(SourceLine, &I, DiagOpts.TabStop);
1515
1516 // Toggle inverted colors on or off for this character.
1517 if (DiagOpts.ShowColors) {
1518 if (WasPrintable == PrintReversed) {
1519 PrintReversed = !PrintReversed;
1520 if (PrintReversed)
1521 OS.reverseColor();
1522 else {
1523 OS.resetColor();
1524 CurrentColor = std::nullopt;
1525 }
1526 }
1527
1528 // Apply syntax highlighting information.
1529 const auto *CharStyle = llvm::find_if(Styles, [I](const StyleRange &R) {
1530 return (R.Start < I && R.End >= I);
1531 });
1532
1533 if (CharStyle != Styles.end()) {
1534 if (!CurrentColor ||
1535 (CurrentColor && *CurrentColor != CharStyle->Color)) {
1536 OS.changeColor(CharStyle->Color);
1537 CurrentColor = CharStyle->Color;
1538 }
1539 } else if (CurrentColor) {
1540 OS.resetColor();
1541 CurrentColor = std::nullopt;
1542 }
1543 }
1544
1545 OS << Str;
1546 }
1547
1548 if (DiagOpts.ShowColors)
1549 OS.resetColor();
1550
1551 OS << '\n';
1552}
1553
1554void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints,
1555 const SourceManager &SM) {
1556 if (!DiagOpts.ShowParseableFixits)
1557 return;
1558
1559 // We follow FixItRewriter's example in not (yet) handling
1560 // fix-its in macros.
1561 for (const auto &H : Hints) {
1562 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1563 H.RemoveRange.getEnd().isMacroID())
1564 return;
1565 }
1566
1567 for (const auto &H : Hints) {
1568 SourceLocation BLoc = H.RemoveRange.getBegin();
1569 SourceLocation ELoc = H.RemoveRange.getEnd();
1570
1571 FileIDAndOffset BInfo = SM.getDecomposedLoc(BLoc);
1572 FileIDAndOffset EInfo = SM.getDecomposedLoc(ELoc);
1573
1574 // Adjust for token ranges.
1575 if (H.RemoveRange.isTokenRange())
1576 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
1577
1578 // We specifically do not do word-wrapping or tab-expansion here,
1579 // because this is supposed to be easy to parse.
1580 PresumedLoc PLoc = SM.getPresumedLoc(BLoc);
1581 if (PLoc.isInvalid())
1582 break;
1583
1584 OS << "fix-it:\"";
1585 OS.write_escaped(PLoc.getFilename());
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)
1590 << "}:\"";
1591 OS.write_escaped(H.CodeToInsert);
1592 OS << "\"\n";
1593 }
1594}
#define V(N, I)
static StringRef bytes(const std::vector< T, Allocator > &v)
static size_t getNumDisplayWidth(size_t N)
Definition Disasm.cpp:133
Defines the clang::FileManager interface and associated types.
static SmallVectorImpl< char > & operator+=(SmallVectorImpl< char > &Includes, StringRef RHS)
#define SM(sm)
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.
Definition Diagnostic.h:237
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isValid() const
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.
Definition Lexer.h:78
void SetKeepWhitespaceMode(bool Val)
SetKeepWhitespaceMode - This method lets clients enable or disable whitespace retention mode.
Definition Lexer.h:254
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition Lexer.h:236
void seek(unsigned Offset, bool IsAtStartOfLine)
Set the lexer's buffer pointer to Offset.
Definition Lexer.cpp:277
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,...
Definition Lexer.cpp:451
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 ...
Definition Lexer.cpp:498
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.
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.
Definition Token.h:36
bool isLiteral(TokenKind K)
Return true if this is a "literal" kind, like a numeric constant, string, etc.
Definition TokenKinds.h:97
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'.
Definition CharInfo.h:99
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
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',...
Definition CharInfo.h:108
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.