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