SourceMgr.cpp 21 KB

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