Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 14 | pmbaty | 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 |   /// 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(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(SrcMgr.Buffers.begin(), SrcMgr.Buffers.end(), | ||
| 164 | std::back_inserter(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(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 |