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