150
|
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
|
|
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"
|
|
22 #include <algorithm>
|
|
23
|
|
24 using namespace clang;
|
|
25
|
|
26 static const enum raw_ostream::Colors noteColor =
|
|
27 raw_ostream::BLACK;
|
|
28 static const enum raw_ostream::Colors remarkColor =
|
|
29 raw_ostream::BLUE;
|
|
30 static const enum raw_ostream::Colors fixitColor =
|
|
31 raw_ostream::GREEN;
|
|
32 static const enum raw_ostream::Colors caretColor =
|
|
33 raw_ostream::GREEN;
|
|
34 static const enum raw_ostream::Colors warningColor =
|
|
35 raw_ostream::MAGENTA;
|
|
36 static const enum raw_ostream::Colors templateColor =
|
|
37 raw_ostream::CYAN;
|
|
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;
|
|
43
|
|
44 /// Add highlights to differences in template strings.
|
|
45 static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
|
|
46 bool &Normal, bool Bold) {
|
|
47 while (1) {
|
|
48 size_t Pos = Str.find(ToggleHighlight);
|
|
49 OS << Str.slice(0, Pos);
|
|
50 if (Pos == StringRef::npos)
|
|
51 break;
|
|
52
|
|
53 Str = Str.substr(Pos + 1);
|
|
54 if (Normal)
|
|
55 OS.changeColor(templateColor, true);
|
|
56 else {
|
|
57 OS.resetColor();
|
|
58 if (Bold)
|
|
59 OS.changeColor(savedColor, true);
|
|
60 }
|
|
61 Normal = !Normal;
|
|
62 }
|
|
63 }
|
|
64
|
|
65 /// Number of spaces to indent when word-wrapping.
|
|
66 const unsigned WordWrapIndentation = 6;
|
|
67
|
|
68 static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
|
|
69 int bytes = 0;
|
|
70 while (0<i) {
|
|
71 if (SourceLine[--i]=='\t')
|
|
72 break;
|
|
73 ++bytes;
|
|
74 }
|
|
75 return bytes;
|
|
76 }
|
|
77
|
|
78 /// returns a printable representation of first item from input range
|
|
79 ///
|
|
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'.
|
|
83 ///
|
|
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'.
|
|
88 ///
|
|
89 /// \note The index is updated to be used with a subsequent call to
|
|
90 /// printableTextForNextCharacter.
|
|
91 ///
|
|
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)
|
|
96 ///
|
|
97 static std::pair<SmallString<16>, bool>
|
|
98 printableTextForNextCharacter(StringRef SourceLine, size_t *i,
|
|
99 unsigned TabStop) {
|
|
100 assert(i && "i must not be null");
|
|
101 assert(*i<SourceLine.size() && "must point to a valid index");
|
|
102
|
|
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");
|
|
110 ++(*i);
|
|
111
|
|
112 SmallString<16> expandedTab;
|
|
113 expandedTab.assign(NumSpaces, ' ');
|
|
114 return std::make_pair(expandedTab, true);
|
|
115 }
|
|
116
|
|
117 unsigned char const *begin, *end;
|
|
118 begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i));
|
|
119 end = begin + (SourceLine.size() - *i);
|
|
120
|
|
121 if (llvm::isLegalUTF8Sequence(begin, end)) {
|
|
122 llvm::UTF32 c;
|
|
123 llvm::UTF32 *cptr = &c;
|
|
124 unsigned char const *original_begin = begin;
|
|
125 unsigned char const *cp_end =
|
|
126 begin + llvm::getNumBytesForUTF8(SourceLine[*i]);
|
|
127
|
|
128 llvm::ConversionResult res = llvm::ConvertUTF8toUTF32(
|
|
129 &begin, cp_end, &cptr, cptr + 1, llvm::strictConversion);
|
|
130 (void)res;
|
|
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;
|
|
135
|
|
136 if (!llvm::sys::locale::isPrint(c)) {
|
|
137 // If next character is valid UTF-8, but not printable
|
|
138 SmallString<16> expandedCP("<U+>");
|
|
139 while (c) {
|
|
140 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16));
|
|
141 c/=16;
|
|
142 }
|
|
143 while (expandedCP.size() < 8)
|
|
144 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0));
|
|
145 return std::make_pair(expandedCP, false);
|
|
146 }
|
|
147
|
|
148 // If next character is valid UTF-8, and printable
|
|
149 return std::make_pair(SmallString<16>(original_begin, cp_end), true);
|
|
150
|
|
151 }
|
|
152
|
|
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);
|
|
158 ++(*i);
|
|
159 return std::make_pair(expandedByte, false);
|
|
160 }
|
|
161
|
|
162 static void expandTabs(std::string &SourceLine, unsigned TabStop) {
|
|
163 size_t i = SourceLine.size();
|
|
164 while (i>0) {
|
|
165 i--;
|
|
166 if (SourceLine[i]!='\t')
|
|
167 continue;
|
|
168 size_t tmp_i = i;
|
|
169 std::pair<SmallString<16>,bool> res
|
|
170 = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop);
|
|
171 SourceLine.replace(i, 1, res.first.c_str());
|
|
172 }
|
|
173 }
|
|
174
|
|
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).
|
|
178 ///
|
|
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
|
|
182 /// more than once.
|
|
183 ///
|
|
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.
|
|
187 ///
|
|
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
|
|
190 ///
|
|
191 /// example: (given a tabstop of 8)
|
|
192 ///
|
|
193 /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
|
|
194 ///
|
|
195 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
|
|
196 /// display)
|
|
197 static void byteToColumn(StringRef SourceLine, unsigned TabStop,
|
|
198 SmallVectorImpl<int> &out) {
|
|
199 out.clear();
|
|
200
|
|
201 if (SourceLine.empty()) {
|
|
202 out.resize(1u,0);
|
|
203 return;
|
|
204 }
|
|
205
|
|
206 out.resize(SourceLine.size()+1, -1);
|
|
207
|
|
208 int columns = 0;
|
|
209 size_t i = 0;
|
|
210 while (i<SourceLine.size()) {
|
|
211 out[i] = columns;
|
|
212 std::pair<SmallString<16>,bool> res
|
|
213 = printableTextForNextCharacter(SourceLine, &i, TabStop);
|
|
214 columns += llvm::sys::locale::columnWidth(res.first);
|
|
215 }
|
|
216 out.back() = columns;
|
|
217 }
|
|
218
|
|
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()
|
|
222 ///
|
|
223 /// The last element in the array is the number of bytes in the source string
|
|
224 ///
|
|
225 /// example: (given a tabstop of 8)
|
|
226 ///
|
|
227 /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
|
|
228 ///
|
|
229 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
|
|
230 /// display)
|
|
231 static void columnToByte(StringRef SourceLine, unsigned TabStop,
|
|
232 SmallVectorImpl<int> &out) {
|
|
233 out.clear();
|
|
234
|
|
235 if (SourceLine.empty()) {
|
|
236 out.resize(1u, 0);
|
|
237 return;
|
|
238 }
|
|
239
|
|
240 int columns = 0;
|
|
241 size_t i = 0;
|
|
242 while (i<SourceLine.size()) {
|
|
243 out.resize(columns+1, -1);
|
|
244 out.back() = i;
|
|
245 std::pair<SmallString<16>,bool> res
|
|
246 = printableTextForNextCharacter(SourceLine, &i, TabStop);
|
|
247 columns += llvm::sys::locale::columnWidth(res.first);
|
|
248 }
|
|
249 out.resize(columns+1, -1);
|
|
250 out.back() = i;
|
|
251 }
|
|
252
|
|
253 namespace {
|
|
254 struct SourceColumnMap {
|
|
255 SourceColumnMap(StringRef SourceLine, unsigned TabStop)
|
|
256 : m_SourceLine(SourceLine) {
|
|
257
|
|
258 ::byteToColumn(SourceLine, TabStop, m_byteToColumn);
|
|
259 ::columnToByte(SourceLine, TabStop, m_columnToByte);
|
|
260
|
|
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());
|
|
267 }
|
|
268 int columns() const { return m_byteToColumn.back(); }
|
|
269 int bytes() const { return m_columnToByte.back(); }
|
|
270
|
|
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];
|
|
276 }
|
|
277
|
|
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)
|
|
282 --N;
|
|
283 return m_byteToColumn[N];
|
|
284 }
|
|
285
|
|
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];
|
|
292 }
|
|
293
|
|
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) {}
|
|
298 return N;
|
|
299 }
|
|
300
|
|
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) {}
|
|
305 return N;
|
|
306 }
|
|
307
|
|
308 StringRef getSourceLine() const {
|
|
309 return m_SourceLine;
|
|
310 }
|
|
311
|
|
312 private:
|
|
313 const std::string m_SourceLine;
|
|
314 SmallVector<int,200> m_byteToColumn;
|
|
315 SmallVector<int,200> m_columnToByte;
|
|
316 };
|
|
317 } // end anonymous namespace
|
|
318
|
|
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,
|
|
324 unsigned Columns,
|
|
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)
|
|
332 return;
|
|
333
|
|
334 // No special characters are allowed in CaretLine.
|
|
335 assert(CaretLine.end() ==
|
|
336 llvm::find_if(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.
|
|
514 static 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.
|
|
526 static 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.
|
|
545 static 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.
|
|
612 static 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
|
|
666 TextDiagnostic::TextDiagnostic(raw_ostream &OS,
|
|
667 const LangOptions &LangOpts,
|
|
668 DiagnosticOptions *DiagOpts)
|
|
669 : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {}
|
|
670
|
|
671 TextDiagnostic::~TextDiagnostic() {}
|
|
672
|
|
673 void TextDiagnostic::emitDiagnosticMessage(
|
|
674 FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
|
|
675 StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
|
|
676 DiagOrStoredDiag D) {
|
|
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,
|
|
688 DiagOpts->CLFallbackMode);
|
|
689 printDiagnosticMessage(OS,
|
|
690 /*IsSupplemental*/ Level == DiagnosticsEngine::Note,
|
|
691 Message, OS.tell() - StartOfLocationInfo,
|
|
692 DiagOpts->MessageLength, DiagOpts->ShowColors);
|
|
693 }
|
|
694
|
|
695 /*static*/ void
|
|
696 TextDiagnostic::printDiagnosticLevel(raw_ostream &OS,
|
|
697 DiagnosticsEngine::Level Level,
|
|
698 bool ShowColors,
|
|
699 bool CLFallbackMode) {
|
|
700 if (ShowColors) {
|
|
701 // Print diagnostic category in bold and color
|
|
702 switch (Level) {
|
|
703 case DiagnosticsEngine::Ignored:
|
|
704 llvm_unreachable("Invalid diagnostic type");
|
|
705 case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break;
|
|
706 case DiagnosticsEngine::Remark: OS.changeColor(remarkColor, true); break;
|
|
707 case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break;
|
|
708 case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break;
|
|
709 case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break;
|
|
710 }
|
|
711 }
|
|
712
|
|
713 switch (Level) {
|
|
714 case DiagnosticsEngine::Ignored:
|
|
715 llvm_unreachable("Invalid diagnostic type");
|
|
716 case DiagnosticsEngine::Note: OS << "note"; break;
|
|
717 case DiagnosticsEngine::Remark: OS << "remark"; break;
|
|
718 case DiagnosticsEngine::Warning: OS << "warning"; break;
|
|
719 case DiagnosticsEngine::Error: OS << "error"; break;
|
|
720 case DiagnosticsEngine::Fatal: OS << "fatal error"; break;
|
|
721 }
|
|
722
|
|
723 // In clang-cl /fallback mode, print diagnostics as "error(clang):". This
|
|
724 // makes it more clear whether a message is coming from clang or cl.exe,
|
|
725 // and it prevents MSBuild from concluding that the build failed just because
|
|
726 // there is an "error:" in the output.
|
|
727 if (CLFallbackMode)
|
|
728 OS << "(clang)";
|
|
729
|
|
730 OS << ": ";
|
|
731
|
|
732 if (ShowColors)
|
|
733 OS.resetColor();
|
|
734 }
|
|
735
|
|
736 /*static*/
|
|
737 void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
|
|
738 bool IsSupplemental,
|
|
739 StringRef Message,
|
|
740 unsigned CurrentColumn,
|
|
741 unsigned Columns, bool ShowColors) {
|
|
742 bool Bold = false;
|
|
743 if (ShowColors && !IsSupplemental) {
|
|
744 // Print primary diagnostic messages in bold and without color, to visually
|
|
745 // indicate the transition from continuation notes and other output.
|
|
746 OS.changeColor(savedColor, true);
|
|
747 Bold = true;
|
|
748 }
|
|
749
|
|
750 if (Columns)
|
|
751 printWordWrapped(OS, Message, Columns, CurrentColumn, Bold);
|
|
752 else {
|
|
753 bool Normal = true;
|
|
754 applyTemplateHighlighting(OS, Message, Normal, Bold);
|
|
755 assert(Normal && "Formatting should have returned to normal");
|
|
756 }
|
|
757
|
|
758 if (ShowColors)
|
|
759 OS.resetColor();
|
|
760 OS << '\n';
|
|
761 }
|
|
762
|
|
763 void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) {
|
|
764 #ifdef _WIN32
|
|
765 SmallString<4096> TmpFilename;
|
|
766 #endif
|
|
767 if (DiagOpts->AbsolutePath) {
|
|
768 auto File = SM.getFileManager().getFile(Filename);
|
|
769 if (File) {
|
|
770 // We want to print a simplified absolute path, i. e. without "dots".
|
|
771 //
|
|
772 // The hardest part here are the paths like "<part1>/<link>/../<part2>".
|
|
773 // On Unix-like systems, we cannot just collapse "<link>/..", because
|
|
774 // paths are resolved sequentially, and, thereby, the path
|
|
775 // "<part1>/<part2>" may point to a different location. That is why
|
|
776 // we use FileManager::getCanonicalName(), which expands all indirections
|
|
777 // with llvm::sys::fs::real_path() and caches the result.
|
|
778 //
|
|
779 // On the other hand, it would be better to preserve as much of the
|
|
780 // original path as possible, because that helps a user to recognize it.
|
|
781 // real_path() expands all links, which sometimes too much. Luckily,
|
|
782 // on Windows we can just use llvm::sys::path::remove_dots(), because,
|
|
783 // on that system, both aforementioned paths point to the same place.
|
|
784 #ifdef _WIN32
|
|
785 TmpFilename = (*File)->getName();
|
|
786 llvm::sys::fs::make_absolute(TmpFilename);
|
|
787 llvm::sys::path::native(TmpFilename);
|
|
788 llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
|
|
789 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
|
|
790 #else
|
|
791 Filename = SM.getFileManager().getCanonicalName(*File);
|
|
792 #endif
|
|
793 }
|
|
794 }
|
|
795
|
|
796 OS << Filename;
|
|
797 }
|
|
798
|
|
799 /// Print out the file/line/column information and include trace.
|
|
800 ///
|
|
801 /// This method handlen the emission of the diagnostic location information.
|
|
802 /// This includes extracting as much location information as is present for
|
|
803 /// the diagnostic and printing it, as well as any include stack or source
|
|
804 /// ranges necessary.
|
|
805 void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
|
|
806 DiagnosticsEngine::Level Level,
|
|
807 ArrayRef<CharSourceRange> Ranges) {
|
|
808 if (PLoc.isInvalid()) {
|
|
809 // At least print the file name if available:
|
|
810 FileID FID = Loc.getFileID();
|
|
811 if (FID.isValid()) {
|
|
812 const FileEntry *FE = Loc.getFileEntry();
|
|
813 if (FE && FE->isValid()) {
|
|
814 emitFilename(FE->getName(), Loc.getManager());
|
|
815 OS << ": ";
|
|
816 }
|
|
817 }
|
|
818 return;
|
|
819 }
|
|
820 unsigned LineNo = PLoc.getLine();
|
|
821
|
|
822 if (!DiagOpts->ShowLocation)
|
|
823 return;
|
|
824
|
|
825 if (DiagOpts->ShowColors)
|
|
826 OS.changeColor(savedColor, true);
|
|
827
|
|
828 emitFilename(PLoc.getFilename(), Loc.getManager());
|
|
829 switch (DiagOpts->getFormat()) {
|
|
830 case DiagnosticOptions::Clang: OS << ':' << LineNo; break;
|
|
831 case DiagnosticOptions::MSVC: OS << '(' << LineNo; break;
|
|
832 case DiagnosticOptions::Vi: OS << " +" << LineNo; break;
|
|
833 }
|
|
834
|
|
835 if (DiagOpts->ShowColumn)
|
|
836 // Compute the column number.
|
|
837 if (unsigned ColNo = PLoc.getColumn()) {
|
|
838 if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) {
|
|
839 OS << ',';
|
|
840 // Visual Studio 2010 or earlier expects column number to be off by one
|
|
841 if (LangOpts.MSCompatibilityVersion &&
|
|
842 !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
|
|
843 ColNo--;
|
|
844 } else
|
|
845 OS << ':';
|
|
846 OS << ColNo;
|
|
847 }
|
|
848 switch (DiagOpts->getFormat()) {
|
|
849 case DiagnosticOptions::Clang:
|
|
850 case DiagnosticOptions::Vi: OS << ':'; break;
|
|
851 case DiagnosticOptions::MSVC:
|
|
852 // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the
|
|
853 // space and prints 'file(4): error'.
|
|
854 OS << ')';
|
|
855 if (LangOpts.MSCompatibilityVersion &&
|
|
856 !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
|
|
857 OS << ' ';
|
|
858 OS << ':';
|
|
859 break;
|
|
860 }
|
|
861
|
|
862 if (DiagOpts->ShowSourceRanges && !Ranges.empty()) {
|
|
863 FileID CaretFileID = Loc.getExpansionLoc().getFileID();
|
|
864 bool PrintedRange = false;
|
|
865
|
|
866 for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
|
|
867 RE = Ranges.end();
|
|
868 RI != RE; ++RI) {
|
|
869 // Ignore invalid ranges.
|
|
870 if (!RI->isValid()) continue;
|
|
871
|
|
872 auto &SM = Loc.getManager();
|
|
873 SourceLocation B = SM.getExpansionLoc(RI->getBegin());
|
|
874 CharSourceRange ERange = SM.getExpansionRange(RI->getEnd());
|
|
875 SourceLocation E = ERange.getEnd();
|
|
876 bool IsTokenRange = ERange.isTokenRange();
|
|
877
|
|
878 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
|
|
879 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
|
|
880
|
|
881 // If the start or end of the range is in another file, just discard
|
|
882 // it.
|
|
883 if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
|
|
884 continue;
|
|
885
|
|
886 // Add in the length of the token, so that we cover multi-char
|
|
887 // tokens.
|
|
888 unsigned TokSize = 0;
|
|
889 if (IsTokenRange)
|
|
890 TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
|
|
891
|
|
892 FullSourceLoc BF(B, SM), EF(E, SM);
|
|
893 OS << '{'
|
|
894 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-'
|
|
895 << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize)
|
|
896 << '}';
|
|
897 PrintedRange = true;
|
|
898 }
|
|
899
|
|
900 if (PrintedRange)
|
|
901 OS << ':';
|
|
902 }
|
|
903 OS << ' ';
|
|
904 }
|
|
905
|
|
906 void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
|
|
907 if (DiagOpts->ShowLocation && PLoc.isValid())
|
|
908 OS << "In file included from " << PLoc.getFilename() << ':'
|
|
909 << PLoc.getLine() << ":\n";
|
|
910 else
|
|
911 OS << "In included file:\n";
|
|
912 }
|
|
913
|
|
914 void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
|
915 StringRef ModuleName) {
|
|
916 if (DiagOpts->ShowLocation && PLoc.isValid())
|
|
917 OS << "In module '" << ModuleName << "' imported from "
|
|
918 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
|
|
919 else
|
|
920 OS << "In module '" << ModuleName << "':\n";
|
|
921 }
|
|
922
|
|
923 void TextDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc,
|
|
924 PresumedLoc PLoc,
|
|
925 StringRef ModuleName) {
|
|
926 if (DiagOpts->ShowLocation && PLoc.isValid())
|
|
927 OS << "While building module '" << ModuleName << "' imported from "
|
|
928 << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
|
|
929 else
|
|
930 OS << "While building module '" << ModuleName << "':\n";
|
|
931 }
|
|
932
|
|
933 /// Find the suitable set of lines to show to include a set of ranges.
|
|
934 static llvm::Optional<std::pair<unsigned, unsigned>>
|
|
935 findLinesForRange(const CharSourceRange &R, FileID FID,
|
|
936 const SourceManager &SM) {
|
|
937 if (!R.isValid()) return None;
|
|
938
|
|
939 SourceLocation Begin = R.getBegin();
|
|
940 SourceLocation End = R.getEnd();
|
|
941 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID)
|
|
942 return None;
|
|
943
|
|
944 return std::make_pair(SM.getExpansionLineNumber(Begin),
|
|
945 SM.getExpansionLineNumber(End));
|
|
946 }
|
|
947
|
|
948 /// Add as much of range B into range A as possible without exceeding a maximum
|
|
949 /// size of MaxRange. Ranges are inclusive.
|
|
950 static std::pair<unsigned, unsigned>
|
|
951 maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
|
|
952 unsigned MaxRange) {
|
|
953 // If A is already the maximum size, we're done.
|
|
954 unsigned Slack = MaxRange - (A.second - A.first + 1);
|
|
955 if (Slack == 0)
|
|
956 return A;
|
|
957
|
|
958 // Easy case: merge succeeds within MaxRange.
|
|
959 unsigned Min = std::min(A.first, B.first);
|
|
960 unsigned Max = std::max(A.second, B.second);
|
|
961 if (Max - Min + 1 <= MaxRange)
|
|
962 return {Min, Max};
|
|
963
|
|
964 // If we can't reach B from A within MaxRange, there's nothing to do.
|
|
965 // Don't add lines to the range that contain nothing interesting.
|
|
966 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
|
|
967 (B.second < A.second && A.second - B.second + 1 > MaxRange))
|
|
968 return A;
|
|
969
|
|
970 // Otherwise, expand A towards B to produce a range of size MaxRange. We
|
|
971 // attempt to expand by the same amount in both directions if B strictly
|
|
972 // contains A.
|
|
973
|
|
974 // Expand downwards by up to half the available amount, then upwards as
|
|
975 // much as possible, then downwards as much as possible.
|
|
976 A.second = std::min(A.second + (Slack + 1) / 2, Max);
|
|
977 Slack = MaxRange - (A.second - A.first + 1);
|
|
978 A.first = std::max(Min + Slack, A.first) - Slack;
|
|
979 A.second = std::min(A.first + MaxRange - 1, Max);
|
|
980 return A;
|
|
981 }
|
|
982
|
|
983 /// Highlight a SourceRange (with ~'s) for any characters on LineNo.
|
|
984 static void highlightRange(const CharSourceRange &R,
|
|
985 unsigned LineNo, FileID FID,
|
|
986 const SourceColumnMap &map,
|
|
987 std::string &CaretLine,
|
|
988 const SourceManager &SM,
|
|
989 const LangOptions &LangOpts) {
|
|
990 if (!R.isValid()) return;
|
|
991
|
|
992 SourceLocation Begin = R.getBegin();
|
|
993 SourceLocation End = R.getEnd();
|
|
994
|
|
995 unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
|
|
996 if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
|
|
997 return; // No intersection.
|
|
998
|
|
999 unsigned EndLineNo = SM.getExpansionLineNumber(End);
|
|
1000 if (EndLineNo < LineNo || SM.getFileID(End) != FID)
|
|
1001 return; // No intersection.
|
|
1002
|
|
1003 // Compute the column number of the start.
|
|
1004 unsigned StartColNo = 0;
|
|
1005 if (StartLineNo == LineNo) {
|
|
1006 StartColNo = SM.getExpansionColumnNumber(Begin);
|
|
1007 if (StartColNo) --StartColNo; // Zero base the col #.
|
|
1008 }
|
|
1009
|
|
1010 // Compute the column number of the end.
|
|
1011 unsigned EndColNo = map.getSourceLine().size();
|
|
1012 if (EndLineNo == LineNo) {
|
|
1013 EndColNo = SM.getExpansionColumnNumber(End);
|
|
1014 if (EndColNo) {
|
|
1015 --EndColNo; // Zero base the col #.
|
|
1016
|
|
1017 // Add in the length of the token, so that we cover multi-char tokens if
|
|
1018 // this is a token range.
|
|
1019 if (R.isTokenRange())
|
|
1020 EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
|
|
1021 } else {
|
|
1022 EndColNo = CaretLine.size();
|
|
1023 }
|
|
1024 }
|
|
1025
|
|
1026 assert(StartColNo <= EndColNo && "Invalid range!");
|
|
1027
|
|
1028 // Check that a token range does not highlight only whitespace.
|
|
1029 if (R.isTokenRange()) {
|
|
1030 // Pick the first non-whitespace column.
|
|
1031 while (StartColNo < map.getSourceLine().size() &&
|
|
1032 (map.getSourceLine()[StartColNo] == ' ' ||
|
|
1033 map.getSourceLine()[StartColNo] == '\t'))
|
|
1034 StartColNo = map.startOfNextColumn(StartColNo);
|
|
1035
|
|
1036 // Pick the last non-whitespace column.
|
|
1037 if (EndColNo > map.getSourceLine().size())
|
|
1038 EndColNo = map.getSourceLine().size();
|
|
1039 while (EndColNo &&
|
|
1040 (map.getSourceLine()[EndColNo-1] == ' ' ||
|
|
1041 map.getSourceLine()[EndColNo-1] == '\t'))
|
|
1042 EndColNo = map.startOfPreviousColumn(EndColNo);
|
|
1043
|
|
1044 // If the start/end passed each other, then we are trying to highlight a
|
|
1045 // range that just exists in whitespace. That most likely means we have
|
|
1046 // a multi-line highlighting range that covers a blank line.
|
|
1047 if (StartColNo > EndColNo) {
|
|
1048 assert(StartLineNo != EndLineNo && "trying to highlight whitespace");
|
|
1049 StartColNo = EndColNo;
|
|
1050 }
|
|
1051 }
|
|
1052
|
|
1053 assert(StartColNo <= map.getSourceLine().size() && "Invalid range!");
|
|
1054 assert(EndColNo <= map.getSourceLine().size() && "Invalid range!");
|
|
1055
|
|
1056 // Fill the range with ~'s.
|
|
1057 StartColNo = map.byteToContainingColumn(StartColNo);
|
|
1058 EndColNo = map.byteToContainingColumn(EndColNo);
|
|
1059
|
|
1060 assert(StartColNo <= EndColNo && "Invalid range!");
|
|
1061 if (CaretLine.size() < EndColNo)
|
|
1062 CaretLine.resize(EndColNo,' ');
|
|
1063 std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~');
|
|
1064 }
|
|
1065
|
|
1066 static std::string buildFixItInsertionLine(FileID FID,
|
|
1067 unsigned LineNo,
|
|
1068 const SourceColumnMap &map,
|
|
1069 ArrayRef<FixItHint> Hints,
|
|
1070 const SourceManager &SM,
|
|
1071 const DiagnosticOptions *DiagOpts) {
|
|
1072 std::string FixItInsertionLine;
|
|
1073 if (Hints.empty() || !DiagOpts->ShowFixits)
|
|
1074 return FixItInsertionLine;
|
|
1075 unsigned PrevHintEndCol = 0;
|
|
1076
|
|
1077 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
|
|
1078 I != E; ++I) {
|
|
1079 if (!I->CodeToInsert.empty()) {
|
|
1080 // We have an insertion hint. Determine whether the inserted
|
|
1081 // code contains no newlines and is on the same line as the caret.
|
|
1082 std::pair<FileID, unsigned> HintLocInfo
|
|
1083 = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
|
|
1084 if (FID == HintLocInfo.first &&
|
|
1085 LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
|
|
1086 StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
|
|
1087 // Insert the new code into the line just below the code
|
|
1088 // that the user wrote.
|
|
1089 // Note: When modifying this function, be very careful about what is a
|
|
1090 // "column" (printed width, platform-dependent) and what is a
|
|
1091 // "byte offset" (SourceManager "column").
|
|
1092 unsigned HintByteOffset
|
|
1093 = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
|
|
1094
|
|
1095 // The hint must start inside the source or right at the end
|
|
1096 assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1);
|
|
1097 unsigned HintCol = map.byteToContainingColumn(HintByteOffset);
|
|
1098
|
|
1099 // If we inserted a long previous hint, push this one forwards, and add
|
|
1100 // an extra space to show that this is not part of the previous
|
|
1101 // completion. This is sort of the best we can do when two hints appear
|
|
1102 // to overlap.
|
|
1103 //
|
|
1104 // Note that if this hint is located immediately after the previous
|
|
1105 // hint, no space will be added, since the location is more important.
|
|
1106 if (HintCol < PrevHintEndCol)
|
|
1107 HintCol = PrevHintEndCol + 1;
|
|
1108
|
|
1109 // This should NOT use HintByteOffset, because the source might have
|
|
1110 // Unicode characters in earlier columns.
|
|
1111 unsigned NewFixItLineSize = FixItInsertionLine.size() +
|
|
1112 (HintCol - PrevHintEndCol) + I->CodeToInsert.size();
|
|
1113 if (NewFixItLineSize > FixItInsertionLine.size())
|
|
1114 FixItInsertionLine.resize(NewFixItLineSize, ' ');
|
|
1115
|
|
1116 std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
|
|
1117 FixItInsertionLine.end() - I->CodeToInsert.size());
|
|
1118
|
|
1119 PrevHintEndCol =
|
|
1120 HintCol + llvm::sys::locale::columnWidth(I->CodeToInsert);
|
|
1121 }
|
|
1122 }
|
|
1123 }
|
|
1124
|
|
1125 expandTabs(FixItInsertionLine, DiagOpts->TabStop);
|
|
1126
|
|
1127 return FixItInsertionLine;
|
|
1128 }
|
|
1129
|
|
1130 /// Emit a code snippet and caret line.
|
|
1131 ///
|
|
1132 /// This routine emits a single line's code snippet and caret line..
|
|
1133 ///
|
|
1134 /// \param Loc The location for the caret.
|
|
1135 /// \param Ranges The underlined ranges for this code snippet.
|
|
1136 /// \param Hints The FixIt hints active for this diagnostic.
|
|
1137 void TextDiagnostic::emitSnippetAndCaret(
|
|
1138 FullSourceLoc Loc, DiagnosticsEngine::Level Level,
|
|
1139 SmallVectorImpl<CharSourceRange> &Ranges, ArrayRef<FixItHint> Hints) {
|
|
1140 assert(Loc.isValid() && "must have a valid source location here");
|
|
1141 assert(Loc.isFileID() && "must have a file location here");
|
|
1142
|
|
1143 // If caret diagnostics are enabled and we have location, we want to
|
|
1144 // emit the caret. However, we only do this if the location moved
|
|
1145 // from the last diagnostic, if the last diagnostic was a note that
|
|
1146 // was part of a different warning or error diagnostic, or if the
|
|
1147 // diagnostic has ranges. We don't want to emit the same caret
|
|
1148 // multiple times if one loc has multiple diagnostics.
|
|
1149 if (!DiagOpts->ShowCarets)
|
|
1150 return;
|
|
1151 if (Loc == LastLoc && Ranges.empty() && Hints.empty() &&
|
|
1152 (LastLevel != DiagnosticsEngine::Note || Level == LastLevel))
|
|
1153 return;
|
|
1154
|
|
1155 // Decompose the location into a FID/Offset pair.
|
|
1156 std::pair<FileID, unsigned> LocInfo = Loc.getDecomposedLoc();
|
|
1157 FileID FID = LocInfo.first;
|
|
1158 const SourceManager &SM = Loc.getManager();
|
|
1159
|
|
1160 // Get information about the buffer it points into.
|
|
1161 bool Invalid = false;
|
|
1162 StringRef BufData = Loc.getBufferData(&Invalid);
|
|
1163 if (Invalid)
|
|
1164 return;
|
|
1165
|
|
1166 unsigned CaretLineNo = Loc.getLineNumber();
|
|
1167 unsigned CaretColNo = Loc.getColumnNumber();
|
|
1168
|
|
1169 // Arbitrarily stop showing snippets when the line is too long.
|
|
1170 static const size_t MaxLineLengthToPrint = 4096;
|
|
1171 if (CaretColNo > MaxLineLengthToPrint)
|
|
1172 return;
|
|
1173
|
|
1174 // Find the set of lines to include.
|
|
1175 const unsigned MaxLines = DiagOpts->SnippetLineLimit;
|
|
1176 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
|
|
1177 for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
|
|
1178 E = Ranges.end();
|
|
1179 I != E; ++I)
|
|
1180 if (auto OptionalRange = findLinesForRange(*I, FID, SM))
|
|
1181 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines);
|
|
1182
|
|
1183 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1; ++LineNo) {
|
|
1184 const char *BufStart = BufData.data();
|
|
1185 const char *BufEnd = BufStart + BufData.size();
|
|
1186
|
|
1187 // Rewind from the current position to the start of the line.
|
|
1188 const char *LineStart =
|
|
1189 BufStart +
|
|
1190 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second;
|
|
1191 if (LineStart == BufEnd)
|
|
1192 break;
|
|
1193
|
|
1194 // Compute the line end.
|
|
1195 const char *LineEnd = LineStart;
|
|
1196 while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd)
|
|
1197 ++LineEnd;
|
|
1198
|
|
1199 // Arbitrarily stop showing snippets when the line is too long.
|
|
1200 // FIXME: Don't print any lines in this case.
|
|
1201 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
|
|
1202 return;
|
|
1203
|
|
1204 // Trim trailing null-bytes.
|
|
1205 StringRef Line(LineStart, LineEnd - LineStart);
|
|
1206 while (!Line.empty() && Line.back() == '\0' &&
|
|
1207 (LineNo != CaretLineNo || Line.size() > CaretColNo))
|
|
1208 Line = Line.drop_back();
|
|
1209
|
|
1210 // Copy the line of code into an std::string for ease of manipulation.
|
|
1211 std::string SourceLine(Line.begin(), Line.end());
|
|
1212
|
|
1213 // Build the byte to column map.
|
|
1214 const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop);
|
|
1215
|
|
1216 // Create a line for the caret that is filled with spaces that is the same
|
|
1217 // number of columns as the line of source code.
|
|
1218 std::string CaretLine(sourceColMap.columns(), ' ');
|
|
1219
|
|
1220 // Highlight all of the characters covered by Ranges with ~ characters.
|
|
1221 for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
|
|
1222 E = Ranges.end();
|
|
1223 I != E; ++I)
|
|
1224 highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts);
|
|
1225
|
|
1226 // Next, insert the caret itself.
|
|
1227 if (CaretLineNo == LineNo) {
|
|
1228 CaretColNo = sourceColMap.byteToContainingColumn(CaretColNo - 1);
|
|
1229 if (CaretLine.size() < CaretColNo + 1)
|
|
1230 CaretLine.resize(CaretColNo + 1, ' ');
|
|
1231 CaretLine[CaretColNo] = '^';
|
|
1232 }
|
|
1233
|
|
1234 std::string FixItInsertionLine = buildFixItInsertionLine(
|
|
1235 FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get());
|
|
1236
|
|
1237 // If the source line is too long for our terminal, select only the
|
|
1238 // "interesting" source region within that line.
|
|
1239 unsigned Columns = DiagOpts->MessageLength;
|
|
1240 if (Columns)
|
|
1241 selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
|
|
1242 Columns, sourceColMap);
|
|
1243
|
|
1244 // If we are in -fdiagnostics-print-source-range-info mode, we are trying
|
|
1245 // to produce easily machine parsable output. Add a space before the
|
|
1246 // source line and the caret to make it trivial to tell the main diagnostic
|
|
1247 // line from what the user is intended to see.
|
|
1248 if (DiagOpts->ShowSourceRanges) {
|
|
1249 SourceLine = ' ' + SourceLine;
|
|
1250 CaretLine = ' ' + CaretLine;
|
|
1251 }
|
|
1252
|
|
1253 // Finally, remove any blank spaces from the end of CaretLine.
|
|
1254 while (!CaretLine.empty() && CaretLine[CaretLine.size() - 1] == ' ')
|
|
1255 CaretLine.erase(CaretLine.end() - 1);
|
|
1256
|
|
1257 // Emit what we have computed.
|
|
1258 emitSnippet(SourceLine);
|
|
1259
|
|
1260 if (!CaretLine.empty()) {
|
|
1261 if (DiagOpts->ShowColors)
|
|
1262 OS.changeColor(caretColor, true);
|
|
1263 OS << CaretLine << '\n';
|
|
1264 if (DiagOpts->ShowColors)
|
|
1265 OS.resetColor();
|
|
1266 }
|
|
1267
|
|
1268 if (!FixItInsertionLine.empty()) {
|
|
1269 if (DiagOpts->ShowColors)
|
|
1270 // Print fixit line in color
|
|
1271 OS.changeColor(fixitColor, false);
|
|
1272 if (DiagOpts->ShowSourceRanges)
|
|
1273 OS << ' ';
|
|
1274 OS << FixItInsertionLine << '\n';
|
|
1275 if (DiagOpts->ShowColors)
|
|
1276 OS.resetColor();
|
|
1277 }
|
|
1278 }
|
|
1279
|
|
1280 // Print out any parseable fixit information requested by the options.
|
|
1281 emitParseableFixits(Hints, SM);
|
|
1282 }
|
|
1283
|
|
1284 void TextDiagnostic::emitSnippet(StringRef line) {
|
|
1285 if (line.empty())
|
|
1286 return;
|
|
1287
|
|
1288 size_t i = 0;
|
|
1289
|
|
1290 std::string to_print;
|
|
1291 bool print_reversed = false;
|
|
1292
|
|
1293 while (i<line.size()) {
|
|
1294 std::pair<SmallString<16>,bool> res
|
|
1295 = printableTextForNextCharacter(line, &i, DiagOpts->TabStop);
|
|
1296 bool was_printable = res.second;
|
|
1297
|
|
1298 if (DiagOpts->ShowColors && was_printable == print_reversed) {
|
|
1299 if (print_reversed)
|
|
1300 OS.reverseColor();
|
|
1301 OS << to_print;
|
|
1302 to_print.clear();
|
|
1303 if (DiagOpts->ShowColors)
|
|
1304 OS.resetColor();
|
|
1305 }
|
|
1306
|
|
1307 print_reversed = !was_printable;
|
|
1308 to_print += res.first.str();
|
|
1309 }
|
|
1310
|
|
1311 if (print_reversed && DiagOpts->ShowColors)
|
|
1312 OS.reverseColor();
|
|
1313 OS << to_print;
|
|
1314 if (print_reversed && DiagOpts->ShowColors)
|
|
1315 OS.resetColor();
|
|
1316
|
|
1317 OS << '\n';
|
|
1318 }
|
|
1319
|
|
1320 void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints,
|
|
1321 const SourceManager &SM) {
|
|
1322 if (!DiagOpts->ShowParseableFixits)
|
|
1323 return;
|
|
1324
|
|
1325 // We follow FixItRewriter's example in not (yet) handling
|
|
1326 // fix-its in macros.
|
|
1327 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
|
|
1328 I != E; ++I) {
|
|
1329 if (I->RemoveRange.isInvalid() ||
|
|
1330 I->RemoveRange.getBegin().isMacroID() ||
|
|
1331 I->RemoveRange.getEnd().isMacroID())
|
|
1332 return;
|
|
1333 }
|
|
1334
|
|
1335 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
|
|
1336 I != E; ++I) {
|
|
1337 SourceLocation BLoc = I->RemoveRange.getBegin();
|
|
1338 SourceLocation ELoc = I->RemoveRange.getEnd();
|
|
1339
|
|
1340 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
|
|
1341 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
|
|
1342
|
|
1343 // Adjust for token ranges.
|
|
1344 if (I->RemoveRange.isTokenRange())
|
|
1345 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
|
|
1346
|
|
1347 // We specifically do not do word-wrapping or tab-expansion here,
|
|
1348 // because this is supposed to be easy to parse.
|
|
1349 PresumedLoc PLoc = SM.getPresumedLoc(BLoc);
|
|
1350 if (PLoc.isInvalid())
|
|
1351 break;
|
|
1352
|
|
1353 OS << "fix-it:\"";
|
|
1354 OS.write_escaped(PLoc.getFilename());
|
|
1355 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)
|
|
1356 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)
|
|
1357 << '-' << SM.getLineNumber(EInfo.first, EInfo.second)
|
|
1358 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second)
|
|
1359 << "}:\"";
|
|
1360 OS.write_escaped(I->CodeToInsert);
|
|
1361 OS << "\"\n";
|
|
1362 }
|
|
1363 }
|