1 //===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file implements the SourceMgr class. This class is used as a simple
11 // substrate for diagnostics, #include handling, and other low level things for
14 //===----------------------------------------------------------------------===//
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/Twine.h"
19 #include "llvm/Support/Locale.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include <system_error>
26 static const size_t TabStop
= 8;
29 struct LineNoCacheTy
{
30 unsigned LastQueryBufferID
;
31 const char *LastQuery
;
32 unsigned LineNoOfQuery
;
36 static LineNoCacheTy
*getCache(void *Ptr
) {
37 return (LineNoCacheTy
*)Ptr
;
41 SourceMgr::~SourceMgr() {
42 // Delete the line # cache if allocated.
43 if (LineNoCacheTy
*Cache
= getCache(LineNoCache
))
47 unsigned SourceMgr::AddIncludeFile(const std::string
&Filename
,
49 std::string
&IncludedFile
) {
50 IncludedFile
= Filename
;
51 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> NewBufOrErr
=
52 MemoryBuffer::getFile(IncludedFile
);
54 // If the file didn't exist directly, see if it's in an include path.
55 for (unsigned i
= 0, e
= IncludeDirectories
.size(); i
!= e
&& !NewBufOrErr
;
58 IncludeDirectories
[i
] + sys::path::get_separator().data() + Filename
;
59 NewBufOrErr
= MemoryBuffer::getFile(IncludedFile
);
65 return AddNewSourceBuffer(std::move(*NewBufOrErr
), IncludeLoc
);
68 unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc
) const {
69 for (unsigned i
= 0, e
= Buffers
.size(); i
!= e
; ++i
)
70 if (Loc
.getPointer() >= Buffers
[i
].Buffer
->getBufferStart() &&
71 // Use <= here so that a pointer to the null at the end of the buffer
72 // is included as part of the buffer.
73 Loc
.getPointer() <= Buffers
[i
].Buffer
->getBufferEnd())
78 std::pair
<unsigned, unsigned>
79 SourceMgr::getLineAndColumn(SMLoc Loc
, unsigned BufferID
) const {
81 BufferID
= FindBufferContainingLoc(Loc
);
82 assert(BufferID
&& "Invalid Location!");
84 const MemoryBuffer
*Buff
= getMemoryBuffer(BufferID
);
86 // Count the number of \n's between the start of the file and the specified
90 const char *BufStart
= Buff
->getBufferStart();
91 const char *Ptr
= BufStart
;
93 // If we have a line number cache, and if the query is to a later point in the
94 // same file, start searching from the last query location. This optimizes
95 // for the case when multiple diagnostics come out of one file in order.
96 if (LineNoCacheTy
*Cache
= getCache(LineNoCache
))
97 if (Cache
->LastQueryBufferID
== BufferID
&&
98 Cache
->LastQuery
<= Loc
.getPointer()) {
99 Ptr
= Cache
->LastQuery
;
100 LineNo
= Cache
->LineNoOfQuery
;
103 // Scan for the location being queried, keeping track of the number of lines
105 for (; SMLoc::getFromPointer(Ptr
) != Loc
; ++Ptr
)
106 if (*Ptr
== '\n') ++LineNo
;
108 // Allocate the line number cache if it doesn't exist.
110 LineNoCache
= new LineNoCacheTy();
112 // Update the line # cache.
113 LineNoCacheTy
&Cache
= *getCache(LineNoCache
);
114 Cache
.LastQueryBufferID
= BufferID
;
115 Cache
.LastQuery
= Ptr
;
116 Cache
.LineNoOfQuery
= LineNo
;
118 size_t NewlineOffs
= StringRef(BufStart
, Ptr
-BufStart
).find_last_of("\n\r");
119 if (NewlineOffs
== StringRef::npos
) NewlineOffs
= ~(size_t)0;
120 return std::make_pair(LineNo
, Ptr
-BufStart
-NewlineOffs
);
123 void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc
, raw_ostream
&OS
) const {
124 if (IncludeLoc
== SMLoc()) return; // Top of stack.
126 unsigned CurBuf
= FindBufferContainingLoc(IncludeLoc
);
127 assert(CurBuf
&& "Invalid or unspecified location!");
129 PrintIncludeStack(getBufferInfo(CurBuf
).IncludeLoc
, OS
);
131 OS
<< "Included from "
132 << getBufferInfo(CurBuf
).Buffer
->getBufferIdentifier()
133 << ":" << FindLineNumber(IncludeLoc
, CurBuf
) << ":\n";
137 SMDiagnostic
SourceMgr::GetMessage(SMLoc Loc
, SourceMgr::DiagKind Kind
,
139 ArrayRef
<SMRange
> Ranges
,
140 ArrayRef
<SMFixIt
> FixIts
) const {
142 // First thing to do: find the current buffer containing the specified
143 // location to pull out the source line.
144 SmallVector
<std::pair
<unsigned, unsigned>, 4> ColRanges
;
145 std::pair
<unsigned, unsigned> LineAndCol
;
146 const char *BufferID
= "<unknown>";
150 unsigned CurBuf
= FindBufferContainingLoc(Loc
);
151 assert(CurBuf
&& "Invalid or unspecified location!");
153 const MemoryBuffer
*CurMB
= getMemoryBuffer(CurBuf
);
154 BufferID
= CurMB
->getBufferIdentifier();
156 // Scan backward to find the start of the line.
157 const char *LineStart
= Loc
.getPointer();
158 const char *BufStart
= CurMB
->getBufferStart();
159 while (LineStart
!= BufStart
&& LineStart
[-1] != '\n' &&
160 LineStart
[-1] != '\r')
163 // Get the end of the line.
164 const char *LineEnd
= Loc
.getPointer();
165 const char *BufEnd
= CurMB
->getBufferEnd();
166 while (LineEnd
!= BufEnd
&& LineEnd
[0] != '\n' && LineEnd
[0] != '\r')
168 LineStr
= std::string(LineStart
, LineEnd
);
170 // Convert any ranges to column ranges that only intersect the line of the
172 for (unsigned i
= 0, e
= Ranges
.size(); i
!= e
; ++i
) {
173 SMRange R
= Ranges
[i
];
174 if (!R
.isValid()) continue;
176 // If the line doesn't contain any part of the range, then ignore it.
177 if (R
.Start
.getPointer() > LineEnd
|| R
.End
.getPointer() < LineStart
)
180 // Ignore pieces of the range that go onto other lines.
181 if (R
.Start
.getPointer() < LineStart
)
182 R
.Start
= SMLoc::getFromPointer(LineStart
);
183 if (R
.End
.getPointer() > LineEnd
)
184 R
.End
= SMLoc::getFromPointer(LineEnd
);
186 // Translate from SMLoc ranges to column ranges.
187 // FIXME: Handle multibyte characters.
188 ColRanges
.push_back(std::make_pair(R
.Start
.getPointer()-LineStart
,
189 R
.End
.getPointer()-LineStart
));
192 LineAndCol
= getLineAndColumn(Loc
, CurBuf
);
195 return SMDiagnostic(*this, Loc
, BufferID
, LineAndCol
.first
,
196 LineAndCol
.second
-1, Kind
, Msg
.str(),
197 LineStr
, ColRanges
, FixIts
);
200 void SourceMgr::PrintMessage(raw_ostream
&OS
, const SMDiagnostic
&Diagnostic
,
201 bool ShowColors
) const {
202 // Report the message with the diagnostic handler if present.
204 DiagHandler(Diagnostic
, DiagContext
);
208 if (Diagnostic
.getLoc().isValid()) {
209 unsigned CurBuf
= FindBufferContainingLoc(Diagnostic
.getLoc());
210 assert(CurBuf
&& "Invalid or unspecified location!");
211 PrintIncludeStack(getBufferInfo(CurBuf
).IncludeLoc
, OS
);
214 Diagnostic
.print(nullptr, OS
, ShowColors
);
217 void SourceMgr::PrintMessage(raw_ostream
&OS
, SMLoc Loc
,
218 SourceMgr::DiagKind Kind
,
219 const Twine
&Msg
, ArrayRef
<SMRange
> Ranges
,
220 ArrayRef
<SMFixIt
> FixIts
, bool ShowColors
) const {
221 PrintMessage(OS
, GetMessage(Loc
, Kind
, Msg
, Ranges
, FixIts
), ShowColors
);
224 void SourceMgr::PrintMessage(SMLoc Loc
, SourceMgr::DiagKind Kind
,
225 const Twine
&Msg
, ArrayRef
<SMRange
> Ranges
,
226 ArrayRef
<SMFixIt
> FixIts
, bool ShowColors
) const {
227 PrintMessage(llvm::errs(), Loc
, Kind
, Msg
, Ranges
, FixIts
, ShowColors
);
230 //===----------------------------------------------------------------------===//
231 // SMDiagnostic Implementation
232 //===----------------------------------------------------------------------===//
234 SMDiagnostic::SMDiagnostic(const SourceMgr
&sm
, SMLoc L
, StringRef FN
,
235 int Line
, int Col
, SourceMgr::DiagKind Kind
,
236 StringRef Msg
, StringRef LineStr
,
237 ArrayRef
<std::pair
<unsigned,unsigned> > Ranges
,
238 ArrayRef
<SMFixIt
> Hints
)
239 : SM(&sm
), Loc(L
), Filename(FN
), LineNo(Line
), ColumnNo(Col
), Kind(Kind
),
240 Message(Msg
), LineContents(LineStr
), Ranges(Ranges
.vec()),
241 FixIts(Hints
.begin(), Hints
.end()) {
242 std::sort(FixIts
.begin(), FixIts
.end());
245 static void buildFixItLine(std::string
&CaretLine
, std::string
&FixItLine
,
246 ArrayRef
<SMFixIt
> FixIts
, ArrayRef
<char> SourceLine
){
250 const char *LineStart
= SourceLine
.begin();
251 const char *LineEnd
= SourceLine
.end();
253 size_t PrevHintEndCol
= 0;
255 for (ArrayRef
<SMFixIt
>::iterator I
= FixIts
.begin(), E
= FixIts
.end();
257 // If the fixit contains a newline or tab, ignore it.
258 if (I
->getText().find_first_of("\n\r\t") != StringRef::npos
)
261 SMRange R
= I
->getRange();
263 // If the line doesn't contain any part of the range, then ignore it.
264 if (R
.Start
.getPointer() > LineEnd
|| R
.End
.getPointer() < LineStart
)
267 // Translate from SMLoc to column.
268 // Ignore pieces of the range that go onto other lines.
269 // FIXME: Handle multibyte characters in the source line.
271 if (R
.Start
.getPointer() < LineStart
)
274 FirstCol
= R
.Start
.getPointer() - LineStart
;
276 // If we inserted a long previous hint, push this one forwards, and add
277 // an extra space to show that this is not part of the previous
278 // completion. This is sort of the best we can do when two hints appear
281 // Note that if this hint is located immediately after the previous
282 // hint, no space will be added, since the location is more important.
283 unsigned HintCol
= FirstCol
;
284 if (HintCol
< PrevHintEndCol
)
285 HintCol
= PrevHintEndCol
+ 1;
287 // FIXME: This assertion is intended to catch unintended use of multibyte
288 // characters in fixits. If we decide to do this, we'll have to track
289 // separate byte widths for the source and fixit lines.
290 assert((size_t)llvm::sys::locale::columnWidth(I
->getText()) ==
291 I
->getText().size());
293 // This relies on one byte per column in our fixit hints.
294 unsigned LastColumnModified
= HintCol
+ I
->getText().size();
295 if (LastColumnModified
> FixItLine
.size())
296 FixItLine
.resize(LastColumnModified
, ' ');
298 std::copy(I
->getText().begin(), I
->getText().end(),
299 FixItLine
.begin() + HintCol
);
301 PrevHintEndCol
= LastColumnModified
;
303 // For replacements, mark the removal range with '~'.
304 // FIXME: Handle multibyte characters in the source line.
306 if (R
.End
.getPointer() >= LineEnd
)
307 LastCol
= LineEnd
- LineStart
;
309 LastCol
= R
.End
.getPointer() - LineStart
;
311 std::fill(&CaretLine
[FirstCol
], &CaretLine
[LastCol
], '~');
315 static void printSourceLine(raw_ostream
&S
, StringRef LineContents
) {
316 // Print out the source line one character at a time, so we can expand tabs.
317 for (unsigned i
= 0, e
= LineContents
.size(), OutCol
= 0; i
!= e
; ++i
) {
318 if (LineContents
[i
] != '\t') {
319 S
<< LineContents
[i
];
324 // If we have a tab, emit at least one space, then round up to 8 columns.
328 } while ((OutCol
% TabStop
) != 0);
333 static bool isNonASCII(char c
) {
337 void SMDiagnostic::print(const char *ProgName
, raw_ostream
&S
,
338 bool ShowColors
) const {
339 // Display colors only if OS supports colors.
340 ShowColors
&= S
.has_colors();
343 S
.changeColor(raw_ostream::SAVEDCOLOR
, true);
345 if (ProgName
&& ProgName
[0])
346 S
<< ProgName
<< ": ";
348 if (!Filename
.empty()) {
357 S
<< ':' << (ColumnNo
+1);
363 case SourceMgr::DK_Error
:
365 S
.changeColor(raw_ostream::RED
, true);
368 case SourceMgr::DK_Warning
:
370 S
.changeColor(raw_ostream::MAGENTA
, true);
373 case SourceMgr::DK_Note
:
375 S
.changeColor(raw_ostream::BLACK
, true);
382 S
.changeColor(raw_ostream::SAVEDCOLOR
, true);
385 S
<< Message
<< '\n';
390 if (LineNo
== -1 || ColumnNo
== -1)
393 // FIXME: If there are multibyte or multi-column characters in the source, all
394 // our ranges will be wrong. To do this properly, we'll need a byte-to-column
395 // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
396 // expanding them later, and bail out rather than show incorrect ranges and
397 // misaligned fixits for any other odd characters.
398 if (std::find_if(LineContents
.begin(), LineContents
.end(), isNonASCII
) !=
399 LineContents
.end()) {
400 printSourceLine(S
, LineContents
);
403 size_t NumColumns
= LineContents
.size();
405 // Build the line with the caret and ranges.
406 std::string
CaretLine(NumColumns
+1, ' ');
408 // Expand any ranges.
409 for (unsigned r
= 0, e
= Ranges
.size(); r
!= e
; ++r
) {
410 std::pair
<unsigned, unsigned> R
= Ranges
[r
];
411 std::fill(&CaretLine
[R
.first
],
412 &CaretLine
[std::min((size_t)R
.second
, CaretLine
.size())],
417 // FIXME: Find the beginning of the line properly for multibyte characters.
418 std::string FixItInsertionLine
;
419 buildFixItLine(CaretLine
, FixItInsertionLine
, FixIts
,
420 makeArrayRef(Loc
.getPointer() - ColumnNo
,
421 LineContents
.size()));
423 // Finally, plop on the caret.
424 if (unsigned(ColumnNo
) <= NumColumns
)
425 CaretLine
[ColumnNo
] = '^';
427 CaretLine
[NumColumns
] = '^';
429 // ... and remove trailing whitespace so the output doesn't wrap for it. We
430 // know that the line isn't completely empty because it has the caret in it at
432 CaretLine
.erase(CaretLine
.find_last_not_of(' ')+1);
434 printSourceLine(S
, LineContents
);
437 S
.changeColor(raw_ostream::GREEN
, true);
439 // Print out the caret line, matching tabs in the source line.
440 for (unsigned i
= 0, e
= CaretLine
.size(), OutCol
= 0; i
!= e
; ++i
) {
441 if (i
>= LineContents
.size() || LineContents
[i
] != '\t') {
447 // Okay, we have a tab. Insert the appropriate number of characters.
451 } while ((OutCol
% TabStop
) != 0);
458 // Print out the replacement line, matching tabs in the source line.
459 if (FixItInsertionLine
.empty())
462 for (size_t i
= 0, e
= FixItInsertionLine
.size(), OutCol
= 0; i
< e
; ++i
) {
463 if (i
>= LineContents
.size() || LineContents
[i
] != '\t') {
464 S
<< FixItInsertionLine
[i
];
469 // Okay, we have a tab. Insert the appropriate number of characters.
471 S
<< FixItInsertionLine
[i
];
472 // FIXME: This is trying not to break up replacements, but then to re-sync
473 // with the tabs between replacements. This will fail, though, if two
474 // fix-it replacements are exactly adjacent, or if a fix-it contains a
475 // space. Really we should be precomputing column widths, which we'll
476 // need anyway for multibyte chars.
477 if (FixItInsertionLine
[i
] != ' ')
480 } while (((OutCol
% TabStop
) != 0) && i
!= e
);