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