clang 18.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"
15#include "llvm/ADT/SmallString.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 "llvm/Support/Path.h"
21#include "llvm/Support/raw_ostream.h"
22#include <algorithm>
23#include <optional>
24
25using namespace clang;
26
27static const enum raw_ostream::Colors noteColor =
28 raw_ostream::BLACK;
29static const enum raw_ostream::Colors remarkColor =
30 raw_ostream::BLUE;
31static const enum raw_ostream::Colors fixitColor =
32 raw_ostream::GREEN;
33static const enum raw_ostream::Colors caretColor =
34 raw_ostream::GREEN;
35static const enum raw_ostream::Colors warningColor =
36 raw_ostream::MAGENTA;
37static const enum raw_ostream::Colors templateColor =
38 raw_ostream::CYAN;
39static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
40static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
41// Used for changing only the bold attribute.
42static const enum raw_ostream::Colors savedColor =
43 raw_ostream::SAVEDCOLOR;
44
45/// Add highlights to differences in template strings.
46static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
47 bool &Normal, bool Bold) {
48 while (true) {
49 size_t Pos = Str.find(ToggleHighlight);
50 OS << Str.slice(0, Pos);
51 if (Pos == StringRef::npos)
52 break;
53
54 Str = Str.substr(Pos + 1);
55 if (Normal)
56 OS.changeColor(templateColor, true);
57 else {
58 OS.resetColor();
59 if (Bold)
60 OS.changeColor(savedColor, true);
61 }
62 Normal = !Normal;
63 }
64}
65
66/// Number of spaces to indent when word-wrapping.
67const unsigned WordWrapIndentation = 6;
68
69static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
70 int bytes = 0;
71 while (0<i) {
72 if (SourceLine[--i]=='\t')
73 break;
74 ++bytes;
75 }
76 return bytes;
77}
78
79/// returns a printable representation of first item from input range
80///
81/// This function returns a printable representation of the next item in a line
82/// of source. If the next byte begins a valid and printable character, that
83/// character is returned along with 'true'.
84///
85/// Otherwise, if the next byte begins a valid, but unprintable character, a
86/// printable, escaped representation of the character is returned, along with
87/// 'false'. Otherwise a printable, escaped representation of the next byte
88/// is returned along with 'false'.
89///
90/// \note The index is updated to be used with a subsequent call to
91/// printableTextForNextCharacter.
92///
93/// \param SourceLine The line of source
94/// \param I Pointer to byte index,
95/// \param TabStop used to expand tabs
96/// \return pair(printable text, 'true' iff original text was printable)
97///
98static std::pair<SmallString<16>, bool>
99printableTextForNextCharacter(StringRef SourceLine, size_t *I,
100 unsigned TabStop) {
101 assert(I && "I must not be null");
102 assert(*I < SourceLine.size() && "must point to a valid index");
103
104 if (SourceLine[*I] == '\t') {
105 assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
106 "Invalid -ftabstop value");
107 unsigned Col = bytesSincePreviousTabOrLineBegin(SourceLine, *I);
108 unsigned NumSpaces = TabStop - (Col % TabStop);
109 assert(0 < NumSpaces && NumSpaces <= TabStop
110 && "Invalid computation of space amt");
111 ++(*I);
112
113 SmallString<16> ExpandedTab;
114 ExpandedTab.assign(NumSpaces, ' ');
115 return std::make_pair(ExpandedTab, true);
116 }
117
118 const unsigned char *Begin = SourceLine.bytes_begin() + *I;
119
120 // Fast path for the common ASCII case.
121 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) {
122 ++(*I);
123 return std::make_pair(SmallString<16>(Begin, Begin + 1), true);
124 }
125 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin);
126 const unsigned char *End = Begin + CharSize;
127
128 // Convert it to UTF32 and check if it's printable.
129 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) {
130 llvm::UTF32 C;
131 llvm::UTF32 *CPtr = &C;
132
133 // Begin and end before conversion.
134 unsigned char const *OriginalBegin = Begin;
135 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
136 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);
137 (void)Res;
138 assert(Res == llvm::conversionOK);
139 assert(OriginalBegin < Begin);
140 assert((Begin - OriginalBegin) == CharSize);
141
142 (*I) += (Begin - OriginalBegin);
143
144 // Valid, multi-byte, printable UTF8 character.
145 if (llvm::sys::locale::isPrint(C))
146 return std::make_pair(SmallString<16>(OriginalBegin, End), true);
147
148 // Valid but not printable.
149 SmallString<16> Str("<U+>");
150 while (C) {
151 Str.insert(Str.begin() + 3, llvm::hexdigit(C % 16));
152 C /= 16;
153 }
154 while (Str.size() < 8)
155 Str.insert(Str.begin() + 3, llvm::hexdigit(0));
156 return std::make_pair(Str, false);
157 }
158
159 // Otherwise, not printable since it's not valid UTF8.
160 SmallString<16> ExpandedByte("<XX>");
161 unsigned char Byte = SourceLine[*I];
162 ExpandedByte[1] = llvm::hexdigit(Byte / 16);
163 ExpandedByte[2] = llvm::hexdigit(Byte % 16);
164 ++(*I);
165 return std::make_pair(ExpandedByte, false);
166}
167
168static void expandTabs(std::string &SourceLine, unsigned TabStop) {
169 size_t I = SourceLine.size();
170 while (I > 0) {
171 I--;
172 if (SourceLine[I] != '\t')
173 continue;
174 size_t TmpI = I;
175 auto [Str, Printable] =
176 printableTextForNextCharacter(SourceLine, &TmpI, TabStop);
177 SourceLine.replace(I, 1, Str.c_str());
178 }
179}
180
181/// \p BytesOut:
182/// A mapping from columns to the byte of the source line that produced the
183/// character displaying at that column. This is the inverse of \p ColumnsOut.
184///
185/// The last element in the array is the number of bytes in the source string.
186///
187/// example: (given a tabstop of 8)
188///
189/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
190///
191/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
192/// display)
193///
194/// \p ColumnsOut:
195/// A mapping from the bytes
196/// of the printable representation of the line to the columns those printable
197/// characters will appear at (numbering the first column as 0).
198///
199/// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab
200/// character) then the array will map that byte to the first column the
201/// tab appears at and the next value in the map will have been incremented
202/// more than once.
203///
204/// If a byte is the first in a sequence of bytes that together map to a single
205/// entity in the output, then the array will map that byte to the appropriate
206/// column while the subsequent bytes will be -1.
207///
208/// The last element in the array does not correspond to any byte in the input
209/// and instead is the number of columns needed to display the source
210///
211/// example: (given a tabstop of 8)
212///
213/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
214///
215/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
216/// display)
217static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop,
218 SmallVectorImpl<int> &BytesOut,
219 SmallVectorImpl<int> &ColumnsOut) {
220 assert(BytesOut.empty());
221 assert(ColumnsOut.empty());
222
223 if (SourceLine.empty()) {
224 BytesOut.resize(1u, 0);
225 ColumnsOut.resize(1u, 0);
226 return;
227 }
228
229 ColumnsOut.resize(SourceLine.size() + 1, -1);
230
231 int Columns = 0;
232 size_t I = 0;
233 while (I < SourceLine.size()) {
234 ColumnsOut[I] = Columns;
235 BytesOut.resize(Columns + 1, -1);
236 BytesOut.back() = I;
237 auto [Str, Printable] =
238 printableTextForNextCharacter(SourceLine, &I, TabStop);
239 Columns += llvm::sys::locale::columnWidth(Str);
240 }
241
242 ColumnsOut.back() = Columns;
243 BytesOut.resize(Columns + 1, -1);
244 BytesOut.back() = I;
245}
246
247namespace {
248struct SourceColumnMap {
249 SourceColumnMap(StringRef SourceLine, unsigned TabStop)
250 : m_SourceLine(SourceLine) {
251
252 genColumnByteMapping(SourceLine, TabStop, m_columnToByte, m_byteToColumn);
253
254 assert(m_byteToColumn.size()==SourceLine.size()+1);
255 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size());
256 assert(m_byteToColumn.size()
257 == static_cast<unsigned>(m_columnToByte.back()+1));
258 assert(static_cast<unsigned>(m_byteToColumn.back()+1)
259 == m_columnToByte.size());
260 }
261 int columns() const { return m_byteToColumn.back(); }
262 int bytes() const { return m_columnToByte.back(); }
263
264 /// Map a byte to the column which it is at the start of, or return -1
265 /// if it is not at the start of a column (for a UTF-8 trailing byte).
266 int byteToColumn(int n) const {
267 assert(0<=n && n<static_cast<int>(m_byteToColumn.size()));
268 return m_byteToColumn[n];
269 }
270
271 /// Map a byte to the first column which contains it.
272 int byteToContainingColumn(int N) const {
273 assert(0 <= N && N < static_cast<int>(m_byteToColumn.size()));
274 while (m_byteToColumn[N] == -1)
275 --N;
276 return m_byteToColumn[N];
277 }
278
279 /// Map a column to the byte which starts the column, or return -1 if
280 /// the column the second or subsequent column of an expanded tab or similar
281 /// multi-column entity.
282 int columnToByte(int n) const {
283 assert(0<=n && n<static_cast<int>(m_columnToByte.size()));
284 return m_columnToByte[n];
285 }
286
287 /// Map from a byte index to the next byte which starts a column.
288 int startOfNextColumn(int N) const {
289 assert(0 <= N && N < static_cast<int>(m_byteToColumn.size() - 1));
290 while (byteToColumn(++N) == -1) {}
291 return N;
292 }
293
294 /// Map from a byte index to the previous byte which starts a column.
295 int startOfPreviousColumn(int N) const {
296 assert(0 < N && N < static_cast<int>(m_byteToColumn.size()));
297 while (byteToColumn(--N) == -1) {}
298 return N;
299 }
300
301 StringRef getSourceLine() const {
302 return m_SourceLine;
303 }
304
305private:
306 const std::string m_SourceLine;
307 SmallVector<int,200> m_byteToColumn;
308 SmallVector<int,200> m_columnToByte;
309};
310} // end anonymous namespace
311
312/// When the source code line we want to print is too long for
313/// the terminal, select the "interesting" region.
314static void selectInterestingSourceRegion(std::string &SourceLine,
315 std::string &CaretLine,
316 std::string &FixItInsertionLine,
317 unsigned Columns,
318 const SourceColumnMap &map) {
319 unsigned CaretColumns = CaretLine.size();
320 unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);
321 unsigned MaxColumns = std::max(static_cast<unsigned>(map.columns()),
322 std::max(CaretColumns, FixItColumns));
323 // if the number of columns is less than the desired number we're done
324 if (MaxColumns <= Columns)
325 return;
326
327 // No special characters are allowed in CaretLine.
328 assert(llvm::none_of(CaretLine, [](char c) { return c < ' ' || '~' < c; }));
329
330 // Find the slice that we need to display the full caret line
331 // correctly.
332 unsigned CaretStart = 0, CaretEnd = CaretLine.size();
333 for (; CaretStart != CaretEnd; ++CaretStart)
334 if (!isWhitespace(CaretLine[CaretStart]))
335 break;
336
337 for (; CaretEnd != CaretStart; --CaretEnd)
338 if (!isWhitespace(CaretLine[CaretEnd - 1]))
339 break;
340
341 // caret has already been inserted into CaretLine so the above whitespace
342 // check is guaranteed to include the caret
343
344 // If we have a fix-it line, make sure the slice includes all of the
345 // fix-it information.
346 if (!FixItInsertionLine.empty()) {
347 unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
348 for (; FixItStart != FixItEnd; ++FixItStart)
349 if (!isWhitespace(FixItInsertionLine[FixItStart]))
350 break;
351
352 for (; FixItEnd != FixItStart; --FixItEnd)
353 if (!isWhitespace(FixItInsertionLine[FixItEnd - 1]))
354 break;
355
356 // We can safely use the byte offset FixItStart as the column offset
357 // because the characters up until FixItStart are all ASCII whitespace
358 // characters.
359 unsigned FixItStartCol = FixItStart;
360 unsigned FixItEndCol
361 = llvm::sys::locale::columnWidth(FixItInsertionLine.substr(0, FixItEnd));
362
363 CaretStart = std::min(FixItStartCol, CaretStart);
364 CaretEnd = std::max(FixItEndCol, CaretEnd);
365 }
366
367 // CaretEnd may have been set at the middle of a character
368 // If it's not at a character's first column then advance it past the current
369 // character.
370 while (static_cast<int>(CaretEnd) < map.columns() &&
371 -1 == map.columnToByte(CaretEnd))
372 ++CaretEnd;
373
374 assert((static_cast<int>(CaretStart) > map.columns() ||
375 -1!=map.columnToByte(CaretStart)) &&
376 "CaretStart must not point to a column in the middle of a source"
377 " line character");
378 assert((static_cast<int>(CaretEnd) > map.columns() ||
379 -1!=map.columnToByte(CaretEnd)) &&
380 "CaretEnd must not point to a column in the middle of a source line"
381 " character");
382
383 // CaretLine[CaretStart, CaretEnd) contains all of the interesting
384 // parts of the caret line. While this slice is smaller than the
385 // number of columns we have, try to grow the slice to encompass
386 // more context.
387
388 unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart,
389 map.columns()));
390 unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd,
391 map.columns()));
392
393 unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart
394 - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart));
395
396 char const *front_ellipse = " ...";
397 char const *front_space = " ";
398 char const *back_ellipse = "...";
399 unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse);
400
401 unsigned TargetColumns = Columns;
402 // Give us extra room for the ellipses
403 // and any of the caret line that extends past the source
404 if (TargetColumns > ellipses_space+CaretColumnsOutsideSource)
405 TargetColumns -= ellipses_space+CaretColumnsOutsideSource;
406
407 while (SourceStart>0 || SourceEnd<SourceLine.size()) {
408 bool ExpandedRegion = false;
409
410 if (SourceStart>0) {
411 unsigned NewStart = map.startOfPreviousColumn(SourceStart);
412
413 // Skip over any whitespace we see here; we're looking for
414 // another bit of interesting text.
415 // FIXME: Detect non-ASCII whitespace characters too.
416 while (NewStart && isWhitespace(SourceLine[NewStart]))
417 NewStart = map.startOfPreviousColumn(NewStart);
418
419 // Skip over this bit of "interesting" text.
420 while (NewStart) {
421 unsigned Prev = map.startOfPreviousColumn(NewStart);
422 if (isWhitespace(SourceLine[Prev]))
423 break;
424 NewStart = Prev;
425 }
426
427 assert(map.byteToColumn(NewStart) != -1);
428 unsigned NewColumns = map.byteToColumn(SourceEnd) -
429 map.byteToColumn(NewStart);
430 if (NewColumns <= TargetColumns) {
431 SourceStart = NewStart;
432 ExpandedRegion = true;
433 }
434 }
435
436 if (SourceEnd<SourceLine.size()) {
437 unsigned NewEnd = map.startOfNextColumn(SourceEnd);
438
439 // Skip over any whitespace we see here; we're looking for
440 // another bit of interesting text.
441 // FIXME: Detect non-ASCII whitespace characters too.
442 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd]))
443 NewEnd = map.startOfNextColumn(NewEnd);
444
445 // Skip over this bit of "interesting" text.
446 while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd]))
447 NewEnd = map.startOfNextColumn(NewEnd);
448
449 assert(map.byteToColumn(NewEnd) != -1);
450 unsigned NewColumns = map.byteToColumn(NewEnd) -
451 map.byteToColumn(SourceStart);
452 if (NewColumns <= TargetColumns) {
453 SourceEnd = NewEnd;
454 ExpandedRegion = true;
455 }
456 }
457
458 if (!ExpandedRegion)
459 break;
460 }
461
462 CaretStart = map.byteToColumn(SourceStart);
463 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
464
465 // [CaretStart, CaretEnd) is the slice we want. Update the various
466 // output lines to show only this slice.
467 assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 &&
468 SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1);
469 assert(SourceStart <= SourceEnd);
470 assert(CaretStart <= CaretEnd);
471
472 unsigned BackColumnsRemoved
473 = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd);
474 unsigned FrontColumnsRemoved = CaretStart;
475 unsigned ColumnsKept = CaretEnd-CaretStart;
476
477 // We checked up front that the line needed truncation
478 assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns);
479
480 // The line needs some truncation, and we'd prefer to keep the front
481 // if possible, so remove the back
482 if (BackColumnsRemoved > strlen(back_ellipse))
483 SourceLine.replace(SourceEnd, std::string::npos, back_ellipse);
484
485 // If that's enough then we're done
486 if (FrontColumnsRemoved+ColumnsKept <= Columns)
487 return;
488
489 // Otherwise remove the front as well
490 if (FrontColumnsRemoved > strlen(front_ellipse)) {
491 SourceLine.replace(0, SourceStart, front_ellipse);
492 CaretLine.replace(0, CaretStart, front_space);
493 if (!FixItInsertionLine.empty())
494 FixItInsertionLine.replace(0, CaretStart, front_space);
495 }
496}
497
498/// Skip over whitespace in the string, starting at the given
499/// index.
500///
501/// \returns The index of the first non-whitespace character that is
502/// greater than or equal to Idx or, if no such character exists,
503/// returns the end of the string.
504static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) {
505 while (Idx < Length && isWhitespace(Str[Idx]))
506 ++Idx;
507 return Idx;
508}
509
510/// If the given character is the start of some kind of
511/// balanced punctuation (e.g., quotes or parentheses), return the
512/// character that will terminate the punctuation.
513///
514/// \returns The ending punctuation character, if any, or the NULL
515/// character if the input character does not start any punctuation.
516static inline char findMatchingPunctuation(char c) {
517 switch (c) {
518 case '\'': return '\'';
519 case '`': return '\'';
520 case '"': return '"';
521 case '(': return ')';
522 case '[': return ']';
523 case '{': return '}';
524 default: break;
525 }
526
527 return 0;
528}
529
530/// Find the end of the word starting at the given offset
531/// within a string.
532///
533/// \returns the index pointing one character past the end of the
534/// word.
535static unsigned findEndOfWord(unsigned Start, StringRef Str,
536 unsigned Length, unsigned Column,
537 unsigned Columns) {
538 assert(Start < Str.size() && "Invalid start position!");
539 unsigned End = Start + 1;
540
541 // If we are already at the end of the string, take that as the word.
542 if (End == Str.size())
543 return End;
544
545 // Determine if the start of the string is actually opening
546 // punctuation, e.g., a quote or parentheses.
547 char EndPunct = findMatchingPunctuation(Str[Start]);
548 if (!EndPunct) {
549 // This is a normal word. Just find the first space character.
550 while (End < Length && !isWhitespace(Str[End]))
551 ++End;
552 return End;
553 }
554
555 // We have the start of a balanced punctuation sequence (quotes,
556 // parentheses, etc.). Determine the full sequence is.
557 SmallString<16> PunctuationEndStack;
558 PunctuationEndStack.push_back(EndPunct);
559 while (End < Length && !PunctuationEndStack.empty()) {
560 if (Str[End] == PunctuationEndStack.back())
561 PunctuationEndStack.pop_back();
562 else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
563 PunctuationEndStack.push_back(SubEndPunct);
564
565 ++End;
566 }
567
568 // Find the first space character after the punctuation ended.
569 while (End < Length && !isWhitespace(Str[End]))
570 ++End;
571
572 unsigned PunctWordLength = End - Start;
573 if (// If the word fits on this line
574 Column + PunctWordLength <= Columns ||
575 // ... or the word is "short enough" to take up the next line
576 // without too much ugly white space
577 PunctWordLength < Columns/3)
578 return End; // Take the whole thing as a single "word".
579
580 // The whole quoted/parenthesized string is too long to print as a
581 // single "word". Instead, find the "word" that starts just after
582 // the punctuation and use that end-point instead. This will recurse
583 // until it finds something small enough to consider a word.
584 return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
585}
586
587/// Print the given string to a stream, word-wrapping it to
588/// some number of columns in the process.
589///
590/// \param OS the stream to which the word-wrapping string will be
591/// emitted.
592/// \param Str the string to word-wrap and output.
593/// \param Columns the number of columns to word-wrap to.
594/// \param Column the column number at which the first character of \p
595/// Str will be printed. This will be non-zero when part of the first
596/// line has already been printed.
597/// \param Bold if the current text should be bold
598/// \returns true if word-wrapping was required, or false if the
599/// string fit on the first line.
600static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns,
601 unsigned Column, bool Bold) {
602 const unsigned Length = std::min(Str.find('\n'), Str.size());
603 bool TextNormal = true;
604
605 bool Wrapped = false;
606 for (unsigned WordStart = 0, WordEnd; WordStart < Length;
607 WordStart = WordEnd) {
608 // Find the beginning of the next word.
609 WordStart = skipWhitespace(WordStart, Str, Length);
610 if (WordStart == Length)
611 break;
612
613 // Find the end of this word.
614 WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
615
616 // Does this word fit on the current line?
617 unsigned WordLength = WordEnd - WordStart;
618 if (Column + WordLength < Columns) {
619 // This word fits on the current line; print it there.
620 if (WordStart) {
621 OS << ' ';
622 Column += 1;
623 }
624 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
625 TextNormal, Bold);
626 Column += WordLength;
627 continue;
628 }
629
630 // This word does not fit on the current line, so wrap to the next
631 // line.
632 OS << '\n';
633 OS.indent(WordWrapIndentation);
634 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
635 TextNormal, Bold);
636 Column = WordWrapIndentation + WordLength;
637 Wrapped = true;
638 }
639
640 // Append any remaning text from the message with its existing formatting.
641 applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold);
642
643 assert(TextNormal && "Text highlighted at end of diagnostic message.");
644
645 return Wrapped;
646}
647
649 const LangOptions &LangOpts,
650 DiagnosticOptions *DiagOpts)
651 : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {}
652
654
657 StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
659 uint64_t StartOfLocationInfo = OS.tell();
660
661 // Emit the location of this particular diagnostic.
662 if (Loc.isValid())
663 emitDiagnosticLoc(Loc, PLoc, Level, Ranges);
664
665 if (DiagOpts->ShowColors)
666 OS.resetColor();
667
668 if (DiagOpts->ShowLevel)
669 printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);
671 /*IsSupplemental*/ Level == DiagnosticsEngine::Note,
672 Message, OS.tell() - StartOfLocationInfo,
673 DiagOpts->MessageLength, DiagOpts->ShowColors);
674}
675
676/*static*/ void
679 bool ShowColors) {
680 if (ShowColors) {
681 // Print diagnostic category in bold and color
682 switch (Level) {
684 llvm_unreachable("Invalid diagnostic type");
685 case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break;
686 case DiagnosticsEngine::Remark: OS.changeColor(remarkColor, true); break;
687 case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break;
688 case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break;
689 case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break;
690 }
691 }
692
693 switch (Level) {
695 llvm_unreachable("Invalid diagnostic type");
696 case DiagnosticsEngine::Note: OS << "note: "; break;
697 case DiagnosticsEngine::Remark: OS << "remark: "; break;
698 case DiagnosticsEngine::Warning: OS << "warning: "; break;
699 case DiagnosticsEngine::Error: OS << "error: "; break;
700 case DiagnosticsEngine::Fatal: OS << "fatal error: "; break;
701 }
702
703 if (ShowColors)
704 OS.resetColor();
705}
706
707/*static*/
709 bool IsSupplemental,
710 StringRef Message,
711 unsigned CurrentColumn,
712 unsigned Columns, bool ShowColors) {
713 bool Bold = false;
714 if (ShowColors && !IsSupplemental) {
715 // Print primary diagnostic messages in bold and without color, to visually
716 // indicate the transition from continuation notes and other output.
717 OS.changeColor(savedColor, true);
718 Bold = true;
719 }
720
721 if (Columns)
722 printWordWrapped(OS, Message, Columns, CurrentColumn, Bold);
723 else {
724 bool Normal = true;
725 applyTemplateHighlighting(OS, Message, Normal, Bold);
726 assert(Normal && "Formatting should have returned to normal");
727 }
728
729 if (ShowColors)
730 OS.resetColor();
731 OS << '\n';
732}
733
734void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) {
735#ifdef _WIN32
736 SmallString<4096> TmpFilename;
737#endif
738 if (DiagOpts->AbsolutePath) {
739 auto File = SM.getFileManager().getOptionalFileRef(Filename);
740 if (File) {
741 // We want to print a simplified absolute path, i. e. without "dots".
742 //
743 // The hardest part here are the paths like "<part1>/<link>/../<part2>".
744 // On Unix-like systems, we cannot just collapse "<link>/..", because
745 // paths are resolved sequentially, and, thereby, the path
746 // "<part1>/<part2>" may point to a different location. That is why
747 // we use FileManager::getCanonicalName(), which expands all indirections
748 // with llvm::sys::fs::real_path() and caches the result.
749 //
750 // On the other hand, it would be better to preserve as much of the
751 // original path as possible, because that helps a user to recognize it.
752 // real_path() expands all links, which sometimes too much. Luckily,
753 // on Windows we can just use llvm::sys::path::remove_dots(), because,
754 // on that system, both aforementioned paths point to the same place.
755#ifdef _WIN32
756 TmpFilename = File->getName();
757 llvm::sys::fs::make_absolute(TmpFilename);
758 llvm::sys::path::native(TmpFilename);
759 llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
760 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
761#else
762 Filename = SM.getFileManager().getCanonicalName(*File);
763#endif
764 }
765 }
766
767 OS << Filename;
768}
769
770/// Print out the file/line/column information and include trace.
771///
772/// This method handles the emission of the diagnostic location information.
773/// This includes extracting as much location information as is present for
774/// the diagnostic and printing it, as well as any include stack or source
775/// ranges necessary.
779 if (PLoc.isInvalid()) {
780 // At least print the file name if available:
781 if (FileID FID = Loc.getFileID(); FID.isValid()) {
782 if (OptionalFileEntryRef FE = Loc.getFileEntryRef()) {
783 emitFilename(FE->getName(), Loc.getManager());
784 OS << ": ";
785 }
786 }
787 return;
788 }
789 unsigned LineNo = PLoc.getLine();
790
791 if (!DiagOpts->ShowLocation)
792 return;
793
794 if (DiagOpts->ShowColors)
795 OS.changeColor(savedColor, true);
796
797 emitFilename(PLoc.getFilename(), Loc.getManager());
798 switch (DiagOpts->getFormat()) {
801 if (DiagOpts->ShowLine)
802 OS << ':' << LineNo;
803 break;
804 case DiagnosticOptions::MSVC: OS << '(' << LineNo; break;
805 case DiagnosticOptions::Vi: OS << " +" << LineNo; break;
806 }
807
808 if (DiagOpts->ShowColumn)
809 // Compute the column number.
810 if (unsigned ColNo = PLoc.getColumn()) {
811 if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) {
812 OS << ',';
813 // Visual Studio 2010 or earlier expects column number to be off by one
814 if (LangOpts.MSCompatibilityVersion &&
816 ColNo--;
817 } else
818 OS << ':';
819 OS << ColNo;
820 }
821 switch (DiagOpts->getFormat()) {
824 case DiagnosticOptions::Vi: OS << ':'; break;
826 // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
827 // space and prints 'file(4): error'.
828 OS << ')';
829 if (LangOpts.MSCompatibilityVersion &&
831 OS << ' ';
832 OS << ':';
833 break;
834 }
835
836 if (DiagOpts->ShowSourceRanges && !Ranges.empty()) {
837 FileID CaretFileID = Loc.getExpansionLoc().getFileID();
838 bool PrintedRange = false;
839 const SourceManager &SM = Loc.getManager();
840
841 for (const auto &R : Ranges) {
842 // Ignore invalid ranges.
843 if (!R.isValid())
844 continue;
845
846 SourceLocation B = SM.getExpansionLoc(R.getBegin());
847 CharSourceRange ERange = SM.getExpansionRange(R.getEnd());
848 SourceLocation E = ERange.getEnd();
849
850 // If the start or end of the range is in another file, just
851 // discard it.
852 if (SM.getFileID(B) != CaretFileID || SM.getFileID(E) != CaretFileID)
853 continue;
854
855 // Add in the length of the token, so that we cover multi-char
856 // tokens.
857 unsigned TokSize = 0;
858 if (ERange.isTokenRange())
860
861 FullSourceLoc BF(B, SM), EF(E, SM);
862 OS << '{'
863 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-'
864 << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize)
865 << '}';
866 PrintedRange = true;
867 }
868
869 if (PrintedRange)
870 OS << ':';
871 }
872 OS << ' ';
873}
874
876 if (DiagOpts->ShowLocation && PLoc.isValid()) {
877 OS << "In file included from ";
878 emitFilename(PLoc.getFilename(), Loc.getManager());
879 OS << ':' << PLoc.getLine() << ":\n";
880 } else
881 OS << "In included file:\n";
882}
883
885 StringRef ModuleName) {
886 if (DiagOpts->ShowLocation && PLoc.isValid())
887 OS << "In module '" << ModuleName << "' imported from "
888 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
889 else
890 OS << "In module '" << ModuleName << "':\n";
891}
892
894 PresumedLoc PLoc,
895 StringRef ModuleName) {
896 if (DiagOpts->ShowLocation && PLoc.isValid())
897 OS << "While building module '" << ModuleName << "' imported from "
898 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
899 else
900 OS << "While building module '" << ModuleName << "':\n";
901}
902
903/// Find the suitable set of lines to show to include a set of ranges.
904static std::optional<std::pair<unsigned, unsigned>>
906 const SourceManager &SM) {
907 if (!R.isValid())
908 return std::nullopt;
909
911 SourceLocation End = R.getEnd();
912 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID)
913 return std::nullopt;
914
915 return std::make_pair(SM.getExpansionLineNumber(Begin),
916 SM.getExpansionLineNumber(End));
917}
918
919/// Add as much of range B into range A as possible without exceeding a maximum
920/// size of MaxRange. Ranges are inclusive.
921static std::pair<unsigned, unsigned>
922maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
923 unsigned MaxRange) {
924 // If A is already the maximum size, we're done.
925 unsigned Slack = MaxRange - (A.second - A.first + 1);
926 if (Slack == 0)
927 return A;
928
929 // Easy case: merge succeeds within MaxRange.
930 unsigned Min = std::min(A.first, B.first);
931 unsigned Max = std::max(A.second, B.second);
932 if (Max - Min + 1 <= MaxRange)
933 return {Min, Max};
934
935 // If we can't reach B from A within MaxRange, there's nothing to do.
936 // Don't add lines to the range that contain nothing interesting.
937 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
938 (B.second < A.second && A.second - B.second + 1 > MaxRange))
939 return A;
940
941 // Otherwise, expand A towards B to produce a range of size MaxRange. We
942 // attempt to expand by the same amount in both directions if B strictly
943 // contains A.
944
945 // Expand downwards by up to half the available amount, then upwards as
946 // much as possible, then downwards as much as possible.
947 A.second = std::min(A.second + (Slack + 1) / 2, Max);
948 Slack = MaxRange - (A.second - A.first + 1);
949 A.first = std::max(Min + Slack, A.first) - Slack;
950 A.second = std::min(A.first + MaxRange - 1, Max);
951 return A;
952}
953
954struct LineRange {
955 unsigned LineNo;
956 unsigned StartCol;
957 unsigned EndCol;
958};
959
960/// Highlight \p R (with ~'s) on the current source line.
961static void highlightRange(const LineRange &R, const SourceColumnMap &Map,
962 std::string &CaretLine) {
963 // Pick the first non-whitespace column.
964 unsigned StartColNo = R.StartCol;
965 while (StartColNo < Map.getSourceLine().size() &&
966 (Map.getSourceLine()[StartColNo] == ' ' ||
967 Map.getSourceLine()[StartColNo] == '\t'))
968 StartColNo = Map.startOfNextColumn(StartColNo);
969
970 // Pick the last non-whitespace column.
971 unsigned EndColNo =
972 std::min(static_cast<size_t>(R.EndCol), Map.getSourceLine().size());
973 while (EndColNo && (Map.getSourceLine()[EndColNo - 1] == ' ' ||
974 Map.getSourceLine()[EndColNo - 1] == '\t'))
975 EndColNo = Map.startOfPreviousColumn(EndColNo);
976
977 // If the start/end passed each other, then we are trying to highlight a
978 // range that just exists in whitespace. That most likely means we have
979 // a multi-line highlighting range that covers a blank line.
980 if (StartColNo > EndColNo)
981 return;
982
983 // Fill the range with ~'s.
984 StartColNo = Map.byteToContainingColumn(StartColNo);
985 EndColNo = Map.byteToContainingColumn(EndColNo);
986
987 assert(StartColNo <= EndColNo && "Invalid range!");
988 if (CaretLine.size() < EndColNo)
989 CaretLine.resize(EndColNo, ' ');
990 std::fill(CaretLine.begin() + StartColNo, CaretLine.begin() + EndColNo, '~');
991}
992
993static std::string buildFixItInsertionLine(FileID FID,
994 unsigned LineNo,
995 const SourceColumnMap &map,
997 const SourceManager &SM,
998 const DiagnosticOptions *DiagOpts) {
999 std::string FixItInsertionLine;
1000 if (Hints.empty() || !DiagOpts->ShowFixits)
1001 return FixItInsertionLine;
1002 unsigned PrevHintEndCol = 0;
1003
1004 for (const auto &H : Hints) {
1005 if (H.CodeToInsert.empty())
1006 continue;
1007
1008 // We have an insertion hint. Determine whether the inserted
1009 // code contains no newlines and is on the same line as the caret.
1010 std::pair<FileID, unsigned> HintLocInfo =
1011 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin());
1012 if (FID == HintLocInfo.first &&
1013 LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1014 StringRef(H.CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
1015 // Insert the new code into the line just below the code
1016 // that the user wrote.
1017 // Note: When modifying this function, be very careful about what is a
1018 // "column" (printed width, platform-dependent) and what is a
1019 // "byte offset" (SourceManager "column").
1020 unsigned HintByteOffset =
1021 SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
1022
1023 // The hint must start inside the source or right at the end
1024 assert(HintByteOffset < static_cast<unsigned>(map.bytes()) + 1);
1025 unsigned HintCol = map.byteToContainingColumn(HintByteOffset);
1026
1027 // If we inserted a long previous hint, push this one forwards, and add
1028 // an extra space to show that this is not part of the previous
1029 // completion. This is sort of the best we can do when two hints appear
1030 // to overlap.
1031 //
1032 // Note that if this hint is located immediately after the previous
1033 // hint, no space will be added, since the location is more important.
1034 if (HintCol < PrevHintEndCol)
1035 HintCol = PrevHintEndCol + 1;
1036
1037 // This should NOT use HintByteOffset, because the source might have
1038 // Unicode characters in earlier columns.
1039 unsigned NewFixItLineSize = FixItInsertionLine.size() +
1040 (HintCol - PrevHintEndCol) +
1041 H.CodeToInsert.size();
1042 if (NewFixItLineSize > FixItInsertionLine.size())
1043 FixItInsertionLine.resize(NewFixItLineSize, ' ');
1044
1045 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(),
1046 FixItInsertionLine.end() - H.CodeToInsert.size());
1047
1048 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert);
1049 }
1050 }
1051
1052 expandTabs(FixItInsertionLine, DiagOpts->TabStop);
1053
1054 return FixItInsertionLine;
1055}
1056
1057static unsigned getNumDisplayWidth(unsigned N) {
1058 unsigned L = 1u, M = 10u;
1059 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1)
1060 M *= 10u;
1061
1062 return L;
1063}
1064
1065/// Filter out invalid ranges, ranges that don't fit into the window of
1066/// source lines we will print, and ranges from other files.
1067///
1068/// For the remaining ranges, convert them to simple LineRange structs,
1069/// which only cover one line at a time.
1072 const SourceManager &SM,
1073 const std::pair<unsigned, unsigned> &Lines, FileID FID,
1074 const LangOptions &LangOpts) {
1075 SmallVector<LineRange> LineRanges;
1076
1077 for (const CharSourceRange &R : Ranges) {
1078 if (R.isInvalid())
1079 continue;
1080 SourceLocation Begin = R.getBegin();
1081 SourceLocation End = R.getEnd();
1082
1083 unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
1084 if (StartLineNo > Lines.second || SM.getFileID(Begin) != FID)
1085 continue;
1086
1087 unsigned EndLineNo = SM.getExpansionLineNumber(End);
1088 if (EndLineNo < Lines.first || SM.getFileID(End) != FID)
1089 continue;
1090
1091 unsigned StartColumn = SM.getExpansionColumnNumber(Begin);
1092 unsigned EndColumn = SM.getExpansionColumnNumber(End);
1093 if (R.isTokenRange())
1094 EndColumn += Lexer::MeasureTokenLength(End, SM, LangOpts);
1095
1096 // Only a single line.
1097 if (StartLineNo == EndLineNo) {
1098 LineRanges.push_back({StartLineNo, StartColumn - 1, EndColumn - 1});
1099 continue;
1100 }
1101
1102 // Start line.
1103 LineRanges.push_back({StartLineNo, StartColumn - 1, ~0u});
1104
1105 // Middle lines.
1106 for (unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
1107 LineRanges.push_back({S, 0, ~0u});
1108
1109 // End line.
1110 LineRanges.push_back({EndLineNo, 0, EndColumn - 1});
1111 }
1112
1113 return LineRanges;
1114}
1115
1116/// Emit a code snippet and caret line.
1117///
1118/// This routine emits a single line's code snippet and caret line..
1119///
1120/// \param Loc The location for the caret.
1121/// \param Ranges The underlined ranges for this code snippet.
1122/// \param Hints The FixIt hints active for this diagnostic.
1123void TextDiagnostic::emitSnippetAndCaret(
1126 assert(Loc.isValid() && "must have a valid source location here");
1127 assert(Loc.isFileID() && "must have a file location here");
1128
1129 // If caret diagnostics are enabled and we have location, we want to
1130 // emit the caret. However, we only do this if the location moved
1131 // from the last diagnostic, if the last diagnostic was a note that
1132 // was part of a different warning or error diagnostic, or if the
1133 // diagnostic has ranges. We don't want to emit the same caret
1134 // multiple times if one loc has multiple diagnostics.
1135 if (!DiagOpts->ShowCarets)
1136 return;
1137 if (Loc == LastLoc && Ranges.empty() && Hints.empty() &&
1139 return;
1140
1141 FileID FID = Loc.getFileID();
1142 const SourceManager &SM = Loc.getManager();
1143
1144 // Get information about the buffer it points into.
1145 bool Invalid = false;
1146 StringRef BufData = Loc.getBufferData(&Invalid);
1147 if (Invalid)
1148 return;
1149 const char *BufStart = BufData.data();
1150 const char *BufEnd = BufStart + BufData.size();
1151
1152 unsigned CaretLineNo = Loc.getLineNumber();
1153 unsigned CaretColNo = Loc.getColumnNumber();
1154
1155 // Arbitrarily stop showing snippets when the line is too long.
1156 static const size_t MaxLineLengthToPrint = 4096;
1157 if (CaretColNo > MaxLineLengthToPrint)
1158 return;
1159
1160 // Find the set of lines to include.
1161 const unsigned MaxLines = DiagOpts->SnippetLineLimit;
1162 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1163 unsigned DisplayLineNo = Loc.getPresumedLoc().getLine();
1164 for (const auto &I : Ranges) {
1165 if (auto OptionalRange = findLinesForRange(I, FID, SM))
1166 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines);
1167
1168 DisplayLineNo =
1169 std::min(DisplayLineNo, SM.getPresumedLineNumber(I.getBegin()));
1170 }
1171
1172 // Our line numbers look like:
1173 // " [number] | "
1174 // Where [number] is MaxLineNoDisplayWidth columns
1175 // and the full thing is therefore MaxLineNoDisplayWidth + 4 columns.
1176 unsigned MaxLineNoDisplayWidth =
1177 DiagOpts->ShowLineNumbers
1178 ? std::max(4u, getNumDisplayWidth(DisplayLineNo + MaxLines))
1179 : 0;
1180 auto indentForLineNumbers = [&] {
1181 if (MaxLineNoDisplayWidth > 0)
1182 OS.indent(MaxLineNoDisplayWidth + 2) << "| ";
1183 };
1184
1185 SmallVector<LineRange> LineRanges =
1186 prepareAndFilterRanges(Ranges, SM, Lines, FID, LangOpts);
1187
1188 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
1189 ++LineNo, ++DisplayLineNo) {
1190 // Rewind from the current position to the start of the line.
1191 const char *LineStart =
1192 BufStart +
1193 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second;
1194 if (LineStart == BufEnd)
1195 break;
1196
1197 // Compute the line end.
1198 const char *LineEnd = LineStart;
1199 while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd)
1200 ++LineEnd;
1201
1202 // Arbitrarily stop showing snippets when the line is too long.
1203 // FIXME: Don't print any lines in this case.
1204 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1205 return;
1206
1207 // Copy the line of code into an std::string for ease of manipulation.
1208 std::string SourceLine(LineStart, LineEnd);
1209 // Remove trailing null bytes.
1210 while (!SourceLine.empty() && SourceLine.back() == '\0' &&
1211 (LineNo != CaretLineNo || SourceLine.size() > CaretColNo))
1212 SourceLine.pop_back();
1213
1214 // Build the byte to column map.
1215 const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop);
1216
1217 std::string CaretLine;
1218 // Highlight all of the characters covered by Ranges with ~ characters.
1219 for (const auto &LR : LineRanges) {
1220 if (LR.LineNo == LineNo)
1221 highlightRange(LR, sourceColMap, CaretLine);
1222 }
1223
1224 // Next, insert the caret itself.
1225 if (CaretLineNo == LineNo) {
1226 size_t Col = sourceColMap.byteToContainingColumn(CaretColNo - 1);
1227 CaretLine.resize(std::max(Col + 1, CaretLine.size()), ' ');
1228 CaretLine[Col] = '^';
1229 }
1230
1231 std::string FixItInsertionLine = buildFixItInsertionLine(
1232 FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get());
1233
1234 // If the source line is too long for our terminal, select only the
1235 // "interesting" source region within that line.
1236 unsigned Columns = DiagOpts->MessageLength;
1237 if (Columns)
1238 selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
1239 Columns, sourceColMap);
1240
1241 // If we are in -fdiagnostics-print-source-range-info mode, we are trying
1242 // to produce easily machine parsable output. Add a space before the
1243 // source line and the caret to make it trivial to tell the main diagnostic
1244 // line from what the user is intended to see.
1245 if (DiagOpts->ShowSourceRanges && !SourceLine.empty()) {
1246 SourceLine = ' ' + SourceLine;
1247 CaretLine = ' ' + CaretLine;
1248 }
1249
1250 // Emit what we have computed.
1251 emitSnippet(SourceLine, MaxLineNoDisplayWidth, DisplayLineNo);
1252
1253 if (!CaretLine.empty()) {
1254 indentForLineNumbers();
1255 if (DiagOpts->ShowColors)
1256 OS.changeColor(caretColor, true);
1257 OS << CaretLine << '\n';
1258 if (DiagOpts->ShowColors)
1259 OS.resetColor();
1260 }
1261
1262 if (!FixItInsertionLine.empty()) {
1263 indentForLineNumbers();
1264 if (DiagOpts->ShowColors)
1265 // Print fixit line in color
1266 OS.changeColor(fixitColor, false);
1267 if (DiagOpts->ShowSourceRanges)
1268 OS << ' ';
1269 OS << FixItInsertionLine << '\n';
1270 if (DiagOpts->ShowColors)
1271 OS.resetColor();
1272 }
1273 }
1274
1275 // Print out any parseable fixit information requested by the options.
1276 emitParseableFixits(Hints, SM);
1277}
1278
1279void TextDiagnostic::emitSnippet(StringRef SourceLine,
1280 unsigned MaxLineNoDisplayWidth,
1281 unsigned LineNo) {
1282 // Emit line number.
1283 if (MaxLineNoDisplayWidth > 0) {
1284 unsigned LineNoDisplayWidth = getNumDisplayWidth(LineNo);
1285 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1)
1286 << LineNo << " | ";
1287 }
1288
1289 // Print the source line one character at a time.
1290 bool PrintReversed = false;
1291 size_t I = 0;
1292 while (I < SourceLine.size()) {
1293 auto [Str, WasPrintable] =
1294 printableTextForNextCharacter(SourceLine, &I, DiagOpts->TabStop);
1295
1296 // Toggle inverted colors on or off for this character.
1297 if (DiagOpts->ShowColors) {
1298 if (WasPrintable == PrintReversed) {
1299 PrintReversed = !PrintReversed;
1300 if (PrintReversed)
1301 OS.reverseColor();
1302 else
1303 OS.resetColor();
1304 }
1305 }
1306 OS << Str;
1307 }
1308
1309 if (DiagOpts->ShowColors)
1310 OS.resetColor();
1311
1312 OS << '\n';
1313}
1314
1315void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints,
1316 const SourceManager &SM) {
1317 if (!DiagOpts->ShowParseableFixits)
1318 return;
1319
1320 // We follow FixItRewriter's example in not (yet) handling
1321 // fix-its in macros.
1322 for (const auto &H : Hints) {
1323 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() ||
1324 H.RemoveRange.getEnd().isMacroID())
1325 return;
1326 }
1327
1328 for (const auto &H : Hints) {
1329 SourceLocation BLoc = H.RemoveRange.getBegin();
1330 SourceLocation ELoc = H.RemoveRange.getEnd();
1331
1332 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
1333 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
1334
1335 // Adjust for token ranges.
1336 if (H.RemoveRange.isTokenRange())
1337 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
1338
1339 // We specifically do not do word-wrapping or tab-expansion here,
1340 // because this is supposed to be easy to parse.
1341 PresumedLoc PLoc = SM.getPresumedLoc(BLoc);
1342 if (PLoc.isInvalid())
1343 break;
1344
1345 OS << "fix-it:\"";
1346 OS.write_escaped(PLoc.getFilename());
1347 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)
1348 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)
1349 << '-' << SM.getLineNumber(EInfo.first, EInfo.second)
1350 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second)
1351 << "}:\"";
1352 OS.write_escaped(H.CodeToInsert);
1353 OS << "\"\n";
1354 }
1355}
static StringRef bytes(const std::vector< T, Allocator > &v)
Definition: ASTWriter.cpp:123
#define SM(sm)
Definition: Cuda.cpp:80
Defines the clang::FileManager interface and associated types.
StringRef Filename
Definition: Format.cpp:2936
bool ShowColors
Definition: Logger.cpp:29
Defines the SourceManager interface.
static enum raw_ostream::Colors caretColor
static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i)
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 enum raw_ostream::Colors fixitColor
static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, bool &Normal, bool Bold)
Add highlights to differences in template strings.
static enum raw_ostream::Colors savedColor
static enum raw_ostream::Colors errorColor
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 enum raw_ostream::Colors remarkColor
static enum raw_ostream::Colors fatalColor
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 void selectInterestingSourceRegion(std::string &SourceLine, std::string &CaretLine, std::string &FixItInsertionLine, unsigned Columns, const SourceColumnMap &map)
When the source code line we want to print is too long for the terminal, select the "interesting" reg...
static std::string buildFixItInsertionLine(FileID FID, unsigned LineNo, const SourceColumnMap &map, ArrayRef< FixItHint > Hints, const SourceManager &SM, const DiagnosticOptions *DiagOpts)
static enum raw_ostream::Colors warningColor
static char findMatchingPunctuation(char c)
If the given character is the start of some kind of balanced punctuation (e.g., quotes or parentheses...
static enum raw_ostream::Colors noteColor
static void expandTabs(std::string &SourceLine, unsigned TabStop)
static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop, SmallVectorImpl< int > &BytesOut, SmallVectorImpl< int > &ColumnsOut)
BytesOut: A mapping from columns to the byte of the source line that produced the character displayin...
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 unsigned getNumDisplayWidth(unsigned N)
static enum raw_ostream::Colors templateColor
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,...
SourceLocation Begin
__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.
Class to encapsulate the logic for formatting a diagnostic message.
const LangOptions & LangOpts
SourceLocation LastLoc
The location of the previous diagnostic if known.
DiagnosticsEngine::Level LastLevel
The level of the last diagnostic emitted.
IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:195
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
FileID getFileID() 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
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:83
bool isCompatibleWithMSVC(MSVCMajorVersion MajorVersion) const
Definition: LangOptions.h:556
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:450
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.
bool isValid() const
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
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
TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, DiagnosticOptions *DiagOpts)
@ C
Languages that the frontend can parse and compile.
llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition: CharInfo.h:93
const char ToggleHighlight
Special character that the diagnostic printer will use to toggle the bold attribute.
Definition: Diagnostic.h:1836
unsigned EndCol
unsigned LineNo
unsigned StartCol