Home | History | Annotate | Download | only in Support
      1 //===- SourceMgr.h - Manager for Source Buffers & Diagnostics ---*- C++ -*-===//
      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 declares the SMDiagnostic and SourceMgr classes.  This
     11 // provides a simple substrate for diagnostics, #include handling, and other low
     12 // level things for simple parsers.
     13 //
     14 //===----------------------------------------------------------------------===//
     15 
     16 #ifndef LLVM_SUPPORT_SOURCEMGR_H
     17 #define LLVM_SUPPORT_SOURCEMGR_H
     18 
     19 #include "llvm/ADT/ArrayRef.h"
     20 #include "llvm/ADT/None.h"
     21 #include "llvm/ADT/SmallVector.h"
     22 #include "llvm/ADT/StringRef.h"
     23 #include "llvm/ADT/Twine.h"
     24 #include "llvm/Support/MemoryBuffer.h"
     25 #include "llvm/Support/SMLoc.h"
     26 #include <algorithm>
     27 #include <cassert>
     28 #include <memory>
     29 #include <string>
     30 #include <utility>
     31 #include <vector>
     32 
     33 namespace llvm {
     34 
     35 class raw_ostream;
     36 class SMDiagnostic;
     37 class SMFixIt;
     38 
     39 /// This owns the files read by a parser, handles include stacks,
     40 /// and handles diagnostic wrangling.
     41 class SourceMgr {
     42 public:
     43   enum DiagKind {
     44     DK_Error,
     45     DK_Warning,
     46     DK_Remark,
     47     DK_Note,
     48   };
     49 
     50   /// Clients that want to handle their own diagnostics in a custom way can
     51   /// register a function pointer+context as a diagnostic handler.
     52   /// It gets called each time PrintMessage is invoked.
     53   using DiagHandlerTy = void (*)(const SMDiagnostic &, void *Context);
     54 
     55 private:
     56   struct SrcBuffer {
     57     /// The memory buffer for the file.
     58     std::unique_ptr<MemoryBuffer> Buffer;
     59 
     60     /// This is the location of the parent include, or null if at the top level.
     61     SMLoc IncludeLoc;
     62   };
     63 
     64   /// This is all of the buffers that we are reading from.
     65   std::vector<SrcBuffer> Buffers;
     66 
     67   // This is the list of directories we should search for include files in.
     68   std::vector<std::string> IncludeDirectories;
     69 
     70   /// This is a cache for line number queries, its implementation is really
     71   /// private to SourceMgr.cpp.
     72   mutable void *LineNoCache = nullptr;
     73 
     74   DiagHandlerTy DiagHandler = nullptr;
     75   void *DiagContext = nullptr;
     76 
     77   bool isValidBufferID(unsigned i) const { return i && i <= Buffers.size(); }
     78 
     79 public:
     80   SourceMgr() = default;
     81   SourceMgr(const SourceMgr &) = delete;
     82   SourceMgr &operator=(const SourceMgr &) = delete;
     83   ~SourceMgr();
     84 
     85   void setIncludeDirs(const std::vector<std::string> &Dirs) {
     86     IncludeDirectories = Dirs;
     87   }
     88 
     89   /// Specify a diagnostic handler to be invoked every time PrintMessage is
     90   /// called. \p Ctx is passed into the handler when it is invoked.
     91   void setDiagHandler(DiagHandlerTy DH, void *Ctx = nullptr) {
     92     DiagHandler = DH;
     93     DiagContext = Ctx;
     94   }
     95 
     96   DiagHandlerTy getDiagHandler() const { return DiagHandler; }
     97   void *getDiagContext() const { return DiagContext; }
     98 
     99   const SrcBuffer &getBufferInfo(unsigned i) const {
    100     assert(isValidBufferID(i));
    101     return Buffers[i - 1];
    102   }
    103 
    104   const MemoryBuffer *getMemoryBuffer(unsigned i) const {
    105     assert(isValidBufferID(i));
    106     return Buffers[i - 1].Buffer.get();
    107   }
    108 
    109   unsigned getNumBuffers() const {
    110     return Buffers.size();
    111   }
    112 
    113   unsigned getMainFileID() const {
    114     assert(getNumBuffers());
    115     return 1;
    116   }
    117 
    118   SMLoc getParentIncludeLoc(unsigned i) const {
    119     assert(isValidBufferID(i));
    120     return Buffers[i - 1].IncludeLoc;
    121   }
    122 
    123   /// Add a new source buffer to this source manager. This takes ownership of
    124   /// the memory buffer.
    125   unsigned AddNewSourceBuffer(std::unique_ptr<MemoryBuffer> F,
    126                               SMLoc IncludeLoc) {
    127     SrcBuffer NB;
    128     NB.Buffer = std::move(F);
    129     NB.IncludeLoc = IncludeLoc;
    130     Buffers.push_back(std::move(NB));
    131     return Buffers.size();
    132   }
    133 
    134   /// Search for a file with the specified name in the current directory or in
    135   /// one of the IncludeDirs.
    136   ///
    137   /// If no file is found, this returns 0, otherwise it returns the buffer ID
    138   /// of the stacked file. The full path to the included file can be found in
    139   /// \p IncludedFile.
    140   unsigned AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc,
    141                           std::string &IncludedFile);
    142 
    143   /// Return the ID of the buffer containing the specified location.
    144   ///
    145   /// 0 is returned if the buffer is not found.
    146   unsigned FindBufferContainingLoc(SMLoc Loc) const;
    147 
    148   /// Find the line number for the specified location in the specified file.
    149   /// This is not a fast method.
    150   unsigned FindLineNumber(SMLoc Loc, unsigned BufferID = 0) const {
    151     return getLineAndColumn(Loc, BufferID).first;
    152   }
    153 
    154   /// Find the line and column number for the specified location in the
    155   /// specified file. This is not a fast method.
    156   std::pair<unsigned, unsigned> getLineAndColumn(SMLoc Loc,
    157                                                  unsigned BufferID = 0) const;
    158 
    159   /// Emit a message about the specified location with the specified string.
    160   ///
    161   /// \param ShowColors Display colored messages if output is a terminal and
    162   /// the default error handler is used.
    163   void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind,
    164                     const Twine &Msg,
    165                     ArrayRef<SMRange> Ranges = None,
    166                     ArrayRef<SMFixIt> FixIts = None,
    167                     bool ShowColors = true) const;
    168 
    169   /// Emits a diagnostic to llvm::errs().
    170   void PrintMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg,
    171                     ArrayRef<SMRange> Ranges = None,
    172                     ArrayRef<SMFixIt> FixIts = None,
    173                     bool ShowColors = true) const;
    174 
    175   /// Emits a manually-constructed diagnostic to the given output stream.
    176   ///
    177   /// \param ShowColors Display colored messages if output is a terminal and
    178   /// the default error handler is used.
    179   void PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic,
    180                     bool ShowColors = true) const;
    181 
    182   /// Return an SMDiagnostic at the specified location with the specified
    183   /// string.
    184   ///
    185   /// \param Msg If non-null, the kind of message (e.g., "error") which is
    186   /// prefixed to the message.
    187   SMDiagnostic GetMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg,
    188                           ArrayRef<SMRange> Ranges = None,
    189                           ArrayRef<SMFixIt> FixIts = None) const;
    190 
    191   /// Prints the names of included files and the line of the file they were
    192   /// included from. A diagnostic handler can use this before printing its
    193   /// custom formatted message.
    194   ///
    195   /// \param IncludeLoc The location of the include.
    196   /// \param OS the raw_ostream to print on.
    197   void PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const;
    198 };
    199 
    200 /// Represents a single fixit, a replacement of one range of text with another.
    201 class SMFixIt {
    202   SMRange Range;
    203 
    204   std::string Text;
    205 
    206 public:
    207   // FIXME: Twine.str() is not very efficient.
    208   SMFixIt(SMLoc Loc, const Twine &Insertion)
    209     : Range(Loc, Loc), Text(Insertion.str()) {
    210     assert(Loc.isValid());
    211   }
    212 
    213   // FIXME: Twine.str() is not very efficient.
    214   SMFixIt(SMRange R, const Twine &Replacement)
    215     : Range(R), Text(Replacement.str()) {
    216     assert(R.isValid());
    217   }
    218 
    219   StringRef getText() const { return Text; }
    220   SMRange getRange() const { return Range; }
    221 
    222   bool operator<(const SMFixIt &Other) const {
    223     if (Range.Start.getPointer() != Other.Range.Start.getPointer())
    224       return Range.Start.getPointer() < Other.Range.Start.getPointer();
    225     if (Range.End.getPointer() != Other.Range.End.getPointer())
    226       return Range.End.getPointer() < Other.Range.End.getPointer();
    227     return Text < Other.Text;
    228   }
    229 };
    230 
    231 /// Instances of this class encapsulate one diagnostic report, allowing
    232 /// printing to a raw_ostream as a caret diagnostic.
    233 class SMDiagnostic {
    234   const SourceMgr *SM = nullptr;
    235   SMLoc Loc;
    236   std::string Filename;
    237   int LineNo = 0;
    238   int ColumnNo = 0;
    239   SourceMgr::DiagKind Kind = SourceMgr::DK_Error;
    240   std::string Message, LineContents;
    241   std::vector<std::pair<unsigned, unsigned>> Ranges;
    242   SmallVector<SMFixIt, 4> FixIts;
    243 
    244 public:
    245   // Null diagnostic.
    246   SMDiagnostic() = default;
    247   // Diagnostic with no location (e.g. file not found, command line arg error).
    248   SMDiagnostic(StringRef filename, SourceMgr::DiagKind Knd, StringRef Msg)
    249     : Filename(filename), LineNo(-1), ColumnNo(-1), Kind(Knd), Message(Msg) {}
    250 
    251   // Diagnostic with a location.
    252   SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN,
    253                int Line, int Col, SourceMgr::DiagKind Kind,
    254                StringRef Msg, StringRef LineStr,
    255                ArrayRef<std::pair<unsigned,unsigned>> Ranges,
    256                ArrayRef<SMFixIt> FixIts = None);
    257 
    258   const SourceMgr *getSourceMgr() const { return SM; }
    259   SMLoc getLoc() const { return Loc; }
    260   StringRef getFilename() const { return Filename; }
    261   int getLineNo() const { return LineNo; }
    262   int getColumnNo() const { return ColumnNo; }
    263   SourceMgr::DiagKind getKind() const { return Kind; }
    264   StringRef getMessage() const { return Message; }
    265   StringRef getLineContents() const { return LineContents; }
    266   ArrayRef<std::pair<unsigned, unsigned>> getRanges() const { return Ranges; }
    267 
    268   void addFixIt(const SMFixIt &Hint) {
    269     FixIts.push_back(Hint);
    270   }
    271 
    272   ArrayRef<SMFixIt> getFixIts() const {
    273     return FixIts;
    274   }
    275 
    276   void print(const char *ProgName, raw_ostream &S, bool ShowColors = true,
    277              bool ShowKindLabel = true) const;
    278 };
    279 
    280 } // end namespace llvm
    281 
    282 #endif // LLVM_SUPPORT_SOURCEMGR_H
    283