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