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