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