LCOV - code coverage report
Current view: top level - lib/Support - SourceMgr.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 222 253 87.7 %
Date: 2018-10-20 13:21:21 Functions: 18 19 94.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
       2             : //
       3             : //                     The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : //
      10             : // This file implements the SourceMgr class.  This class is used as a simple
      11             : // substrate for diagnostics, #include handling, and other low level things for
      12             : // simple parsers.
      13             : //
      14             : //===----------------------------------------------------------------------===//
      15             : 
      16             : #include "llvm/Support/SourceMgr.h"
      17             : #include "llvm/ADT/ArrayRef.h"
      18             : #include "llvm/ADT/STLExtras.h"
      19             : #include "llvm/ADT/SmallVector.h"
      20             : #include "llvm/ADT/StringRef.h"
      21             : #include "llvm/ADT/Twine.h"
      22             : #include "llvm/Support/ErrorOr.h"
      23             : #include "llvm/Support/Locale.h"
      24             : #include "llvm/Support/MemoryBuffer.h"
      25             : #include "llvm/Support/Path.h"
      26             : #include "llvm/Support/SMLoc.h"
      27             : #include "llvm/Support/raw_ostream.h"
      28             : #include <algorithm>
      29             : #include <cassert>
      30             : #include <cstddef>
      31             : #include <limits>
      32             : #include <memory>
      33             : #include <string>
      34             : #include <utility>
      35             : 
      36             : using namespace llvm;
      37             : 
      38             : static const size_t TabStop = 8;
      39             : 
      40        8470 : unsigned SourceMgr::AddIncludeFile(const std::string &Filename,
      41             :                                    SMLoc IncludeLoc,
      42             :                                    std::string &IncludedFile) {
      43             :   IncludedFile = Filename;
      44             :   ErrorOr<std::unique_ptr<MemoryBuffer>> NewBufOrErr =
      45        8470 :     MemoryBuffer::getFile(IncludedFile);
      46             : 
      47             :   // If the file didn't exist directly, see if it's in an include path.
      48       29962 :   for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr;
      49             :        ++i) {
      50             :     IncludedFile =
      51       26044 :         IncludeDirectories[i] + sys::path::get_separator().data() + Filename;
      52       26044 :     NewBufOrErr = MemoryBuffer::getFile(IncludedFile);
      53             :   }
      54             : 
      55        8470 :   if (!NewBufOrErr)
      56             :     return 0;
      57             : 
      58       16940 :   return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc);
      59             : }
      60             : 
      61      222718 : unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
      62      571920 :   for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
      63      595882 :     if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
      64             :         // Use <= here so that a pointer to the null at the end of the buffer
      65             :         // is included as part of the buffer.
      66      209193 :         Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
      67      171457 :       return i + 1;
      68             :   return 0;
      69             : }
      70             : 
      71             : template <typename T>
      72       56297 : unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const {
      73             : 
      74             :   // Ensure OffsetCache is allocated and populated with offsets of all the
      75             :   // '\n' bytes.
      76             :   std::vector<T> *Offsets = nullptr;
      77       56297 :   if (OffsetCache.isNull()) {
      78        3469 :     Offsets = new std::vector<T>();
      79             :     OffsetCache = Offsets;
      80        3469 :     size_t Sz = Buffer->getBufferSize();
      81             :     assert(Sz <= std::numeric_limits<T>::max());
      82             :     StringRef S = Buffer->getBuffer();
      83    14247075 :     for (size_t N = 0; N < Sz; ++N) {
      84    14243606 :       if (S[N] == '\n') {
      85      362836 :         Offsets->push_back(static_cast<T>(N));
      86             :       }
      87             :     }
      88             :   } else {
      89             :     Offsets = OffsetCache.get<std::vector<T> *>();
      90             :   }
      91             : 
      92       56297 :   const char *BufStart = Buffer->getBufferStart();
      93             :   assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
      94       56297 :   ptrdiff_t PtrDiff = Ptr - BufStart;
      95             :   assert(PtrDiff >= 0 && static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
      96       56297 :   T PtrOffset = static_cast<T>(PtrDiff);
      97             : 
      98             :   // std::lower_bound returns the first EOL offset that's not-less-than
      99             :   // PtrOffset, meaning the EOL that _ends the line_ that PtrOffset is on
     100             :   // (including if PtrOffset refers to the EOL itself). If there's no such
     101             :   // EOL, returns end().
     102             :   auto EOL = std::lower_bound(Offsets->begin(), Offsets->end(), PtrOffset);
     103             : 
     104             :   // Lines count from 1, so add 1 to the distance from the 0th line.
     105       56297 :   return (1 + (EOL - Offsets->begin()));
     106             : }
     107           0 : 
     108             : SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&Other)
     109             :   : Buffer(std::move(Other.Buffer)),
     110             :     OffsetCache(Other.OffsetCache),
     111             :     IncludeLoc(Other.IncludeLoc) {
     112           0 :   Other.OffsetCache = nullptr;
     113           0 : }
     114             : 
     115           0 : SourceMgr::SrcBuffer::~SrcBuffer() {
     116             :   if (!OffsetCache.isNull()) {
     117             :     if (OffsetCache.is<std::vector<uint8_t>*>())
     118           0 :       delete OffsetCache.get<std::vector<uint8_t>*>();
     119           0 :     else if (OffsetCache.is<std::vector<uint16_t>*>())
     120           0 :       delete OffsetCache.get<std::vector<uint16_t>*>();
     121             :     else if (OffsetCache.is<std::vector<uint32_t>*>())
     122             :       delete OffsetCache.get<std::vector<uint32_t>*>();
     123             :     else
     124             :       delete OffsetCache.get<std::vector<uint64_t>*>();
     125             :     OffsetCache = nullptr;
     126             :   }
     127           0 : }
     128             : 
     129           0 : std::pair<unsigned, unsigned>
     130             : SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const {
     131           0 :   if (!BufferID)
     132             :     BufferID = FindBufferContainingLoc(Loc);
     133             :   assert(BufferID && "Invalid Location!");
     134             : 
     135             :   auto &SB = getBufferInfo(BufferID);
     136             :   const char *Ptr = Loc.getPointer();
     137             : 
     138             :   size_t Sz = SB.Buffer->getBufferSize();
     139             :   unsigned LineNo;
     140           0 :   if (Sz <= std::numeric_limits<uint8_t>::max())
     141             :     LineNo = SB.getLineNumber<uint8_t>(Ptr);
     142       15987 :   else if (Sz <= std::numeric_limits<uint16_t>::max())
     143             :     LineNo = SB.getLineNumber<uint16_t>(Ptr);
     144             :   else if (Sz <= std::numeric_limits<uint32_t>::max())
     145             :     LineNo = SB.getLineNumber<uint32_t>(Ptr);
     146             :   else
     147       15987 :     LineNo = SB.getLineNumber<uint64_t>(Ptr);
     148          18 : 
     149             :   const char *BufStart = SB.Buffer->getBufferStart();
     150          18 :   size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r");
     151             :   if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0;
     152             :   return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs);
     153     2952184 : }
     154     2952166 : 
     155       88697 : void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
     156             :   if (IncludeLoc == SMLoc()) return;  // Top of stack.
     157             : 
     158             :   unsigned CurBuf = FindBufferContainingLoc(IncludeLoc);
     159             :   assert(CurBuf && "Invalid or unspecified location!");
     160             : 
     161             :   PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
     162       15987 : 
     163             :   OS << "Included from "
     164       15987 :      << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
     165             :      << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
     166       15987 : }
     167             : 
     168             : SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
     169             :                                    const Twine &Msg,
     170             :                                    ArrayRef<SMRange> Ranges,
     171             :                                    ArrayRef<SMFixIt> FixIts) const {
     172             :   // First thing to do: find the current buffer containing the specified
     173             :   // location to pull out the source line.
     174             :   SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges;
     175       15987 :   std::pair<unsigned, unsigned> LineAndCol;
     176             :   StringRef BufferID = "<unknown>";
     177       39294 :   std::string LineStr;
     178             : 
     179             :   if (Loc.isValid()) {
     180             :     unsigned CurBuf = FindBufferContainingLoc(Loc);
     181             :     assert(CurBuf && "Invalid or unspecified location!");
     182       39294 : 
     183        2640 :     const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf);
     184             :     BufferID = CurMB->getBufferIdentifier();
     185        2640 : 
     186             :     // Scan backward to find the start of the line.
     187             :     const char *LineStart = Loc.getPointer();
     188    11213710 :     const char *BufStart = CurMB->getBufferStart();
     189    11211070 :     while (LineStart != BufStart && LineStart[-1] != '\n' &&
     190      270554 :            LineStart[-1] != '\r')
     191             :       --LineStart;
     192             : 
     193             :     // Get the end of the line.
     194             :     const char *LineEnd = Loc.getPointer();
     195             :     const char *BufEnd = CurMB->getBufferEnd();
     196             :     while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
     197       39294 :       ++LineEnd;
     198             :     LineStr = std::string(LineStart, LineEnd);
     199       39294 : 
     200             :     // Convert any ranges to column ranges that only intersect the line of the
     201       39294 :     // location.
     202             :     for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
     203             :       SMRange R = Ranges[i];
     204             :       if (!R.isValid()) continue;
     205             : 
     206             :       // If the line doesn't contain any part of the range, then ignore it.
     207             :       if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
     208             :         continue;
     209             : 
     210       39294 :       // Ignore pieces of the range that go onto other lines.
     211             :       if (R.Start.getPointer() < LineStart)
     212        1016 :         R.Start = SMLoc::getFromPointer(LineStart);
     213             :       if (R.End.getPointer() > LineEnd)
     214             :         R.End = SMLoc::getFromPointer(LineEnd);
     215             : 
     216             :       // Translate from SMLoc ranges to column ranges.
     217        1016 :       // FIXME: Handle multibyte characters.
     218         811 :       ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart,
     219             :                                          R.End.getPointer()-LineStart));
     220         811 :     }
     221             : 
     222             :     LineAndCol = getLineAndColumn(Loc, CurBuf);
     223       81181 :   }
     224       80370 : 
     225        3585 :   return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first,
     226             :                       LineAndCol.second-1, Kind, Msg.str(),
     227             :                       LineStr, ColRanges, FixIts);
     228             : }
     229             : 
     230             : void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic,
     231             :                              bool ShowColors) const {
     232        1016 :   // Report the message with the diagnostic handler if present.
     233             :   if (DiagHandler) {
     234        1016 :     DiagHandler(Diagnostic, DiagContext);
     235             :     return;
     236        1016 :   }
     237             : 
     238             :   if (Diagnostic.getLoc().isValid()) {
     239             :     unsigned CurBuf = FindBufferContainingLoc(Diagnostic.getLoc());
     240             :     assert(CurBuf && "Invalid or unspecified location!");
     241             :     PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
     242             :   }
     243             : 
     244             :   Diagnostic.print(nullptr, OS, ShowColors);
     245        1016 : }
     246             : 
     247             : void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc,
     248      692583 :                              SourceMgr::DiagKind Kind,
     249             :                              const Twine &Msg, ArrayRef<SMRange> Ranges,
     250             :                              ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
     251      692583 :   PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors);
     252             : }
     253      692583 : 
     254             : void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
     255     1093536 :                              const Twine &Msg, ArrayRef<SMRange> Ranges,
     256     1093536 :                              ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
     257             :   PrintMessage(errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors);
     258         806 : }
     259             : 
     260        2634 : //===----------------------------------------------------------------------===//
     261             : // SMDiagnostic Implementation
     262          18 : //===----------------------------------------------------------------------===//
     263             : 
     264           0 : SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN,
     265             :                            int Line, int Col, SourceMgr::DiagKind Kind,
     266             :                            StringRef Msg, StringRef LineStr,
     267     1093536 :                            ArrayRef<std::pair<unsigned,unsigned>> Ranges,
     268             :                            ArrayRef<SMFixIt> Hints)
     269             :   : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind),
     270       56297 :     Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()),
     271       56297 :     FixIts(Hints.begin(), Hints.end()) {
     272         100 :   llvm::sort(FixIts);
     273             : }
     274             : 
     275             : static void buildFixItLine(std::string &CaretLine, std::string &FixItLine,
     276       56297 :                            ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){
     277             :   if (FixIts.empty())
     278       56297 :     return;
     279             : 
     280       56297 :   const char *LineStart = SourceLine.begin();
     281        1016 :   const char *LineEnd = SourceLine.end();
     282       55281 : 
     283       39294 :   size_t PrevHintEndCol = 0;
     284       15987 : 
     285       15987 :   for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end();
     286             :        I != E; ++I) {
     287           0 :     // If the fixit contains a newline or tab, ignore it.
     288             :     if (I->getText().find_first_of("\n\r\t") != StringRef::npos)
     289       56297 :       continue;
     290      112594 : 
     291       56297 :     SMRange R = I->getRange();
     292      112594 : 
     293             :     // If the line doesn't contain any part of the range, then ignore it.
     294             :     if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
     295        4288 :       continue;
     296        4288 : 
     297             :     // Translate from SMLoc to column.
     298           0 :     // Ignore pieces of the range that go onto other lines.
     299             :     // FIXME: Handle multibyte characters in the source line.
     300             :     unsigned FirstCol;
     301           0 :     if (R.Start.getPointer() < LineStart)
     302             :       FirstCol = 0;
     303           0 :     else
     304           0 :       FirstCol = R.Start.getPointer() - LineStart;
     305           0 : 
     306             :     // If we inserted a long previous hint, push this one forwards, and add
     307             :     // an extra space to show that this is not part of the previous
     308       56031 :     // completion. This is sort of the best we can do when two hints appear
     309             :     // to overlap.
     310             :     //
     311             :     // Note that if this hint is located immediately after the previous
     312             :     // hint, no space will be added, since the location is more important.
     313             :     unsigned HintCol = FirstCol;
     314             :     if (HintCol < PrevHintEndCol)
     315             :       HintCol = PrevHintEndCol + 1;
     316             : 
     317             :     // FIXME: This assertion is intended to catch unintended use of multibyte
     318             :     // characters in fixits. If we decide to do this, we'll have to track
     319       56031 :     // separate byte widths for the source and fixit lines.
     320       55956 :     assert((size_t)sys::locale::columnWidth(I->getText()) ==
     321             :            I->getText().size());
     322             : 
     323             :     // This relies on one byte per column in our fixit hints.
     324       55956 :     unsigned LastColumnModified = HintCol + I->getText().size();
     325             :     if (LastColumnModified > FixItLine.size())
     326             :       FixItLine.resize(LastColumnModified, ' ');
     327             : 
     328       55956 :     std::copy(I->getText().begin(), I->getText().end(),
     329      650281 :               FixItLine.begin() + HintCol);
     330             : 
     331      594325 :     PrevHintEndCol = LastColumnModified;
     332             : 
     333             :     // For replacements, mark the removal range with '~'.
     334             :     // FIXME: Handle multibyte characters in the source line.
     335       55956 :     unsigned LastCol;
     336     1416740 :     if (R.End.getPointer() >= LineEnd)
     337     1360784 :       LastCol = LineEnd - LineStart;
     338       55956 :     else
     339             :       LastCol = R.End.getPointer() - LineStart;
     340             : 
     341             :     std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~');
     342      106926 :   }
     343       50970 : }
     344       50970 : 
     345             : static void printSourceLine(raw_ostream &S, StringRef LineContents) {
     346             :   // Print out the source line one character at a time, so we can expand tabs.
     347         516 :   for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) {
     348             :     size_t NextTab = LineContents.find('\t', i);
     349             :     // If there were no tabs left, print the rest, we are done.
     350             :     if (NextTab == StringRef::npos) {
     351         516 :       S << LineContents.drop_front(i);
     352             :       break;
     353         516 :     }
     354             : 
     355             :     // Otherwise, print from i to NextTab.
     356             :     S << LineContents.slice(i, NextTab);
     357             :     OutCol += NextTab - i;
     358         516 :     i = NextTab;
     359         516 : 
     360             :     // If we have a tab, emit at least one space, then round up to 8 columns.
     361             :     do {
     362       55956 :       S << ' ';
     363             :       ++OutCol;
     364             :     } while ((OutCol % TabStop) != 0);
     365             :   }
     366       56031 :   S << '\n';
     367      111888 : }
     368             : 
     369             : static bool isNonASCII(char c) {
     370       55695 :   return c & 0x80;
     371             : }
     372             : 
     373       55695 : void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors,
     374       51447 :                          bool ShowKindLabel) const {
     375       51447 :   // Display colors only if OS supports colors.
     376             :   ShowColors &= S.has_colors();
     377             : 
     378        4248 :   if (ShowColors)
     379        4248 :     S.changeColor(raw_ostream::SAVEDCOLOR, true);
     380             : 
     381        4248 :   if (ProgName && ProgName[0])
     382             :     S << ProgName << ": ";
     383             : 
     384        4248 :   if (!Filename.empty()) {
     385             :     if (Filename == "-")
     386             :       S << "<stdin>";
     387       55695 :     else
     388             :       S << Filename;
     389             : 
     390             :     if (LineNo != -1) {
     391       55695 :       S << ':' << LineNo;
     392       55695 :       if (ColumnNo != -1)
     393             :         S << ':' << (ColumnNo+1);
     394       55655 :     }
     395             :     S << ": ";
     396             :   }
     397       55655 : 
     398       55655 :   if (ShowKindLabel) {
     399             :     switch (Kind) {
     400             :     case SourceMgr::DK_Error:
     401             :       if (ShowColors)
     402             :         S.changeColor(raw_ostream::RED, true);
     403             :       S << "error: ";
     404       56191 :       break;
     405             :     case SourceMgr::DK_Warning:
     406             :       if (ShowColors)
     407             :         S.changeColor(raw_ostream::MAGENTA, true);
     408       56191 :       S << "warning: ";
     409             :       break;
     410             :     case SourceMgr::DK_Note:
     411      168573 :       if (ShowColors)
     412             :         S.changeColor(raw_ostream::BLACK, true);
     413       56191 :       S << "note: ";
     414             :       break;
     415       55892 :     case SourceMgr::DK_Remark:
     416             :       if (ShowColors)
     417       55892 :         S.changeColor(raw_ostream::BLUE, true);
     418             :       S << "remark: ";
     419             :       break;
     420             :     }
     421          23 : 
     422             :     if (ShowColors) {
     423             :       S.resetColor();
     424             :       S.changeColor(raw_ostream::SAVEDCOLOR, true);
     425          23 :     }
     426          46 :   }
     427             : 
     428          46 :   S << Message << '\n';
     429             : 
     430             :   if (ShowColors)
     431             :     S.resetColor();
     432             : 
     433             :   if (LineNo == -1 || ColumnNo == -1)
     434          23 :     return;
     435             : 
     436             :   // FIXME: If there are multibyte or multi-column characters in the source, all
     437             :   // our ranges will be wrong. To do this properly, we'll need a byte-to-column
     438             :   // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
     439             :   // expanding them later, and bail out rather than show incorrect ranges and
     440             :   // misaligned fixits for any other odd characters.
     441          23 :   if (find_if(LineContents, isNonASCII) != LineContents.end()) {
     442             :     printSourceLine(S, LineContents);
     443             :     return;
     444          23 :   }
     445             :   size_t NumColumns = LineContents.size();
     446             : 
     447             :   // Build the line with the caret and ranges.
     448             :   std::string CaretLine(NumColumns+1, ' ');
     449             : 
     450             :   // Expand any ranges.
     451             :   for (unsigned r = 0, e = Ranges.size(); r != e; ++r) {
     452             :     std::pair<unsigned, unsigned> R = Ranges[r];
     453             :     std::fill(&CaretLine[R.first],
     454          23 :               &CaretLine[std::min((size_t)R.second, CaretLine.size())],
     455           0 :               '~');
     456             :   }
     457             : 
     458             :   // Add any fix-its.
     459             :   // FIXME: Find the beginning of the line properly for multibyte characters.
     460             :   std::string FixItInsertionLine;
     461             :   buildFixItLine(CaretLine, FixItInsertionLine, FixIts,
     462             :                  makeArrayRef(Loc.getPointer() - ColumnNo,
     463             :                               LineContents.size()));
     464          23 : 
     465          23 :   // Finally, plop on the caret.
     466          23 :   if (unsigned(ColumnNo) <= NumColumns)
     467             :     CaretLine[ColumnNo] = '^';
     468             :   else
     469             :     CaretLine[NumColumns] = '^';
     470             : 
     471             :   // ... and remove trailing whitespace so the output doesn't wrap for it.  We
     472             :   // know that the line isn't completely empty because it has the caret in it at
     473             :   // least.
     474             :   CaretLine.erase(CaretLine.find_last_not_of(' ')+1);
     475             : 
     476          23 :   printSourceLine(S, LineContents);
     477           1 : 
     478             :   if (ShowColors)
     479          22 :     S.changeColor(raw_ostream::GREEN, true);
     480             : 
     481          23 :   // Print out the caret line, matching tabs in the source line.
     482             :   for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) {
     483             :     if (i >= LineContents.size() || LineContents[i] != '\t') {
     484             :       S << CaretLine[i];
     485       55895 :       ++OutCol;
     486             :       continue;
     487       80080 :     }
     488      135731 : 
     489             :     // Okay, we have a tab.  Insert the appropriate number of characters.
     490       24185 :     do {
     491       55773 :       S << CaretLine[i];
     492       55773 :       ++OutCol;
     493             :     } while ((OutCol % TabStop) != 0);
     494             :   }
     495             :   S << '\n';
     496       24185 : 
     497       24185 :   if (ShowColors)
     498             :     S.resetColor();
     499             : 
     500             :   // Print out the replacement line, matching tabs in the source line.
     501             :   if (FixItInsertionLine.empty())
     502             :     return;
     503      138747 : 
     504      138747 :   for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) {
     505             :     if (i >= LineContents.size() || LineContents[i] != '\t') {
     506             :       S << FixItInsertionLine[i];
     507       55895 :       ++OutCol;
     508             :       continue;
     509     1952013 :     }
     510     1952013 : 
     511             :     // Okay, we have a tab.  Insert the appropriate number of characters.
     512             :     do {
     513       55975 :       S << FixItInsertionLine[i];
     514             :       // FIXME: This is trying not to break up replacements, but then to re-sync
     515             :       // with the tabs between replacements. This will fail, though, if two
     516       55975 :       // fix-it replacements are exactly adjacent, or if a fix-it contains a
     517             :       // space. Really we should be precomputing column widths, which we'll
     518       55975 :       // need anyway for multibyte chars.
     519           0 :       if (FixItInsertionLine[i] != ' ')
     520             :         ++i;
     521       55975 :       ++OutCol;
     522         211 :     } while (((OutCol % TabStop) != 0) && i != e);
     523             :   }
     524       55975 :   S << '\n';
     525       55888 : }

Generated by: LCOV version 1.13