xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Support/SourceMgr.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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