1194612Sed//===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
2194612Sed//
3194612Sed//                     The LLVM Compiler Infrastructure
4194612Sed//
5194612Sed// This file is distributed under the University of Illinois Open Source
6194612Sed// License. See LICENSE.TXT for details.
7194612Sed//
8194612Sed//===----------------------------------------------------------------------===//
9194612Sed//
10194612Sed// This file implements the SourceMgr class.  This class is used as a simple
11194612Sed// substrate for diagnostics, #include handling, and other low level things for
12194612Sed// simple parsers.
13194612Sed//
14194612Sed//===----------------------------------------------------------------------===//
15194612Sed
16249423Sdim#include "llvm/Support/SourceMgr.h"
17249423Sdim#include "llvm/ADT/OwningPtr.h"
18249423Sdim#include "llvm/ADT/SmallString.h"
19218893Sdim#include "llvm/ADT/Twine.h"
20249423Sdim#include "llvm/Support/Locale.h"
21194612Sed#include "llvm/Support/MemoryBuffer.h"
22194612Sed#include "llvm/Support/raw_ostream.h"
23218893Sdim#include "llvm/Support/system_error.h"
24194612Sedusing namespace llvm;
25194612Sed
26249423Sdimstatic const size_t TabStop = 8;
27249423Sdim
28198090Srdivackynamespace {
29198090Srdivacky  struct LineNoCacheTy {
30198090Srdivacky    int LastQueryBufferID;
31198090Srdivacky    const char *LastQuery;
32198090Srdivacky    unsigned LineNoOfQuery;
33198090Srdivacky  };
34198090Srdivacky}
35198090Srdivacky
36198090Srdivackystatic LineNoCacheTy *getCache(void *Ptr) {
37198090Srdivacky  return (LineNoCacheTy*)Ptr;
38198090Srdivacky}
39198090Srdivacky
40198090Srdivacky
41194612SedSourceMgr::~SourceMgr() {
42198090Srdivacky  // Delete the line # cache if allocated.
43198090Srdivacky  if (LineNoCacheTy *Cache = getCache(LineNoCache))
44198090Srdivacky    delete Cache;
45203954Srdivacky
46194612Sed  while (!Buffers.empty()) {
47194612Sed    delete Buffers.back().Buffer;
48194612Sed    Buffers.pop_back();
49194612Sed  }
50194612Sed}
51194612Sed
52194612Sed/// AddIncludeFile - Search for a file with the specified name in the current
53194612Sed/// directory or in one of the IncludeDirs.  If no file is found, this returns
54194612Sed/// ~0, otherwise it returns the buffer ID of the stacked file.
55263508Sdimsize_t SourceMgr::AddIncludeFile(const std::string &Filename,
56263508Sdim                                 SMLoc IncludeLoc,
57263508Sdim                                 std::string &IncludedFile) {
58218893Sdim  OwningPtr<MemoryBuffer> NewBuf;
59223017Sdim  IncludedFile = Filename;
60223017Sdim  MemoryBuffer::getFile(IncludedFile.c_str(), NewBuf);
61203954Srdivacky
62194612Sed  // If the file didn't exist directly, see if it's in an include path.
63194612Sed  for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBuf; ++i) {
64223017Sdim    IncludedFile = IncludeDirectories[i] + "/" + Filename;
65223017Sdim    MemoryBuffer::getFile(IncludedFile.c_str(), NewBuf);
66194612Sed  }
67203954Srdivacky
68263508Sdim  if (!NewBuf) return ~0U;
69194612Sed
70218893Sdim  return AddNewSourceBuffer(NewBuf.take(), IncludeLoc);
71194612Sed}
72194612Sed
73194612Sed
74194612Sed/// FindBufferContainingLoc - Return the ID of the buffer containing the
75194612Sed/// specified location, returning -1 if not found.
76194612Sedint SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
77194612Sed  for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
78194612Sed    if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
79194612Sed        // Use <= here so that a pointer to the null at the end of the buffer
80194612Sed        // is included as part of the buffer.
81194612Sed        Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
82194612Sed      return i;
83194612Sed  return -1;
84194612Sed}
85194612Sed
86239462Sdim/// getLineAndColumn - Find the line and column number for the specified
87239462Sdim/// location in the specified file.  This is not a fast method.
88239462Sdimstd::pair<unsigned, unsigned>
89239462SdimSourceMgr::getLineAndColumn(SMLoc Loc, int BufferID) const {
90194612Sed  if (BufferID == -1) BufferID = FindBufferContainingLoc(Loc);
91194612Sed  assert(BufferID != -1 && "Invalid Location!");
92203954Srdivacky
93194612Sed  MemoryBuffer *Buff = getBufferInfo(BufferID).Buffer;
94203954Srdivacky
95194612Sed  // Count the number of \n's between the start of the file and the specified
96194612Sed  // location.
97194612Sed  unsigned LineNo = 1;
98203954Srdivacky
99239462Sdim  const char *BufStart = Buff->getBufferStart();
100239462Sdim  const char *Ptr = BufStart;
101194612Sed
102198090Srdivacky  // If we have a line number cache, and if the query is to a later point in the
103198090Srdivacky  // same file, start searching from the last query location.  This optimizes
104198090Srdivacky  // for the case when multiple diagnostics come out of one file in order.
105198090Srdivacky  if (LineNoCacheTy *Cache = getCache(LineNoCache))
106203954Srdivacky    if (Cache->LastQueryBufferID == BufferID &&
107198090Srdivacky        Cache->LastQuery <= Loc.getPointer()) {
108198090Srdivacky      Ptr = Cache->LastQuery;
109198090Srdivacky      LineNo = Cache->LineNoOfQuery;
110198090Srdivacky    }
111198090Srdivacky
112198090Srdivacky  // Scan for the location being queried, keeping track of the number of lines
113198090Srdivacky  // we see.
114194612Sed  for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr)
115194612Sed    if (*Ptr == '\n') ++LineNo;
116203954Srdivacky
117198090Srdivacky  // Allocate the line number cache if it doesn't exist.
118198090Srdivacky  if (LineNoCache == 0)
119198090Srdivacky    LineNoCache = new LineNoCacheTy();
120203954Srdivacky
121198090Srdivacky  // Update the line # cache.
122198090Srdivacky  LineNoCacheTy &Cache = *getCache(LineNoCache);
123198090Srdivacky  Cache.LastQueryBufferID = BufferID;
124198090Srdivacky  Cache.LastQuery = Ptr;
125198090Srdivacky  Cache.LineNoOfQuery = LineNo;
126239462Sdim
127239462Sdim  size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r");
128239462Sdim  if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0;
129239462Sdim  return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs);
130194612Sed}
131194612Sed
132195340Sedvoid SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
133194612Sed  if (IncludeLoc == SMLoc()) return;  // Top of stack.
134203954Srdivacky
135194612Sed  int CurBuf = FindBufferContainingLoc(IncludeLoc);
136194612Sed  assert(CurBuf != -1 && "Invalid or unspecified location!");
137194612Sed
138195340Sed  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
139203954Srdivacky
140195340Sed  OS << "Included from "
141195340Sed     << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
142195340Sed     << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
143194612Sed}
144194612Sed
145194612Sed
146195340Sed/// GetMessage - Return an SMDiagnostic at the specified location with the
147195340Sed/// specified string.
148195340Sed///
149195340Sed/// @param Type - If non-null, the kind of message (e.g., "error") which is
150195340Sed/// prefixed to the message.
151234353SdimSMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
152234353Sdim                                   const Twine &Msg,
153249423Sdim                                   ArrayRef<SMRange> Ranges,
154249423Sdim                                   ArrayRef<SMFixIt> FixIts) const {
155203954Srdivacky
156194612Sed  // First thing to do: find the current buffer containing the specified
157239462Sdim  // location to pull out the source line.
158239462Sdim  SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges;
159239462Sdim  std::pair<unsigned, unsigned> LineAndCol;
160239462Sdim  const char *BufferID = "<unknown>";
161239462Sdim  std::string LineStr;
162239462Sdim
163239462Sdim  if (Loc.isValid()) {
164239462Sdim    int CurBuf = FindBufferContainingLoc(Loc);
165239462Sdim    assert(CurBuf != -1 && "Invalid or unspecified location!");
166203954Srdivacky
167239462Sdim    MemoryBuffer *CurMB = getBufferInfo(CurBuf).Buffer;
168239462Sdim    BufferID = CurMB->getBufferIdentifier();
169239462Sdim
170239462Sdim    // Scan backward to find the start of the line.
171239462Sdim    const char *LineStart = Loc.getPointer();
172239462Sdim    const char *BufStart = CurMB->getBufferStart();
173239462Sdim    while (LineStart != BufStart && LineStart[-1] != '\n' &&
174239462Sdim           LineStart[-1] != '\r')
175239462Sdim      --LineStart;
176199989Srdivacky
177239462Sdim    // Get the end of the line.
178239462Sdim    const char *LineEnd = Loc.getPointer();
179239462Sdim    const char *BufEnd = CurMB->getBufferEnd();
180239462Sdim    while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
181239462Sdim      ++LineEnd;
182239462Sdim    LineStr = std::string(LineStart, LineEnd);
183199989Srdivacky
184239462Sdim    // Convert any ranges to column ranges that only intersect the line of the
185239462Sdim    // location.
186239462Sdim    for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
187239462Sdim      SMRange R = Ranges[i];
188239462Sdim      if (!R.isValid()) continue;
189239462Sdim
190239462Sdim      // If the line doesn't contain any part of the range, then ignore it.
191239462Sdim      if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
192239462Sdim        continue;
193239462Sdim
194239462Sdim      // Ignore pieces of the range that go onto other lines.
195239462Sdim      if (R.Start.getPointer() < LineStart)
196239462Sdim        R.Start = SMLoc::getFromPointer(LineStart);
197239462Sdim      if (R.End.getPointer() > LineEnd)
198239462Sdim        R.End = SMLoc::getFromPointer(LineEnd);
199239462Sdim
200239462Sdim      // Translate from SMLoc ranges to column ranges.
201249423Sdim      // FIXME: Handle multibyte characters.
202239462Sdim      ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart,
203239462Sdim                                         R.End.getPointer()-LineStart));
204239462Sdim    }
205234353Sdim
206239462Sdim    LineAndCol = getLineAndColumn(Loc, CurBuf);
207239462Sdim  }
208234353Sdim
209239462Sdim  return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first,
210239462Sdim                      LineAndCol.second-1, Kind, Msg.str(),
211249423Sdim                      LineStr, ColRanges, FixIts);
212194612Sed}
213195340Sed
214263508Sdimvoid SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc,
215263508Sdim                             SourceMgr::DiagKind Kind,
216234982Sdim                             const Twine &Msg, ArrayRef<SMRange> Ranges,
217249423Sdim                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
218249423Sdim  SMDiagnostic Diagnostic = GetMessage(Loc, Kind, Msg, Ranges, FixIts);
219234353Sdim
220206274Srdivacky  // Report the message with the diagnostic handler if present.
221206274Srdivacky  if (DiagHandler) {
222234353Sdim    DiagHandler(Diagnostic, DiagContext);
223206274Srdivacky    return;
224206274Srdivacky  }
225218893Sdim
226239462Sdim  if (Loc != SMLoc()) {
227239462Sdim    int CurBuf = FindBufferContainingLoc(Loc);
228239462Sdim    assert(CurBuf != -1 && "Invalid or unspecified location!");
229239462Sdim    PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
230239462Sdim  }
231195340Sed
232234982Sdim  Diagnostic.print(0, OS, ShowColors);
233195340Sed}
234195340Sed
235263508Sdimvoid SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
236263508Sdim                             const Twine &Msg, ArrayRef<SMRange> Ranges,
237263508Sdim                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
238263508Sdim  PrintMessage(llvm::errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors);
239263508Sdim}
240263508Sdim
241195340Sed//===----------------------------------------------------------------------===//
242195340Sed// SMDiagnostic Implementation
243195340Sed//===----------------------------------------------------------------------===//
244195340Sed
245249423SdimSMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN,
246234353Sdim                           int Line, int Col, SourceMgr::DiagKind Kind,
247249423Sdim                           StringRef Msg, StringRef LineStr,
248249423Sdim                           ArrayRef<std::pair<unsigned,unsigned> > Ranges,
249249423Sdim                           ArrayRef<SMFixIt> Hints)
250234353Sdim  : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind),
251249423Sdim    Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()),
252249423Sdim    FixIts(Hints.begin(), Hints.end()) {
253249423Sdim  std::sort(FixIts.begin(), FixIts.end());
254234353Sdim}
255234353Sdim
256249423Sdimstatic void buildFixItLine(std::string &CaretLine, std::string &FixItLine,
257249423Sdim                           ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){
258249423Sdim  if (FixIts.empty())
259249423Sdim    return;
260234353Sdim
261249423Sdim  const char *LineStart = SourceLine.begin();
262249423Sdim  const char *LineEnd = SourceLine.end();
263249423Sdim
264249423Sdim  size_t PrevHintEndCol = 0;
265249423Sdim
266249423Sdim  for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end();
267249423Sdim       I != E; ++I) {
268249423Sdim    // If the fixit contains a newline or tab, ignore it.
269249423Sdim    if (I->getText().find_first_of("\n\r\t") != StringRef::npos)
270249423Sdim      continue;
271249423Sdim
272249423Sdim    SMRange R = I->getRange();
273249423Sdim
274249423Sdim    // If the line doesn't contain any part of the range, then ignore it.
275249423Sdim    if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
276249423Sdim      continue;
277249423Sdim
278249423Sdim    // Translate from SMLoc to column.
279249423Sdim    // Ignore pieces of the range that go onto other lines.
280249423Sdim    // FIXME: Handle multibyte characters in the source line.
281249423Sdim    unsigned FirstCol;
282249423Sdim    if (R.Start.getPointer() < LineStart)
283249423Sdim      FirstCol = 0;
284249423Sdim    else
285249423Sdim      FirstCol = R.Start.getPointer() - LineStart;
286249423Sdim
287249423Sdim    // If we inserted a long previous hint, push this one forwards, and add
288249423Sdim    // an extra space to show that this is not part of the previous
289249423Sdim    // completion. This is sort of the best we can do when two hints appear
290249423Sdim    // to overlap.
291249423Sdim    //
292249423Sdim    // Note that if this hint is located immediately after the previous
293249423Sdim    // hint, no space will be added, since the location is more important.
294249423Sdim    unsigned HintCol = FirstCol;
295249423Sdim    if (HintCol < PrevHintEndCol)
296249423Sdim      HintCol = PrevHintEndCol + 1;
297249423Sdim
298249423Sdim    // FIXME: This assertion is intended to catch unintended use of multibyte
299249423Sdim    // characters in fixits. If we decide to do this, we'll have to track
300249423Sdim    // separate byte widths for the source and fixit lines.
301249423Sdim    assert((size_t)llvm::sys::locale::columnWidth(I->getText()) ==
302249423Sdim           I->getText().size());
303249423Sdim
304249423Sdim    // This relies on one byte per column in our fixit hints.
305249423Sdim    unsigned LastColumnModified = HintCol + I->getText().size();
306249423Sdim    if (LastColumnModified > FixItLine.size())
307249423Sdim      FixItLine.resize(LastColumnModified, ' ');
308249423Sdim
309249423Sdim    std::copy(I->getText().begin(), I->getText().end(),
310249423Sdim              FixItLine.begin() + HintCol);
311249423Sdim
312249423Sdim    PrevHintEndCol = LastColumnModified;
313249423Sdim
314249423Sdim    // For replacements, mark the removal range with '~'.
315249423Sdim    // FIXME: Handle multibyte characters in the source line.
316249423Sdim    unsigned LastCol;
317249423Sdim    if (R.End.getPointer() >= LineEnd)
318249423Sdim      LastCol = LineEnd - LineStart;
319249423Sdim    else
320249423Sdim      LastCol = R.End.getPointer() - LineStart;
321249423Sdim
322249423Sdim    std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~');
323249423Sdim  }
324249423Sdim}
325249423Sdim
326249423Sdimstatic void printSourceLine(raw_ostream &S, StringRef LineContents) {
327249423Sdim  // Print out the source line one character at a time, so we can expand tabs.
328249423Sdim  for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) {
329249423Sdim    if (LineContents[i] != '\t') {
330249423Sdim      S << LineContents[i];
331249423Sdim      ++OutCol;
332249423Sdim      continue;
333249423Sdim    }
334249423Sdim
335249423Sdim    // If we have a tab, emit at least one space, then round up to 8 columns.
336249423Sdim    do {
337249423Sdim      S << ' ';
338249423Sdim      ++OutCol;
339249423Sdim    } while ((OutCol % TabStop) != 0);
340249423Sdim  }
341249423Sdim  S << '\n';
342249423Sdim}
343249423Sdim
344249423Sdimstatic bool isNonASCII(char c) {
345249423Sdim  return c & 0x80;
346249423Sdim}
347249423Sdim
348234982Sdimvoid SMDiagnostic::print(const char *ProgName, raw_ostream &S,
349234982Sdim                         bool ShowColors) const {
350239462Sdim  // Display colors only if OS supports colors.
351239462Sdim  ShowColors &= S.has_colors();
352234982Sdim
353234982Sdim  if (ShowColors)
354234982Sdim    S.changeColor(raw_ostream::SAVEDCOLOR, true);
355234982Sdim
356195340Sed  if (ProgName && ProgName[0])
357195340Sed    S << ProgName << ": ";
358195340Sed
359202878Srdivacky  if (!Filename.empty()) {
360202878Srdivacky    if (Filename == "-")
361202878Srdivacky      S << "<stdin>";
362202878Srdivacky    else
363202878Srdivacky      S << Filename;
364203954Srdivacky
365202878Srdivacky    if (LineNo != -1) {
366202878Srdivacky      S << ':' << LineNo;
367202878Srdivacky      if (ColumnNo != -1)
368202878Srdivacky        S << ':' << (ColumnNo+1);
369202878Srdivacky    }
370202878Srdivacky    S << ": ";
371195340Sed  }
372203954Srdivacky
373234353Sdim  switch (Kind) {
374234982Sdim  case SourceMgr::DK_Error:
375234982Sdim    if (ShowColors)
376234982Sdim      S.changeColor(raw_ostream::RED, true);
377234982Sdim    S << "error: ";
378234982Sdim    break;
379234982Sdim  case SourceMgr::DK_Warning:
380234982Sdim    if (ShowColors)
381234982Sdim      S.changeColor(raw_ostream::MAGENTA, true);
382234982Sdim    S << "warning: ";
383234982Sdim    break;
384234982Sdim  case SourceMgr::DK_Note:
385234982Sdim    if (ShowColors)
386234982Sdim      S.changeColor(raw_ostream::BLACK, true);
387234982Sdim    S << "note: ";
388234982Sdim    break;
389234353Sdim  }
390234982Sdim
391234982Sdim  if (ShowColors) {
392234982Sdim    S.resetColor();
393234982Sdim    S.changeColor(raw_ostream::SAVEDCOLOR, true);
394234982Sdim  }
395234982Sdim
396202878Srdivacky  S << Message << '\n';
397199989Srdivacky
398234982Sdim  if (ShowColors)
399234982Sdim    S.resetColor();
400234982Sdim
401234353Sdim  if (LineNo == -1 || ColumnNo == -1)
402234353Sdim    return;
403203954Srdivacky
404249423Sdim  // FIXME: If there are multibyte or multi-column characters in the source, all
405249423Sdim  // our ranges will be wrong. To do this properly, we'll need a byte-to-column
406249423Sdim  // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
407249423Sdim  // expanding them later, and bail out rather than show incorrect ranges and
408249423Sdim  // misaligned fixits for any other odd characters.
409249423Sdim  if (std::find_if(LineContents.begin(), LineContents.end(), isNonASCII) !=
410249423Sdim      LineContents.end()) {
411249423Sdim    printSourceLine(S, LineContents);
412249423Sdim    return;
413249423Sdim  }
414249423Sdim  size_t NumColumns = LineContents.size();
415249423Sdim
416234353Sdim  // Build the line with the caret and ranges.
417249423Sdim  std::string CaretLine(NumColumns+1, ' ');
418234353Sdim
419234353Sdim  // Expand any ranges.
420234353Sdim  for (unsigned r = 0, e = Ranges.size(); r != e; ++r) {
421234353Sdim    std::pair<unsigned, unsigned> R = Ranges[r];
422249423Sdim    std::fill(&CaretLine[R.first],
423249423Sdim              &CaretLine[std::min((size_t)R.second, CaretLine.size())],
424249423Sdim              '~');
425195340Sed  }
426249423Sdim
427249423Sdim  // Add any fix-its.
428249423Sdim  // FIXME: Find the beginning of the line properly for multibyte characters.
429249423Sdim  std::string FixItInsertionLine;
430249423Sdim  buildFixItLine(CaretLine, FixItInsertionLine, FixIts,
431249423Sdim                 makeArrayRef(Loc.getPointer() - ColumnNo,
432249423Sdim                              LineContents.size()));
433249423Sdim
434234353Sdim  // Finally, plop on the caret.
435249423Sdim  if (unsigned(ColumnNo) <= NumColumns)
436234353Sdim    CaretLine[ColumnNo] = '^';
437234353Sdim  else
438249423Sdim    CaretLine[NumColumns] = '^';
439234353Sdim
440234353Sdim  // ... and remove trailing whitespace so the output doesn't wrap for it.  We
441234353Sdim  // know that the line isn't completely empty because it has the caret in it at
442234353Sdim  // least.
443234353Sdim  CaretLine.erase(CaretLine.find_last_not_of(' ')+1);
444234353Sdim
445249423Sdim  printSourceLine(S, LineContents);
446234353Sdim
447234982Sdim  if (ShowColors)
448234982Sdim    S.changeColor(raw_ostream::GREEN, true);
449234982Sdim
450234353Sdim  // Print out the caret line, matching tabs in the source line.
451234353Sdim  for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) {
452234353Sdim    if (i >= LineContents.size() || LineContents[i] != '\t') {
453234353Sdim      S << CaretLine[i];
454234353Sdim      ++OutCol;
455234353Sdim      continue;
456234353Sdim    }
457234353Sdim
458234353Sdim    // Okay, we have a tab.  Insert the appropriate number of characters.
459234353Sdim    do {
460234353Sdim      S << CaretLine[i];
461234353Sdim      ++OutCol;
462249423Sdim    } while ((OutCol % TabStop) != 0);
463234353Sdim  }
464249423Sdim  S << '\n';
465234982Sdim
466234982Sdim  if (ShowColors)
467234982Sdim    S.resetColor();
468249423Sdim
469249423Sdim  // Print out the replacement line, matching tabs in the source line.
470249423Sdim  if (FixItInsertionLine.empty())
471249423Sdim    return;
472234353Sdim
473263508Sdim  for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) {
474249423Sdim    if (i >= LineContents.size() || LineContents[i] != '\t') {
475249423Sdim      S << FixItInsertionLine[i];
476249423Sdim      ++OutCol;
477249423Sdim      continue;
478249423Sdim    }
479249423Sdim
480249423Sdim    // Okay, we have a tab.  Insert the appropriate number of characters.
481249423Sdim    do {
482249423Sdim      S << FixItInsertionLine[i];
483249423Sdim      // FIXME: This is trying not to break up replacements, but then to re-sync
484249423Sdim      // with the tabs between replacements. This will fail, though, if two
485249423Sdim      // fix-it replacements are exactly adjacent, or if a fix-it contains a
486249423Sdim      // space. Really we should be precomputing column widths, which we'll
487249423Sdim      // need anyway for multibyte chars.
488249423Sdim      if (FixItInsertionLine[i] != ' ')
489249423Sdim        ++i;
490249423Sdim      ++OutCol;
491249423Sdim    } while (((OutCol % TabStop) != 0) && i != e);
492249423Sdim  }
493234353Sdim  S << '\n';
494195340Sed}
495