SourceMgr.cpp 21 KB

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