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