| 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 |     , | 
| 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 |   /// Return the include directories of this source manager. | 
| 104 |   ArrayRef<std::string> getIncludeDirs() const { return IncludeDirectories; } | 
| 105 |  | 
| 106 |   void setIncludeDirs(const std::vector<std::string> &Dirs) { | 
| 107 |     IncludeDirectories = Dirs; | 
| 108 |   } | 
| 109 |  | 
| 110 |   /// Specify a diagnostic handler to be invoked every time PrintMessage is | 
| 111 |   /// called. \p Ctx is passed into the handler when it is invoked. | 
| 112 |   void setDiagHandler(DiagHandlerTy DH, void *Ctx = nullptr) { | 
| 113 |     DiagHandler = DH; | 
| 114 |     DiagContext = Ctx; | 
| 115 |   } | 
| 116 |  | 
| 117 |   DiagHandlerTy getDiagHandler() const { return DiagHandler; } | 
| 118 |   void *getDiagContext() const { return DiagContext; } | 
| 119 |  | 
| 120 |   const SrcBuffer &getBufferInfo(unsigned i) const { | 
| 121 |     assert(isValidBufferID(i)); | 
| 122 |     return Buffers[i - 1]; | 
| 123 |   } | 
| 124 |  | 
| 125 |   const MemoryBuffer *getMemoryBuffer(unsigned i) const { | 
| 126 |     assert(isValidBufferID(i)); | 
| 127 |     return Buffers[i - 1].Buffer.get(); | 
| 128 |   } | 
| 129 |  | 
| 130 |   unsigned getNumBuffers() const { return Buffers.size(); } | 
| 131 |  | 
| 132 |   unsigned getMainFileID() const { | 
| 133 |     assert(getNumBuffers()); | 
| 134 |     return 1; | 
| 135 |   } | 
| 136 |  | 
| 137 |   SMLoc getParentIncludeLoc(unsigned i) const { | 
| 138 |     assert(isValidBufferID(i)); | 
| 139 |     return Buffers[i - 1].IncludeLoc; | 
| 140 |   } | 
| 141 |  | 
| 142 |   /// Add a new source buffer to this source manager. This takes ownership of | 
| 143 |   /// the memory buffer. | 
| 144 |   unsigned AddNewSourceBuffer(std::unique_ptr<MemoryBuffer> F, | 
| 145 |                               SMLoc IncludeLoc) { | 
| 146 |     SrcBuffer NB; | 
| 147 |     NB.Buffer = std::move(F); | 
| 148 |     NB.IncludeLoc = IncludeLoc; | 
| 149 |     Buffers.push_back(x: std::move(NB)); | 
| 150 |     return Buffers.size(); | 
| 151 |   } | 
| 152 |  | 
| 153 |   /// Takes the source buffers from the given source manager and append them to | 
| 154 |   /// the current manager. `MainBufferIncludeLoc` is an optional include | 
| 155 |   /// location to attach to the main buffer of `SrcMgr` after it gets moved to | 
| 156 |   /// the current manager. | 
| 157 |   void takeSourceBuffersFrom(SourceMgr &SrcMgr, | 
| 158 |                              SMLoc MainBufferIncludeLoc = SMLoc()) { | 
| 159 |     if (SrcMgr.Buffers.empty()) | 
| 160 |       return; | 
| 161 |  | 
| 162 |     size_t OldNumBuffers = getNumBuffers(); | 
| 163 |     std::move(first: SrcMgr.Buffers.begin(), last: SrcMgr.Buffers.end(), | 
| 164 |               result: std::back_inserter(x&: Buffers)); | 
| 165 |     SrcMgr.Buffers.clear(); | 
| 166 |     Buffers[OldNumBuffers].IncludeLoc = MainBufferIncludeLoc; | 
| 167 |   } | 
| 168 |  | 
| 169 |   /// Search for a file with the specified name in the current directory or in | 
| 170 |   /// one of the IncludeDirs. | 
| 171 |   /// | 
| 172 |   /// If no file is found, this returns 0, otherwise it returns the buffer ID | 
| 173 |   /// of the stacked file. The full path to the included file can be found in | 
| 174 |   /// \p IncludedFile. | 
| 175 |   unsigned AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc, | 
| 176 |                           std::string &IncludedFile); | 
| 177 |  | 
| 178 |   /// Search for a file with the specified name in the current directory or in | 
| 179 |   /// one of the IncludeDirs, and try to open it **without** adding to the | 
| 180 |   /// SourceMgr. If the opened file is intended to be added to the source | 
| 181 |   /// manager, prefer `AddIncludeFile` instead. | 
| 182 |   /// | 
| 183 |   /// If no file is found, this returns an Error, otherwise it returns the | 
| 184 |   /// buffer of the stacked file. The full path to the included file can be | 
| 185 |   /// found in \p IncludedFile. | 
| 186 |   ErrorOr<std::unique_ptr<MemoryBuffer>> | 
| 187 |   OpenIncludeFile(const std::string &Filename, std::string &IncludedFile); | 
| 188 |  | 
| 189 |   /// Return the ID of the buffer containing the specified location. | 
| 190 |   /// | 
| 191 |   /// 0 is returned if the buffer is not found. | 
| 192 |   unsigned FindBufferContainingLoc(SMLoc Loc) const; | 
| 193 |  | 
| 194 |   /// Find the line number for the specified location in the specified file. | 
| 195 |   /// This is not a fast method. | 
| 196 |   unsigned FindLineNumber(SMLoc Loc, unsigned BufferID = 0) const { | 
| 197 |     return getLineAndColumn(Loc, BufferID).first; | 
| 198 |   } | 
| 199 |  | 
| 200 |   /// Find the line and column number for the specified location in the | 
| 201 |   /// specified file. This is not a fast method. | 
| 202 |   std::pair<unsigned, unsigned> getLineAndColumn(SMLoc Loc, | 
| 203 |                                                  unsigned BufferID = 0) const; | 
| 204 |  | 
| 205 |   /// Get a string with the \p SMLoc filename and line number | 
| 206 |   /// formatted in the standard style. | 
| 207 |   std::string getFormattedLocationNoOffset(SMLoc Loc, | 
| 208 |                                            bool IncludePath = false) const; | 
| 209 |  | 
| 210 |   /// Given a line and column number in a mapped buffer, turn it into an SMLoc. | 
| 211 |   /// This will return a null SMLoc if the line/column location is invalid. | 
| 212 |   SMLoc FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, | 
| 213 |                                 unsigned ColNo); | 
| 214 |  | 
| 215 |   /// Emit a message about the specified location with the specified string. | 
| 216 |   /// | 
| 217 |   /// \param ShowColors Display colored messages if output is a terminal and | 
| 218 |   /// the default error handler is used. | 
| 219 |   void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind, const Twine &Msg, | 
| 220 |                     ArrayRef<SMRange> Ranges = {}, | 
| 221 |                     ArrayRef<SMFixIt> FixIts = {}, | 
| 222 |                     bool ShowColors = true) const; | 
| 223 |  | 
| 224 |   /// Emits a diagnostic to llvm::errs(). | 
| 225 |   void PrintMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, | 
| 226 |                     ArrayRef<SMRange> Ranges = {}, | 
| 227 |                     ArrayRef<SMFixIt> FixIts = {}, | 
| 228 |                     bool ShowColors = true) const; | 
| 229 |  | 
| 230 |   /// Emits a manually-constructed diagnostic to the given output stream. | 
| 231 |   /// | 
| 232 |   /// \param ShowColors Display colored messages if output is a terminal and | 
| 233 |   /// the default error handler is used. | 
| 234 |   void PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, | 
| 235 |                     bool ShowColors = true) const; | 
| 236 |  | 
| 237 |   /// Return an SMDiagnostic at the specified location with the specified | 
| 238 |   /// string. | 
| 239 |   /// | 
| 240 |   /// \param Msg If non-null, the kind of message (e.g., "error") which is | 
| 241 |   /// prefixed to the message. | 
| 242 |   SMDiagnostic GetMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, | 
| 243 |                           ArrayRef<SMRange> Ranges = {}, | 
| 244 |                           ArrayRef<SMFixIt> FixIts = {}) const; | 
| 245 |  | 
| 246 |   /// Prints the names of included files and the line of the file they were | 
| 247 |   /// included from. A diagnostic handler can use this before printing its | 
| 248 |   /// custom formatted message. | 
| 249 |   /// | 
| 250 |   /// \param IncludeLoc The location of the include. | 
| 251 |   /// \param OS the raw_ostream to print on. | 
| 252 |   void PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const; | 
| 253 | }; | 
| 254 |  | 
| 255 | /// Represents a single fixit, a replacement of one range of text with another. | 
| 256 | class SMFixIt { | 
| 257 |   SMRange Range; | 
| 258 |  | 
| 259 |   std::string Text; | 
| 260 |  | 
| 261 | public: | 
| 262 |   SMFixIt(SMRange R, const Twine &Replacement); | 
| 263 |  | 
| 264 |   SMFixIt(SMLoc Loc, const Twine &Replacement) | 
| 265 |       : SMFixIt(SMRange(Loc, Loc), Replacement) {} | 
| 266 |  | 
| 267 |   StringRef getText() const { return Text; } | 
| 268 |   SMRange getRange() const { return Range; } | 
| 269 |  | 
| 270 |   bool operator<(const SMFixIt &Other) const { | 
| 271 |     if (Range.Start.getPointer() != Other.Range.Start.getPointer()) | 
| 272 |       return Range.Start.getPointer() < Other.Range.Start.getPointer(); | 
| 273 |     if (Range.End.getPointer() != Other.Range.End.getPointer()) | 
| 274 |       return Range.End.getPointer() < Other.Range.End.getPointer(); | 
| 275 |     return Text < Other.Text; | 
| 276 |   } | 
| 277 | }; | 
| 278 |  | 
| 279 | /// Instances of this class encapsulate one diagnostic report, allowing | 
| 280 | /// printing to a raw_ostream as a caret diagnostic. | 
| 281 | class SMDiagnostic { | 
| 282 |   const SourceMgr *SM = nullptr; | 
| 283 |   SMLoc Loc; | 
| 284 |   std::string Filename; | 
| 285 |   int LineNo = 0; | 
| 286 |   int ColumnNo = 0; | 
| 287 |   SourceMgr::DiagKind Kind = SourceMgr::DK_Error; | 
| 288 |   std::string Message, LineContents; | 
| 289 |   std::vector<std::pair<unsigned, unsigned>> Ranges; | 
| 290 |   SmallVector<SMFixIt, 4> FixIts; | 
| 291 |  | 
| 292 | public: | 
| 293 |   // Null diagnostic. | 
| 294 |   SMDiagnostic() = default; | 
| 295 |   // Diagnostic with no location (e.g. file not found, command line arg error). | 
| 296 |   SMDiagnostic(StringRef filename, SourceMgr::DiagKind Knd, StringRef Msg) | 
| 297 |       : Filename(filename), LineNo(-1), ColumnNo(-1), Kind(Knd), Message(Msg) {} | 
| 298 |  | 
| 299 |   // Diagnostic with a location. | 
| 300 |   SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line, int Col, | 
| 301 |                SourceMgr::DiagKind Kind, StringRef Msg, StringRef LineStr, | 
| 302 |                ArrayRef<std::pair<unsigned, unsigned>> Ranges, | 
| 303 |                ArrayRef<SMFixIt> FixIts = {}); | 
| 304 |  | 
| 305 |   const SourceMgr *getSourceMgr() const { return SM; } | 
| 306 |   SMLoc getLoc() const { return Loc; } | 
| 307 |   StringRef getFilename() const { return Filename; } | 
| 308 |   int getLineNo() const { return LineNo; } | 
| 309 |   int getColumnNo() const { return ColumnNo; } | 
| 310 |   SourceMgr::DiagKind getKind() const { return Kind; } | 
| 311 |   StringRef getMessage() const { return Message; } | 
| 312 |   StringRef getLineContents() const { return LineContents; } | 
| 313 |   ArrayRef<std::pair<unsigned, unsigned>> getRanges() const { return Ranges; } | 
| 314 |  | 
| 315 |   void addFixIt(const SMFixIt &Hint) { FixIts.push_back(Elt: Hint); } | 
| 316 |  | 
| 317 |   ArrayRef<SMFixIt> getFixIts() const { return FixIts; } | 
| 318 |  | 
| 319 |   void print(const char *ProgName, raw_ostream &S, bool ShowColors = true, | 
| 320 |              bool ShowKindLabel = true) const; | 
| 321 | }; | 
| 322 |  | 
| 323 | } // end namespace llvm | 
| 324 |  | 
| 325 | #endif // LLVM_SUPPORT_SOURCEMGR_H | 
| 326 |  |