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