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