1 //===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/TextDiagnostic.h"
10 #include "clang/Basic/CharInfo.h"
11 #include "clang/Basic/DiagnosticOptions.h"
12 #include "clang/Basic/FileManager.h"
13 #include "clang/Basic/SourceManager.h"
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"
24 using namespace clang
;
26 static const enum raw_ostream::Colors noteColor
=
28 static const enum raw_ostream::Colors remarkColor
=
30 static const enum raw_ostream::Colors fixitColor
=
32 static const enum raw_ostream::Colors caretColor
=
34 static const enum raw_ostream::Colors warningColor
=
36 static const enum raw_ostream::Colors templateColor
=
38 static const enum raw_ostream::Colors errorColor
= raw_ostream::RED
;
39 static const enum raw_ostream::Colors fatalColor
= raw_ostream::RED
;
40 // Used for changing only the bold attribute.
41 static const enum raw_ostream::Colors savedColor
=
42 raw_ostream::SAVEDCOLOR
;
44 /// Add highlights to differences in template strings.
45 static void applyTemplateHighlighting(raw_ostream
&OS
, StringRef Str
,
46 bool &Normal
, bool Bold
) {
48 size_t Pos
= Str
.find(ToggleHighlight
);
49 OS
<< Str
.slice(0, Pos
);
50 if (Pos
== StringRef::npos
)
53 Str
= Str
.substr(Pos
+ 1);
55 OS
.changeColor(templateColor
, true);
59 OS
.changeColor(savedColor
, true);
65 /// Number of spaces to indent when word-wrapping.
66 const unsigned WordWrapIndentation
= 6;
68 static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine
, size_t i
) {
71 if (SourceLine
[--i
]=='\t')
78 /// returns a printable representation of first item from input range
80 /// This function returns a printable representation of the next item in a line
81 /// of source. If the next byte begins a valid and printable character, that
82 /// character is returned along with 'true'.
84 /// Otherwise, if the next byte begins a valid, but unprintable character, a
85 /// printable, escaped representation of the character is returned, along with
86 /// 'false'. Otherwise a printable, escaped representation of the next byte
87 /// is returned along with 'false'.
89 /// \note The index is updated to be used with a subsequent call to
90 /// printableTextForNextCharacter.
92 /// \param SourceLine The line of source
93 /// \param i Pointer to byte index,
94 /// \param TabStop used to expand tabs
95 /// \return pair(printable text, 'true' iff original text was printable)
97 static std::pair
<SmallString
<16>, bool>
98 printableTextForNextCharacter(StringRef SourceLine
, size_t *i
,
100 assert(i
&& "i must not be null");
101 assert(*i
<SourceLine
.size() && "must point to a valid index");
103 if (SourceLine
[*i
]=='\t') {
104 assert(0 < TabStop
&& TabStop
<= DiagnosticOptions::MaxTabStop
&&
105 "Invalid -ftabstop value");
106 unsigned col
= bytesSincePreviousTabOrLineBegin(SourceLine
, *i
);
107 unsigned NumSpaces
= TabStop
- col
%TabStop
;
108 assert(0 < NumSpaces
&& NumSpaces
<= TabStop
109 && "Invalid computation of space amt");
112 SmallString
<16> expandedTab
;
113 expandedTab
.assign(NumSpaces
, ' ');
114 return std::make_pair(expandedTab
, true);
117 unsigned char const *begin
, *end
;
118 begin
= reinterpret_cast<unsigned char const *>(&*(SourceLine
.begin() + *i
));
119 end
= begin
+ (SourceLine
.size() - *i
);
121 if (llvm::isLegalUTF8Sequence(begin
, end
)) {
123 llvm::UTF32
*cptr
= &c
;
124 unsigned char const *original_begin
= begin
;
125 unsigned char const *cp_end
=
126 begin
+ llvm::getNumBytesForUTF8(SourceLine
[*i
]);
128 llvm::ConversionResult res
= llvm::ConvertUTF8toUTF32(
129 &begin
, cp_end
, &cptr
, cptr
+ 1, llvm::strictConversion
);
131 assert(llvm::conversionOK
== res
);
132 assert(0 < begin
-original_begin
133 && "we must be further along in the string now");
134 *i
+= begin
-original_begin
;
136 if (!llvm::sys::locale::isPrint(c
)) {
137 // If next character is valid UTF-8, but not printable
138 SmallString
<16> expandedCP("<U+>");
140 expandedCP
.insert(expandedCP
.begin()+3, llvm::hexdigit(c
%16));
143 while (expandedCP
.size() < 8)
144 expandedCP
.insert(expandedCP
.begin()+3, llvm::hexdigit(0));
145 return std::make_pair(expandedCP
, false);
148 // If next character is valid UTF-8, and printable
149 return std::make_pair(SmallString
<16>(original_begin
, cp_end
), true);
153 // If next byte is not valid UTF-8 (and therefore not printable)
154 SmallString
<16> expandedByte("<XX>");
155 unsigned char byte
= SourceLine
[*i
];
156 expandedByte
[1] = llvm::hexdigit(byte
/ 16);
157 expandedByte
[2] = llvm::hexdigit(byte
% 16);
159 return std::make_pair(expandedByte
, false);
162 static void expandTabs(std::string
&SourceLine
, unsigned TabStop
) {
163 size_t i
= SourceLine
.size();
166 if (SourceLine
[i
]!='\t')
169 std::pair
<SmallString
<16>,bool> res
170 = printableTextForNextCharacter(SourceLine
, &tmp_i
, TabStop
);
171 SourceLine
.replace(i
, 1, res
.first
.c_str());
175 /// This function takes a raw source line and produces a mapping from the bytes
176 /// of the printable representation of the line to the columns those printable
177 /// characters will appear at (numbering the first column as 0).
179 /// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab
180 /// character) then the array will map that byte to the first column the
181 /// tab appears at and the next value in the map will have been incremented
184 /// If a byte is the first in a sequence of bytes that together map to a single
185 /// entity in the output, then the array will map that byte to the appropriate
186 /// column while the subsequent bytes will be -1.
188 /// The last element in the array does not correspond to any byte in the input
189 /// and instead is the number of columns needed to display the source
191 /// example: (given a tabstop of 8)
193 /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
195 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
197 static void byteToColumn(StringRef SourceLine
, unsigned TabStop
,
198 SmallVectorImpl
<int> &out
) {
201 if (SourceLine
.empty()) {
206 out
.resize(SourceLine
.size()+1, -1);
210 while (i
<SourceLine
.size()) {
212 std::pair
<SmallString
<16>,bool> res
213 = printableTextForNextCharacter(SourceLine
, &i
, TabStop
);
214 columns
+= llvm::sys::locale::columnWidth(res
.first
);
216 out
.back() = columns
;
219 /// This function takes a raw source line and produces a mapping from columns
220 /// to the byte of the source line that produced the character displaying at
221 /// that column. This is the inverse of the mapping produced by byteToColumn()
223 /// The last element in the array is the number of bytes in the source string
225 /// example: (given a tabstop of 8)
227 /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
229 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
231 static void columnToByte(StringRef SourceLine
, unsigned TabStop
,
232 SmallVectorImpl
<int> &out
) {
235 if (SourceLine
.empty()) {
242 while (i
<SourceLine
.size()) {
243 out
.resize(columns
+1, -1);
245 std::pair
<SmallString
<16>,bool> res
246 = printableTextForNextCharacter(SourceLine
, &i
, TabStop
);
247 columns
+= llvm::sys::locale::columnWidth(res
.first
);
249 out
.resize(columns
+1, -1);
254 struct SourceColumnMap
{
255 SourceColumnMap(StringRef SourceLine
, unsigned TabStop
)
256 : m_SourceLine(SourceLine
) {
258 ::byteToColumn(SourceLine
, TabStop
, m_byteToColumn
);
259 ::columnToByte(SourceLine
, TabStop
, m_columnToByte
);
261 assert(m_byteToColumn
.size()==SourceLine
.size()+1);
262 assert(0 < m_byteToColumn
.size() && 0 < m_columnToByte
.size());
263 assert(m_byteToColumn
.size()
264 == static_cast<unsigned>(m_columnToByte
.back()+1));
265 assert(static_cast<unsigned>(m_byteToColumn
.back()+1)
266 == m_columnToByte
.size());
268 int columns() const { return m_byteToColumn
.back(); }
269 int bytes() const { return m_columnToByte
.back(); }
271 /// Map a byte to the column which it is at the start of, or return -1
272 /// if it is not at the start of a column (for a UTF-8 trailing byte).
273 int byteToColumn(int n
) const {
274 assert(0<=n
&& n
<static_cast<int>(m_byteToColumn
.size()));
275 return m_byteToColumn
[n
];
278 /// Map a byte to the first column which contains it.
279 int byteToContainingColumn(int N
) const {
280 assert(0 <= N
&& N
< static_cast<int>(m_byteToColumn
.size()));
281 while (m_byteToColumn
[N
] == -1)
283 return m_byteToColumn
[N
];
286 /// Map a column to the byte which starts the column, or return -1 if
287 /// the column the second or subsequent column of an expanded tab or similar
288 /// multi-column entity.
289 int columnToByte(int n
) const {
290 assert(0<=n
&& n
<static_cast<int>(m_columnToByte
.size()));
291 return m_columnToByte
[n
];
294 /// Map from a byte index to the next byte which starts a column.
295 int startOfNextColumn(int N
) const {
296 assert(0 <= N
&& N
< static_cast<int>(m_byteToColumn
.size() - 1));
297 while (byteToColumn(++N
) == -1) {}
301 /// Map from a byte index to the previous byte which starts a column.
302 int startOfPreviousColumn(int N
) const {
303 assert(0 < N
&& N
< static_cast<int>(m_byteToColumn
.size()));
304 while (byteToColumn(--N
) == -1) {}
308 StringRef
getSourceLine() const {
313 const std::string m_SourceLine
;
314 SmallVector
<int,200> m_byteToColumn
;
315 SmallVector
<int,200> m_columnToByte
;
317 } // end anonymous namespace
319 /// When the source code line we want to print is too long for
320 /// the terminal, select the "interesting" region.
321 static void selectInterestingSourceRegion(std::string
&SourceLine
,
322 std::string
&CaretLine
,
323 std::string
&FixItInsertionLine
,
325 const SourceColumnMap
&map
) {
326 unsigned CaretColumns
= CaretLine
.size();
327 unsigned FixItColumns
= llvm::sys::locale::columnWidth(FixItInsertionLine
);
328 unsigned MaxColumns
= std::max(static_cast<unsigned>(map
.columns()),
329 std::max(CaretColumns
, FixItColumns
));
330 // if the number of columns is less than the desired number we're done
331 if (MaxColumns
<= Columns
)
334 // No special characters are allowed in CaretLine.
335 assert(llvm::none_of(CaretLine
, [](char c
) { return c
< ' ' || '~' < c
; }));
337 // Find the slice that we need to display the full caret line
339 unsigned CaretStart
= 0, CaretEnd
= CaretLine
.size();
340 for (; CaretStart
!= CaretEnd
; ++CaretStart
)
341 if (!isWhitespace(CaretLine
[CaretStart
]))
344 for (; CaretEnd
!= CaretStart
; --CaretEnd
)
345 if (!isWhitespace(CaretLine
[CaretEnd
- 1]))
348 // caret has already been inserted into CaretLine so the above whitespace
349 // check is guaranteed to include the caret
351 // If we have a fix-it line, make sure the slice includes all of the
352 // fix-it information.
353 if (!FixItInsertionLine
.empty()) {
354 unsigned FixItStart
= 0, FixItEnd
= FixItInsertionLine
.size();
355 for (; FixItStart
!= FixItEnd
; ++FixItStart
)
356 if (!isWhitespace(FixItInsertionLine
[FixItStart
]))
359 for (; FixItEnd
!= FixItStart
; --FixItEnd
)
360 if (!isWhitespace(FixItInsertionLine
[FixItEnd
- 1]))
363 // We can safely use the byte offset FixItStart as the column offset
364 // because the characters up until FixItStart are all ASCII whitespace
366 unsigned FixItStartCol
= FixItStart
;
368 = llvm::sys::locale::columnWidth(FixItInsertionLine
.substr(0, FixItEnd
));
370 CaretStart
= std::min(FixItStartCol
, CaretStart
);
371 CaretEnd
= std::max(FixItEndCol
, CaretEnd
);
374 // CaretEnd may have been set at the middle of a character
375 // If it's not at a character's first column then advance it past the current
377 while (static_cast<int>(CaretEnd
) < map
.columns() &&
378 -1 == map
.columnToByte(CaretEnd
))
381 assert((static_cast<int>(CaretStart
) > map
.columns() ||
382 -1!=map
.columnToByte(CaretStart
)) &&
383 "CaretStart must not point to a column in the middle of a source"
385 assert((static_cast<int>(CaretEnd
) > map
.columns() ||
386 -1!=map
.columnToByte(CaretEnd
)) &&
387 "CaretEnd must not point to a column in the middle of a source line"
390 // CaretLine[CaretStart, CaretEnd) contains all of the interesting
391 // parts of the caret line. While this slice is smaller than the
392 // number of columns we have, try to grow the slice to encompass
395 unsigned SourceStart
= map
.columnToByte(std::min
<unsigned>(CaretStart
,
397 unsigned SourceEnd
= map
.columnToByte(std::min
<unsigned>(CaretEnd
,
400 unsigned CaretColumnsOutsideSource
= CaretEnd
-CaretStart
401 - (map
.byteToColumn(SourceEnd
)-map
.byteToColumn(SourceStart
));
403 char const *front_ellipse
= " ...";
404 char const *front_space
= " ";
405 char const *back_ellipse
= "...";
406 unsigned ellipses_space
= strlen(front_ellipse
) + strlen(back_ellipse
);
408 unsigned TargetColumns
= Columns
;
409 // Give us extra room for the ellipses
410 // and any of the caret line that extends past the source
411 if (TargetColumns
> ellipses_space
+CaretColumnsOutsideSource
)
412 TargetColumns
-= ellipses_space
+CaretColumnsOutsideSource
;
414 while (SourceStart
>0 || SourceEnd
<SourceLine
.size()) {
415 bool ExpandedRegion
= false;
418 unsigned NewStart
= map
.startOfPreviousColumn(SourceStart
);
420 // Skip over any whitespace we see here; we're looking for
421 // another bit of interesting text.
422 // FIXME: Detect non-ASCII whitespace characters too.
423 while (NewStart
&& isWhitespace(SourceLine
[NewStart
]))
424 NewStart
= map
.startOfPreviousColumn(NewStart
);
426 // Skip over this bit of "interesting" text.
428 unsigned Prev
= map
.startOfPreviousColumn(NewStart
);
429 if (isWhitespace(SourceLine
[Prev
]))
434 assert(map
.byteToColumn(NewStart
) != -1);
435 unsigned NewColumns
= map
.byteToColumn(SourceEnd
) -
436 map
.byteToColumn(NewStart
);
437 if (NewColumns
<= TargetColumns
) {
438 SourceStart
= NewStart
;
439 ExpandedRegion
= true;
443 if (SourceEnd
<SourceLine
.size()) {
444 unsigned NewEnd
= map
.startOfNextColumn(SourceEnd
);
446 // Skip over any whitespace we see here; we're looking for
447 // another bit of interesting text.
448 // FIXME: Detect non-ASCII whitespace characters too.
449 while (NewEnd
< SourceLine
.size() && isWhitespace(SourceLine
[NewEnd
]))
450 NewEnd
= map
.startOfNextColumn(NewEnd
);
452 // Skip over this bit of "interesting" text.
453 while (NewEnd
< SourceLine
.size() && isWhitespace(SourceLine
[NewEnd
]))
454 NewEnd
= map
.startOfNextColumn(NewEnd
);
456 assert(map
.byteToColumn(NewEnd
) != -1);
457 unsigned NewColumns
= map
.byteToColumn(NewEnd
) -
458 map
.byteToColumn(SourceStart
);
459 if (NewColumns
<= TargetColumns
) {
461 ExpandedRegion
= true;
469 CaretStart
= map
.byteToColumn(SourceStart
);
470 CaretEnd
= map
.byteToColumn(SourceEnd
) + CaretColumnsOutsideSource
;
472 // [CaretStart, CaretEnd) is the slice we want. Update the various
473 // output lines to show only this slice, with two-space padding
474 // before the lines so that it looks nicer.
476 assert(CaretStart
!=(unsigned)-1 && CaretEnd
!=(unsigned)-1 &&
477 SourceStart
!=(unsigned)-1 && SourceEnd
!=(unsigned)-1);
478 assert(SourceStart
<= SourceEnd
);
479 assert(CaretStart
<= CaretEnd
);
481 unsigned BackColumnsRemoved
482 = map
.byteToColumn(SourceLine
.size())-map
.byteToColumn(SourceEnd
);
483 unsigned FrontColumnsRemoved
= CaretStart
;
484 unsigned ColumnsKept
= CaretEnd
-CaretStart
;
486 // We checked up front that the line needed truncation
487 assert(FrontColumnsRemoved
+ColumnsKept
+BackColumnsRemoved
> Columns
);
489 // The line needs some truncation, and we'd prefer to keep the front
490 // if possible, so remove the back
491 if (BackColumnsRemoved
> strlen(back_ellipse
))
492 SourceLine
.replace(SourceEnd
, std::string::npos
, back_ellipse
);
494 // If that's enough then we're done
495 if (FrontColumnsRemoved
+ColumnsKept
<= Columns
)
498 // Otherwise remove the front as well
499 if (FrontColumnsRemoved
> strlen(front_ellipse
)) {
500 SourceLine
.replace(0, SourceStart
, front_ellipse
);
501 CaretLine
.replace(0, CaretStart
, front_space
);
502 if (!FixItInsertionLine
.empty())
503 FixItInsertionLine
.replace(0, CaretStart
, front_space
);
507 /// Skip over whitespace in the string, starting at the given
510 /// \returns The index of the first non-whitespace character that is
511 /// greater than or equal to Idx or, if no such character exists,
512 /// returns the end of the string.
513 static unsigned skipWhitespace(unsigned Idx
, StringRef Str
, unsigned Length
) {
514 while (Idx
< Length
&& isWhitespace(Str
[Idx
]))
519 /// If the given character is the start of some kind of
520 /// balanced punctuation (e.g., quotes or parentheses), return the
521 /// character that will terminate the punctuation.
523 /// \returns The ending punctuation character, if any, or the NULL
524 /// character if the input character does not start any punctuation.
525 static inline char findMatchingPunctuation(char c
) {
527 case '\'': return '\'';
528 case '`': return '\'';
529 case '"': return '"';
530 case '(': return ')';
531 case '[': return ']';
532 case '{': return '}';
539 /// Find the end of the word starting at the given offset
542 /// \returns the index pointing one character past the end of the
544 static unsigned findEndOfWord(unsigned Start
, StringRef Str
,
545 unsigned Length
, unsigned Column
,
547 assert(Start
< Str
.size() && "Invalid start position!");
548 unsigned End
= Start
+ 1;
550 // If we are already at the end of the string, take that as the word.
551 if (End
== Str
.size())
554 // Determine if the start of the string is actually opening
555 // punctuation, e.g., a quote or parentheses.
556 char EndPunct
= findMatchingPunctuation(Str
[Start
]);
558 // This is a normal word. Just find the first space character.
559 while (End
< Length
&& !isWhitespace(Str
[End
]))
564 // We have the start of a balanced punctuation sequence (quotes,
565 // parentheses, etc.). Determine the full sequence is.
566 SmallString
<16> PunctuationEndStack
;
567 PunctuationEndStack
.push_back(EndPunct
);
568 while (End
< Length
&& !PunctuationEndStack
.empty()) {
569 if (Str
[End
] == PunctuationEndStack
.back())
570 PunctuationEndStack
.pop_back();
571 else if (char SubEndPunct
= findMatchingPunctuation(Str
[End
]))
572 PunctuationEndStack
.push_back(SubEndPunct
);
577 // Find the first space character after the punctuation ended.
578 while (End
< Length
&& !isWhitespace(Str
[End
]))
581 unsigned PunctWordLength
= End
- Start
;
582 if (// If the word fits on this line
583 Column
+ PunctWordLength
<= Columns
||
584 // ... or the word is "short enough" to take up the next line
585 // without too much ugly white space
586 PunctWordLength
< Columns
/3)
587 return End
; // Take the whole thing as a single "word".
589 // The whole quoted/parenthesized string is too long to print as a
590 // single "word". Instead, find the "word" that starts just after
591 // the punctuation and use that end-point instead. This will recurse
592 // until it finds something small enough to consider a word.
593 return findEndOfWord(Start
+ 1, Str
, Length
, Column
+ 1, Columns
);
596 /// Print the given string to a stream, word-wrapping it to
597 /// some number of columns in the process.
599 /// \param OS the stream to which the word-wrapping string will be
601 /// \param Str the string to word-wrap and output.
602 /// \param Columns the number of columns to word-wrap to.
603 /// \param Column the column number at which the first character of \p
604 /// Str will be printed. This will be non-zero when part of the first
605 /// line has already been printed.
606 /// \param Bold if the current text should be bold
607 /// \param Indentation the number of spaces to indent any lines beyond
609 /// \returns true if word-wrapping was required, or false if the
610 /// string fit on the first line.
611 static bool printWordWrapped(raw_ostream
&OS
, StringRef Str
,
615 unsigned Indentation
= WordWrapIndentation
) {
616 const unsigned Length
= std::min(Str
.find('\n'), Str
.size());
617 bool TextNormal
= true;
619 // The string used to indent each line.
620 SmallString
<16> IndentStr
;
621 IndentStr
.assign(Indentation
, ' ');
622 bool Wrapped
= false;
623 for (unsigned WordStart
= 0, WordEnd
; WordStart
< Length
;
624 WordStart
= WordEnd
) {
625 // Find the beginning of the next word.
626 WordStart
= skipWhitespace(WordStart
, Str
, Length
);
627 if (WordStart
== Length
)
630 // Find the end of this word.
631 WordEnd
= findEndOfWord(WordStart
, Str
, Length
, Column
, Columns
);
633 // Does this word fit on the current line?
634 unsigned WordLength
= WordEnd
- WordStart
;
635 if (Column
+ WordLength
< Columns
) {
636 // This word fits on the current line; print it there.
641 applyTemplateHighlighting(OS
, Str
.substr(WordStart
, WordLength
),
643 Column
+= WordLength
;
647 // This word does not fit on the current line, so wrap to the next
650 OS
.write(&IndentStr
[0], Indentation
);
651 applyTemplateHighlighting(OS
, Str
.substr(WordStart
, WordLength
),
653 Column
= Indentation
+ WordLength
;
657 // Append any remaning text from the message with its existing formatting.
658 applyTemplateHighlighting(OS
, Str
.substr(Length
), TextNormal
, Bold
);
660 assert(TextNormal
&& "Text highlighted at end of diagnostic message.");
665 TextDiagnostic::TextDiagnostic(raw_ostream
&OS
,
666 const LangOptions
&LangOpts
,
667 DiagnosticOptions
*DiagOpts
)
668 : DiagnosticRenderer(LangOpts
, DiagOpts
), OS(OS
) {}
670 TextDiagnostic::~TextDiagnostic() {}
672 void TextDiagnostic::emitDiagnosticMessage(
673 FullSourceLoc Loc
, PresumedLoc PLoc
, DiagnosticsEngine::Level Level
,
674 StringRef Message
, ArrayRef
<clang::CharSourceRange
> Ranges
,
675 DiagOrStoredDiag D
) {
676 uint64_t StartOfLocationInfo
= OS
.tell();
678 // Emit the location of this particular diagnostic.
680 emitDiagnosticLoc(Loc
, PLoc
, Level
, Ranges
);
682 if (DiagOpts
->ShowColors
)
685 if (DiagOpts
->ShowLevel
)
686 printDiagnosticLevel(OS
, Level
, DiagOpts
->ShowColors
);
687 printDiagnosticMessage(OS
,
688 /*IsSupplemental*/ Level
== DiagnosticsEngine::Note
,
689 Message
, OS
.tell() - StartOfLocationInfo
,
690 DiagOpts
->MessageLength
, DiagOpts
->ShowColors
);
694 TextDiagnostic::printDiagnosticLevel(raw_ostream
&OS
,
695 DiagnosticsEngine::Level Level
,
698 // Print diagnostic category in bold and color
700 case DiagnosticsEngine::Ignored
:
701 llvm_unreachable("Invalid diagnostic type");
702 case DiagnosticsEngine::Note
: OS
.changeColor(noteColor
, true); break;
703 case DiagnosticsEngine::Remark
: OS
.changeColor(remarkColor
, true); break;
704 case DiagnosticsEngine::Warning
: OS
.changeColor(warningColor
, true); break;
705 case DiagnosticsEngine::Error
: OS
.changeColor(errorColor
, true); break;
706 case DiagnosticsEngine::Fatal
: OS
.changeColor(fatalColor
, true); break;
711 case DiagnosticsEngine::Ignored
:
712 llvm_unreachable("Invalid diagnostic type");
713 case DiagnosticsEngine::Note
: OS
<< "note: "; break;
714 case DiagnosticsEngine::Remark
: OS
<< "remark: "; break;
715 case DiagnosticsEngine::Warning
: OS
<< "warning: "; break;
716 case DiagnosticsEngine::Error
: OS
<< "error: "; break;
717 case DiagnosticsEngine::Fatal
: OS
<< "fatal error: "; break;
725 void TextDiagnostic::printDiagnosticMessage(raw_ostream
&OS
,
728 unsigned CurrentColumn
,
729 unsigned Columns
, bool ShowColors
) {
731 if (ShowColors
&& !IsSupplemental
) {
732 // Print primary diagnostic messages in bold and without color, to visually
733 // indicate the transition from continuation notes and other output.
734 OS
.changeColor(savedColor
, true);
739 printWordWrapped(OS
, Message
, Columns
, CurrentColumn
, Bold
);
742 applyTemplateHighlighting(OS
, Message
, Normal
, Bold
);
743 assert(Normal
&& "Formatting should have returned to normal");
751 void TextDiagnostic::emitFilename(StringRef Filename
, const SourceManager
&SM
) {
753 SmallString
<4096> TmpFilename
;
755 if (DiagOpts
->AbsolutePath
) {
756 auto File
= SM
.getFileManager().getFile(Filename
);
758 // We want to print a simplified absolute path, i. e. without "dots".
760 // The hardest part here are the paths like "<part1>/<link>/../<part2>".
761 // On Unix-like systems, we cannot just collapse "<link>/..", because
762 // paths are resolved sequentially, and, thereby, the path
763 // "<part1>/<part2>" may point to a different location. That is why
764 // we use FileManager::getCanonicalName(), which expands all indirections
765 // with llvm::sys::fs::real_path() and caches the result.
767 // On the other hand, it would be better to preserve as much of the
768 // original path as possible, because that helps a user to recognize it.
769 // real_path() expands all links, which sometimes too much. Luckily,
770 // on Windows we can just use llvm::sys::path::remove_dots(), because,
771 // on that system, both aforementioned paths point to the same place.
773 TmpFilename
= (*File
)->getName();
774 llvm::sys::fs::make_absolute(TmpFilename
);
775 llvm::sys::path::native(TmpFilename
);
776 llvm::sys::path::remove_dots(TmpFilename
, /* remove_dot_dot */ true);
777 Filename
= StringRef(TmpFilename
.data(), TmpFilename
.size());
779 Filename
= SM
.getFileManager().getCanonicalName(*File
);
787 /// Print out the file/line/column information and include trace.
789 /// This method handlen the emission of the diagnostic location information.
790 /// This includes extracting as much location information as is present for
791 /// the diagnostic and printing it, as well as any include stack or source
792 /// ranges necessary.
793 void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc
, PresumedLoc PLoc
,
794 DiagnosticsEngine::Level Level
,
795 ArrayRef
<CharSourceRange
> Ranges
) {
796 if (PLoc
.isInvalid()) {
797 // At least print the file name if available:
798 FileID FID
= Loc
.getFileID();
800 if (const FileEntry
*FE
= Loc
.getFileEntry()) {
801 emitFilename(FE
->getName(), Loc
.getManager());
807 unsigned LineNo
= PLoc
.getLine();
809 if (!DiagOpts
->ShowLocation
)
812 if (DiagOpts
->ShowColors
)
813 OS
.changeColor(savedColor
, true);
815 emitFilename(PLoc
.getFilename(), Loc
.getManager());
816 switch (DiagOpts
->getFormat()) {
817 case DiagnosticOptions::SARIF
:
818 case DiagnosticOptions::Clang
:
819 if (DiagOpts
->ShowLine
)
822 case DiagnosticOptions::MSVC
: OS
<< '(' << LineNo
; break;
823 case DiagnosticOptions::Vi
: OS
<< " +" << LineNo
; break;
826 if (DiagOpts
->ShowColumn
)
827 // Compute the column number.
828 if (unsigned ColNo
= PLoc
.getColumn()) {
829 if (DiagOpts
->getFormat() == DiagnosticOptions::MSVC
) {
831 // Visual Studio 2010 or earlier expects column number to be off by one
832 if (LangOpts
.MSCompatibilityVersion
&&
833 !LangOpts
.isCompatibleWithMSVC(LangOptions::MSVC2012
))
839 switch (DiagOpts
->getFormat()) {
840 case DiagnosticOptions::SARIF
:
841 case DiagnosticOptions::Clang
:
842 case DiagnosticOptions::Vi
: OS
<< ':'; break;
843 case DiagnosticOptions::MSVC
:
844 // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
845 // space and prints 'file(4): error'.
847 if (LangOpts
.MSCompatibilityVersion
&&
848 !LangOpts
.isCompatibleWithMSVC(LangOptions::MSVC2015
))
854 if (DiagOpts
->ShowSourceRanges
&& !Ranges
.empty()) {
855 FileID CaretFileID
= Loc
.getExpansionLoc().getFileID();
856 bool PrintedRange
= false;
858 for (ArrayRef
<CharSourceRange
>::const_iterator RI
= Ranges
.begin(),
861 // Ignore invalid ranges.
862 if (!RI
->isValid()) continue;
864 auto &SM
= Loc
.getManager();
865 SourceLocation B
= SM
.getExpansionLoc(RI
->getBegin());
866 CharSourceRange ERange
= SM
.getExpansionRange(RI
->getEnd());
867 SourceLocation E
= ERange
.getEnd();
868 bool IsTokenRange
= ERange
.isTokenRange();
870 std::pair
<FileID
, unsigned> BInfo
= SM
.getDecomposedLoc(B
);
871 std::pair
<FileID
, unsigned> EInfo
= SM
.getDecomposedLoc(E
);
873 // If the start or end of the range is in another file, just discard
875 if (BInfo
.first
!= CaretFileID
|| EInfo
.first
!= CaretFileID
)
878 // Add in the length of the token, so that we cover multi-char
880 unsigned TokSize
= 0;
882 TokSize
= Lexer::MeasureTokenLength(E
, SM
, LangOpts
);
884 FullSourceLoc
BF(B
, SM
), EF(E
, SM
);
886 << BF
.getLineNumber() << ':' << BF
.getColumnNumber() << '-'
887 << EF
.getLineNumber() << ':' << (EF
.getColumnNumber() + TokSize
)
898 void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc
, PresumedLoc PLoc
) {
899 if (DiagOpts
->ShowLocation
&& PLoc
.isValid())
900 OS
<< "In file included from " << PLoc
.getFilename() << ':'
901 << PLoc
.getLine() << ":\n";
903 OS
<< "In included file:\n";
906 void TextDiagnostic::emitImportLocation(FullSourceLoc Loc
, PresumedLoc PLoc
,
907 StringRef ModuleName
) {
908 if (DiagOpts
->ShowLocation
&& PLoc
.isValid())
909 OS
<< "In module '" << ModuleName
<< "' imported from "
910 << PLoc
.getFilename() << ':' << PLoc
.getLine() << ":\n";
912 OS
<< "In module '" << ModuleName
<< "':\n";
915 void TextDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc
,
917 StringRef ModuleName
) {
918 if (DiagOpts
->ShowLocation
&& PLoc
.isValid())
919 OS
<< "While building module '" << ModuleName
<< "' imported from "
920 << PLoc
.getFilename() << ':' << PLoc
.getLine() << ":\n";
922 OS
<< "While building module '" << ModuleName
<< "':\n";
925 /// Find the suitable set of lines to show to include a set of ranges.
926 static llvm::Optional
<std::pair
<unsigned, unsigned>>
927 findLinesForRange(const CharSourceRange
&R
, FileID FID
,
928 const SourceManager
&SM
) {
929 if (!R
.isValid()) return None
;
931 SourceLocation Begin
= R
.getBegin();
932 SourceLocation End
= R
.getEnd();
933 if (SM
.getFileID(Begin
) != FID
|| SM
.getFileID(End
) != FID
)
936 return std::make_pair(SM
.getExpansionLineNumber(Begin
),
937 SM
.getExpansionLineNumber(End
));
940 /// Add as much of range B into range A as possible without exceeding a maximum
941 /// size of MaxRange. Ranges are inclusive.
942 static std::pair
<unsigned, unsigned>
943 maybeAddRange(std::pair
<unsigned, unsigned> A
, std::pair
<unsigned, unsigned> B
,
945 // If A is already the maximum size, we're done.
946 unsigned Slack
= MaxRange
- (A
.second
- A
.first
+ 1);
950 // Easy case: merge succeeds within MaxRange.
951 unsigned Min
= std::min(A
.first
, B
.first
);
952 unsigned Max
= std::max(A
.second
, B
.second
);
953 if (Max
- Min
+ 1 <= MaxRange
)
956 // If we can't reach B from A within MaxRange, there's nothing to do.
957 // Don't add lines to the range that contain nothing interesting.
958 if ((B
.first
> A
.first
&& B
.first
- A
.first
+ 1 > MaxRange
) ||
959 (B
.second
< A
.second
&& A
.second
- B
.second
+ 1 > MaxRange
))
962 // Otherwise, expand A towards B to produce a range of size MaxRange. We
963 // attempt to expand by the same amount in both directions if B strictly
966 // Expand downwards by up to half the available amount, then upwards as
967 // much as possible, then downwards as much as possible.
968 A
.second
= std::min(A
.second
+ (Slack
+ 1) / 2, Max
);
969 Slack
= MaxRange
- (A
.second
- A
.first
+ 1);
970 A
.first
= std::max(Min
+ Slack
, A
.first
) - Slack
;
971 A
.second
= std::min(A
.first
+ MaxRange
- 1, Max
);
975 /// Highlight a SourceRange (with ~'s) for any characters on LineNo.
976 static void highlightRange(const CharSourceRange
&R
,
977 unsigned LineNo
, FileID FID
,
978 const SourceColumnMap
&map
,
979 std::string
&CaretLine
,
980 const SourceManager
&SM
,
981 const LangOptions
&LangOpts
) {
982 if (!R
.isValid()) return;
984 SourceLocation Begin
= R
.getBegin();
985 SourceLocation End
= R
.getEnd();
987 unsigned StartLineNo
= SM
.getExpansionLineNumber(Begin
);
988 if (StartLineNo
> LineNo
|| SM
.getFileID(Begin
) != FID
)
989 return; // No intersection.
991 unsigned EndLineNo
= SM
.getExpansionLineNumber(End
);
992 if (EndLineNo
< LineNo
|| SM
.getFileID(End
) != FID
)
993 return; // No intersection.
995 // Compute the column number of the start.
996 unsigned StartColNo
= 0;
997 if (StartLineNo
== LineNo
) {
998 StartColNo
= SM
.getExpansionColumnNumber(Begin
);
999 if (StartColNo
) --StartColNo
; // Zero base the col #.
1002 // Compute the column number of the end.
1003 unsigned EndColNo
= map
.getSourceLine().size();
1004 if (EndLineNo
== LineNo
) {
1005 EndColNo
= SM
.getExpansionColumnNumber(End
);
1007 --EndColNo
; // Zero base the col #.
1009 // Add in the length of the token, so that we cover multi-char tokens if
1010 // this is a token range.
1011 if (R
.isTokenRange())
1012 EndColNo
+= Lexer::MeasureTokenLength(End
, SM
, LangOpts
);
1014 EndColNo
= CaretLine
.size();
1018 assert(StartColNo
<= EndColNo
&& "Invalid range!");
1020 // Check that a token range does not highlight only whitespace.
1021 if (R
.isTokenRange()) {
1022 // Pick the first non-whitespace column.
1023 while (StartColNo
< map
.getSourceLine().size() &&
1024 (map
.getSourceLine()[StartColNo
] == ' ' ||
1025 map
.getSourceLine()[StartColNo
] == '\t'))
1026 StartColNo
= map
.startOfNextColumn(StartColNo
);
1028 // Pick the last non-whitespace column.
1029 if (EndColNo
> map
.getSourceLine().size())
1030 EndColNo
= map
.getSourceLine().size();
1032 (map
.getSourceLine()[EndColNo
-1] == ' ' ||
1033 map
.getSourceLine()[EndColNo
-1] == '\t'))
1034 EndColNo
= map
.startOfPreviousColumn(EndColNo
);
1036 // If the start/end passed each other, then we are trying to highlight a
1037 // range that just exists in whitespace. That most likely means we have
1038 // a multi-line highlighting range that covers a blank line.
1039 if (StartColNo
> EndColNo
) {
1040 assert(StartLineNo
!= EndLineNo
&& "trying to highlight whitespace");
1041 StartColNo
= EndColNo
;
1045 assert(StartColNo
<= map
.getSourceLine().size() && "Invalid range!");
1046 assert(EndColNo
<= map
.getSourceLine().size() && "Invalid range!");
1048 // Fill the range with ~'s.
1049 StartColNo
= map
.byteToContainingColumn(StartColNo
);
1050 EndColNo
= map
.byteToContainingColumn(EndColNo
);
1052 assert(StartColNo
<= EndColNo
&& "Invalid range!");
1053 if (CaretLine
.size() < EndColNo
)
1054 CaretLine
.resize(EndColNo
,' ');
1055 std::fill(CaretLine
.begin()+StartColNo
,CaretLine
.begin()+EndColNo
,'~');
1058 static std::string
buildFixItInsertionLine(FileID FID
,
1060 const SourceColumnMap
&map
,
1061 ArrayRef
<FixItHint
> Hints
,
1062 const SourceManager
&SM
,
1063 const DiagnosticOptions
*DiagOpts
) {
1064 std::string FixItInsertionLine
;
1065 if (Hints
.empty() || !DiagOpts
->ShowFixits
)
1066 return FixItInsertionLine
;
1067 unsigned PrevHintEndCol
= 0;
1069 for (ArrayRef
<FixItHint
>::iterator I
= Hints
.begin(), E
= Hints
.end();
1071 if (!I
->CodeToInsert
.empty()) {
1072 // We have an insertion hint. Determine whether the inserted
1073 // code contains no newlines and is on the same line as the caret.
1074 std::pair
<FileID
, unsigned> HintLocInfo
1075 = SM
.getDecomposedExpansionLoc(I
->RemoveRange
.getBegin());
1076 if (FID
== HintLocInfo
.first
&&
1077 LineNo
== SM
.getLineNumber(HintLocInfo
.first
, HintLocInfo
.second
) &&
1078 StringRef(I
->CodeToInsert
).find_first_of("\n\r") == StringRef::npos
) {
1079 // Insert the new code into the line just below the code
1080 // that the user wrote.
1081 // Note: When modifying this function, be very careful about what is a
1082 // "column" (printed width, platform-dependent) and what is a
1083 // "byte offset" (SourceManager "column").
1084 unsigned HintByteOffset
1085 = SM
.getColumnNumber(HintLocInfo
.first
, HintLocInfo
.second
) - 1;
1087 // The hint must start inside the source or right at the end
1088 assert(HintByteOffset
< static_cast<unsigned>(map
.bytes())+1);
1089 unsigned HintCol
= map
.byteToContainingColumn(HintByteOffset
);
1091 // If we inserted a long previous hint, push this one forwards, and add
1092 // an extra space to show that this is not part of the previous
1093 // completion. This is sort of the best we can do when two hints appear
1096 // Note that if this hint is located immediately after the previous
1097 // hint, no space will be added, since the location is more important.
1098 if (HintCol
< PrevHintEndCol
)
1099 HintCol
= PrevHintEndCol
+ 1;
1101 // This should NOT use HintByteOffset, because the source might have
1102 // Unicode characters in earlier columns.
1103 unsigned NewFixItLineSize
= FixItInsertionLine
.size() +
1104 (HintCol
- PrevHintEndCol
) + I
->CodeToInsert
.size();
1105 if (NewFixItLineSize
> FixItInsertionLine
.size())
1106 FixItInsertionLine
.resize(NewFixItLineSize
, ' ');
1108 std::copy(I
->CodeToInsert
.begin(), I
->CodeToInsert
.end(),
1109 FixItInsertionLine
.end() - I
->CodeToInsert
.size());
1112 HintCol
+ llvm::sys::locale::columnWidth(I
->CodeToInsert
);
1117 expandTabs(FixItInsertionLine
, DiagOpts
->TabStop
);
1119 return FixItInsertionLine
;
1122 /// Emit a code snippet and caret line.
1124 /// This routine emits a single line's code snippet and caret line..
1126 /// \param Loc The location for the caret.
1127 /// \param Ranges The underlined ranges for this code snippet.
1128 /// \param Hints The FixIt hints active for this diagnostic.
1129 void TextDiagnostic::emitSnippetAndCaret(
1130 FullSourceLoc Loc
, DiagnosticsEngine::Level Level
,
1131 SmallVectorImpl
<CharSourceRange
> &Ranges
, ArrayRef
<FixItHint
> Hints
) {
1132 assert(Loc
.isValid() && "must have a valid source location here");
1133 assert(Loc
.isFileID() && "must have a file location here");
1135 // If caret diagnostics are enabled and we have location, we want to
1136 // emit the caret. However, we only do this if the location moved
1137 // from the last diagnostic, if the last diagnostic was a note that
1138 // was part of a different warning or error diagnostic, or if the
1139 // diagnostic has ranges. We don't want to emit the same caret
1140 // multiple times if one loc has multiple diagnostics.
1141 if (!DiagOpts
->ShowCarets
)
1143 if (Loc
== LastLoc
&& Ranges
.empty() && Hints
.empty() &&
1144 (LastLevel
!= DiagnosticsEngine::Note
|| Level
== LastLevel
))
1147 // Decompose the location into a FID/Offset pair.
1148 std::pair
<FileID
, unsigned> LocInfo
= Loc
.getDecomposedLoc();
1149 FileID FID
= LocInfo
.first
;
1150 const SourceManager
&SM
= Loc
.getManager();
1152 // Get information about the buffer it points into.
1153 bool Invalid
= false;
1154 StringRef BufData
= Loc
.getBufferData(&Invalid
);
1158 unsigned CaretLineNo
= Loc
.getLineNumber();
1159 unsigned CaretColNo
= Loc
.getColumnNumber();
1161 // Arbitrarily stop showing snippets when the line is too long.
1162 static const size_t MaxLineLengthToPrint
= 4096;
1163 if (CaretColNo
> MaxLineLengthToPrint
)
1166 // Find the set of lines to include.
1167 const unsigned MaxLines
= DiagOpts
->SnippetLineLimit
;
1168 std::pair
<unsigned, unsigned> Lines
= {CaretLineNo
, CaretLineNo
};
1169 for (SmallVectorImpl
<CharSourceRange
>::iterator I
= Ranges
.begin(),
1172 if (auto OptionalRange
= findLinesForRange(*I
, FID
, SM
))
1173 Lines
= maybeAddRange(Lines
, *OptionalRange
, MaxLines
);
1175 for (unsigned LineNo
= Lines
.first
; LineNo
!= Lines
.second
+ 1; ++LineNo
) {
1176 const char *BufStart
= BufData
.data();
1177 const char *BufEnd
= BufStart
+ BufData
.size();
1179 // Rewind from the current position to the start of the line.
1180 const char *LineStart
=
1182 SM
.getDecomposedLoc(SM
.translateLineCol(FID
, LineNo
, 1)).second
;
1183 if (LineStart
== BufEnd
)
1186 // Compute the line end.
1187 const char *LineEnd
= LineStart
;
1188 while (*LineEnd
!= '\n' && *LineEnd
!= '\r' && LineEnd
!= BufEnd
)
1191 // Arbitrarily stop showing snippets when the line is too long.
1192 // FIXME: Don't print any lines in this case.
1193 if (size_t(LineEnd
- LineStart
) > MaxLineLengthToPrint
)
1196 // Trim trailing null-bytes.
1197 StringRef
Line(LineStart
, LineEnd
- LineStart
);
1198 while (!Line
.empty() && Line
.back() == '\0' &&
1199 (LineNo
!= CaretLineNo
|| Line
.size() > CaretColNo
))
1200 Line
= Line
.drop_back();
1202 // Copy the line of code into an std::string for ease of manipulation.
1203 std::string
SourceLine(Line
.begin(), Line
.end());
1205 // Build the byte to column map.
1206 const SourceColumnMap
sourceColMap(SourceLine
, DiagOpts
->TabStop
);
1208 // Create a line for the caret that is filled with spaces that is the same
1209 // number of columns as the line of source code.
1210 std::string
CaretLine(sourceColMap
.columns(), ' ');
1212 // Highlight all of the characters covered by Ranges with ~ characters.
1213 for (SmallVectorImpl
<CharSourceRange
>::iterator I
= Ranges
.begin(),
1216 highlightRange(*I
, LineNo
, FID
, sourceColMap
, CaretLine
, SM
, LangOpts
);
1218 // Next, insert the caret itself.
1219 if (CaretLineNo
== LineNo
) {
1220 CaretColNo
= sourceColMap
.byteToContainingColumn(CaretColNo
- 1);
1221 if (CaretLine
.size() < CaretColNo
+ 1)
1222 CaretLine
.resize(CaretColNo
+ 1, ' ');
1223 CaretLine
[CaretColNo
] = '^';
1226 std::string FixItInsertionLine
= buildFixItInsertionLine(
1227 FID
, LineNo
, sourceColMap
, Hints
, SM
, DiagOpts
.get());
1229 // If the source line is too long for our terminal, select only the
1230 // "interesting" source region within that line.
1231 unsigned Columns
= DiagOpts
->MessageLength
;
1233 selectInterestingSourceRegion(SourceLine
, CaretLine
, FixItInsertionLine
,
1234 Columns
, sourceColMap
);
1236 // If we are in -fdiagnostics-print-source-range-info mode, we are trying
1237 // to produce easily machine parsable output. Add a space before the
1238 // source line and the caret to make it trivial to tell the main diagnostic
1239 // line from what the user is intended to see.
1240 if (DiagOpts
->ShowSourceRanges
) {
1241 SourceLine
= ' ' + SourceLine
;
1242 CaretLine
= ' ' + CaretLine
;
1245 // Finally, remove any blank spaces from the end of CaretLine.
1246 while (!CaretLine
.empty() && CaretLine
[CaretLine
.size() - 1] == ' ')
1247 CaretLine
.erase(CaretLine
.end() - 1);
1249 // Emit what we have computed.
1250 emitSnippet(SourceLine
);
1252 if (!CaretLine
.empty()) {
1253 if (DiagOpts
->ShowColors
)
1254 OS
.changeColor(caretColor
, true);
1255 OS
<< CaretLine
<< '\n';
1256 if (DiagOpts
->ShowColors
)
1260 if (!FixItInsertionLine
.empty()) {
1261 if (DiagOpts
->ShowColors
)
1262 // Print fixit line in color
1263 OS
.changeColor(fixitColor
, false);
1264 if (DiagOpts
->ShowSourceRanges
)
1266 OS
<< FixItInsertionLine
<< '\n';
1267 if (DiagOpts
->ShowColors
)
1272 // Print out any parseable fixit information requested by the options.
1273 emitParseableFixits(Hints
, SM
);
1276 void TextDiagnostic::emitSnippet(StringRef line
) {
1282 std::string to_print
;
1283 bool print_reversed
= false;
1285 while (i
<line
.size()) {
1286 std::pair
<SmallString
<16>,bool> res
1287 = printableTextForNextCharacter(line
, &i
, DiagOpts
->TabStop
);
1288 bool was_printable
= res
.second
;
1290 if (DiagOpts
->ShowColors
&& was_printable
== print_reversed
) {
1295 if (DiagOpts
->ShowColors
)
1299 print_reversed
= !was_printable
;
1300 to_print
+= res
.first
.str();
1303 if (print_reversed
&& DiagOpts
->ShowColors
)
1306 if (print_reversed
&& DiagOpts
->ShowColors
)
1312 void TextDiagnostic::emitParseableFixits(ArrayRef
<FixItHint
> Hints
,
1313 const SourceManager
&SM
) {
1314 if (!DiagOpts
->ShowParseableFixits
)
1317 // We follow FixItRewriter's example in not (yet) handling
1318 // fix-its in macros.
1319 for (ArrayRef
<FixItHint
>::iterator I
= Hints
.begin(), E
= Hints
.end();
1321 if (I
->RemoveRange
.isInvalid() ||
1322 I
->RemoveRange
.getBegin().isMacroID() ||
1323 I
->RemoveRange
.getEnd().isMacroID())
1327 for (ArrayRef
<FixItHint
>::iterator I
= Hints
.begin(), E
= Hints
.end();
1329 SourceLocation BLoc
= I
->RemoveRange
.getBegin();
1330 SourceLocation ELoc
= I
->RemoveRange
.getEnd();
1332 std::pair
<FileID
, unsigned> BInfo
= SM
.getDecomposedLoc(BLoc
);
1333 std::pair
<FileID
, unsigned> EInfo
= SM
.getDecomposedLoc(ELoc
);
1335 // Adjust for token ranges.
1336 if (I
->RemoveRange
.isTokenRange())
1337 EInfo
.second
+= Lexer::MeasureTokenLength(ELoc
, SM
, LangOpts
);
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())
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
)
1352 OS
.write_escaped(I
->CodeToInsert
);