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