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