]>
Commit | Line | Data |
---|---|---|
223e47cc LB |
1 | //===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===// |
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 implements the SourceMgr class. This class is used as a simple | |
11 | // substrate for diagnostics, #include handling, and other low level things for | |
12 | // simple parsers. | |
13 | // | |
14 | //===----------------------------------------------------------------------===// | |
15 | ||
223e47cc | 16 | #include "llvm/Support/SourceMgr.h" |
970d7e83 LB |
17 | #include "llvm/ADT/SmallString.h" |
18 | #include "llvm/ADT/Twine.h" | |
19 | #include "llvm/Support/Locale.h" | |
20 | #include "llvm/Support/MemoryBuffer.h" | |
1a4d82fc | 21 | #include "llvm/Support/Path.h" |
223e47cc | 22 | #include "llvm/Support/raw_ostream.h" |
1a4d82fc | 23 | #include <system_error> |
223e47cc LB |
24 | using namespace llvm; |
25 | ||
970d7e83 LB |
26 | static const size_t TabStop = 8; |
27 | ||
223e47cc LB |
28 | namespace { |
29 | struct LineNoCacheTy { | |
1a4d82fc | 30 | unsigned LastQueryBufferID; |
223e47cc LB |
31 | const char *LastQuery; |
32 | unsigned LineNoOfQuery; | |
33 | }; | |
34 | } | |
35 | ||
36 | static LineNoCacheTy *getCache(void *Ptr) { | |
37 | return (LineNoCacheTy*)Ptr; | |
38 | } | |
39 | ||
40 | ||
41 | SourceMgr::~SourceMgr() { | |
42 | // Delete the line # cache if allocated. | |
43 | if (LineNoCacheTy *Cache = getCache(LineNoCache)) | |
44 | delete Cache; | |
223e47cc LB |
45 | } |
46 | ||
223e47cc LB |
47 | unsigned SourceMgr::AddIncludeFile(const std::string &Filename, |
48 | SMLoc IncludeLoc, | |
49 | std::string &IncludedFile) { | |
223e47cc | 50 | IncludedFile = Filename; |
1a4d82fc | 51 | ErrorOr<std::unique_ptr<MemoryBuffer>> NewBufOrErr = |
85aaf69f | 52 | MemoryBuffer::getFile(IncludedFile); |
223e47cc LB |
53 | |
54 | // If the file didn't exist directly, see if it's in an include path. | |
1a4d82fc JJ |
55 | for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr; |
56 | ++i) { | |
57 | IncludedFile = | |
58 | IncludeDirectories[i] + sys::path::get_separator().data() + Filename; | |
85aaf69f | 59 | NewBufOrErr = MemoryBuffer::getFile(IncludedFile); |
223e47cc LB |
60 | } |
61 | ||
1a4d82fc JJ |
62 | if (!NewBufOrErr) |
63 | return 0; | |
223e47cc | 64 | |
1a4d82fc | 65 | return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc); |
223e47cc LB |
66 | } |
67 | ||
1a4d82fc | 68 | unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const { |
223e47cc LB |
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()) | |
1a4d82fc JJ |
74 | return i + 1; |
75 | return 0; | |
223e47cc LB |
76 | } |
77 | ||
223e47cc | 78 | std::pair<unsigned, unsigned> |
1a4d82fc JJ |
79 | SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { |
80 | if (!BufferID) | |
81 | BufferID = FindBufferContainingLoc(Loc); | |
82 | assert(BufferID && "Invalid Location!"); | |
223e47cc | 83 | |
1a4d82fc | 84 | const MemoryBuffer *Buff = getMemoryBuffer(BufferID); |
223e47cc LB |
85 | |
86 | // Count the number of \n's between the start of the file and the specified | |
87 | // location. | |
88 | unsigned LineNo = 1; | |
89 | ||
90 | const char *BufStart = Buff->getBufferStart(); | |
91 | const char *Ptr = BufStart; | |
92 | ||
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; | |
101 | } | |
102 | ||
103 | // Scan for the location being queried, keeping track of the number of lines | |
104 | // we see. | |
105 | for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr) | |
106 | if (*Ptr == '\n') ++LineNo; | |
107 | ||
108 | // Allocate the line number cache if it doesn't exist. | |
1a4d82fc | 109 | if (!LineNoCache) |
223e47cc LB |
110 | LineNoCache = new LineNoCacheTy(); |
111 | ||
112 | // Update the line # cache. | |
113 | LineNoCacheTy &Cache = *getCache(LineNoCache); | |
114 | Cache.LastQueryBufferID = BufferID; | |
115 | Cache.LastQuery = Ptr; | |
116 | Cache.LineNoOfQuery = LineNo; | |
117 | ||
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); | |
121 | } | |
122 | ||
123 | void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { | |
124 | if (IncludeLoc == SMLoc()) return; // Top of stack. | |
125 | ||
1a4d82fc JJ |
126 | unsigned CurBuf = FindBufferContainingLoc(IncludeLoc); |
127 | assert(CurBuf && "Invalid or unspecified location!"); | |
223e47cc LB |
128 | |
129 | PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS); | |
130 | ||
131 | OS << "Included from " | |
132 | << getBufferInfo(CurBuf).Buffer->getBufferIdentifier() | |
133 | << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n"; | |
134 | } | |
135 | ||
136 | ||
223e47cc LB |
137 | SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, |
138 | const Twine &Msg, | |
970d7e83 LB |
139 | ArrayRef<SMRange> Ranges, |
140 | ArrayRef<SMFixIt> FixIts) const { | |
223e47cc LB |
141 | |
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>"; | |
147 | std::string LineStr; | |
148 | ||
149 | if (Loc.isValid()) { | |
1a4d82fc JJ |
150 | unsigned CurBuf = FindBufferContainingLoc(Loc); |
151 | assert(CurBuf && "Invalid or unspecified location!"); | |
223e47cc | 152 | |
1a4d82fc | 153 | const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf); |
223e47cc LB |
154 | BufferID = CurMB->getBufferIdentifier(); |
155 | ||
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') | |
161 | --LineStart; | |
162 | ||
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') | |
167 | ++LineEnd; | |
168 | LineStr = std::string(LineStart, LineEnd); | |
169 | ||
170 | // Convert any ranges to column ranges that only intersect the line of the | |
171 | // location. | |
172 | for (unsigned i = 0, e = Ranges.size(); i != e; ++i) { | |
173 | SMRange R = Ranges[i]; | |
174 | if (!R.isValid()) continue; | |
175 | ||
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) | |
178 | continue; | |
179 | ||
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); | |
185 | ||
186 | // Translate from SMLoc ranges to column ranges. | |
970d7e83 | 187 | // FIXME: Handle multibyte characters. |
223e47cc LB |
188 | ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart, |
189 | R.End.getPointer()-LineStart)); | |
190 | } | |
191 | ||
192 | LineAndCol = getLineAndColumn(Loc, CurBuf); | |
193 | } | |
194 | ||
195 | return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first, | |
196 | LineAndCol.second-1, Kind, Msg.str(), | |
970d7e83 | 197 | LineStr, ColRanges, FixIts); |
223e47cc LB |
198 | } |
199 | ||
1a4d82fc JJ |
200 | void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, |
201 | bool ShowColors) const { | |
223e47cc LB |
202 | // Report the message with the diagnostic handler if present. |
203 | if (DiagHandler) { | |
204 | DiagHandler(Diagnostic, DiagContext); | |
205 | return; | |
206 | } | |
207 | ||
1a4d82fc JJ |
208 | if (Diagnostic.getLoc().isValid()) { |
209 | unsigned CurBuf = FindBufferContainingLoc(Diagnostic.getLoc()); | |
210 | assert(CurBuf && "Invalid or unspecified location!"); | |
223e47cc LB |
211 | PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS); |
212 | } | |
213 | ||
1a4d82fc JJ |
214 | Diagnostic.print(nullptr, OS, ShowColors); |
215 | } | |
216 | ||
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); | |
222 | } | |
223 | ||
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); | |
223e47cc LB |
228 | } |
229 | ||
230 | //===----------------------------------------------------------------------===// | |
231 | // SMDiagnostic Implementation | |
232 | //===----------------------------------------------------------------------===// | |
233 | ||
970d7e83 | 234 | SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, |
223e47cc | 235 | int Line, int Col, SourceMgr::DiagKind Kind, |
970d7e83 LB |
236 | StringRef Msg, StringRef LineStr, |
237 | ArrayRef<std::pair<unsigned,unsigned> > Ranges, | |
238 | ArrayRef<SMFixIt> Hints) | |
223e47cc | 239 | : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind), |
970d7e83 LB |
240 | Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()), |
241 | FixIts(Hints.begin(), Hints.end()) { | |
242 | std::sort(FixIts.begin(), FixIts.end()); | |
223e47cc LB |
243 | } |
244 | ||
970d7e83 LB |
245 | static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, |
246 | ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){ | |
247 | if (FixIts.empty()) | |
248 | return; | |
249 | ||
250 | const char *LineStart = SourceLine.begin(); | |
251 | const char *LineEnd = SourceLine.end(); | |
252 | ||
253 | size_t PrevHintEndCol = 0; | |
254 | ||
255 | for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end(); | |
256 | I != E; ++I) { | |
257 | // If the fixit contains a newline or tab, ignore it. | |
258 | if (I->getText().find_first_of("\n\r\t") != StringRef::npos) | |
259 | continue; | |
260 | ||
261 | SMRange R = I->getRange(); | |
262 | ||
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) | |
265 | continue; | |
266 | ||
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. | |
270 | unsigned FirstCol; | |
271 | if (R.Start.getPointer() < LineStart) | |
272 | FirstCol = 0; | |
273 | else | |
274 | FirstCol = R.Start.getPointer() - LineStart; | |
275 | ||
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 | |
279 | // to overlap. | |
280 | // | |
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; | |
286 | ||
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()); | |
292 | ||
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, ' '); | |
297 | ||
298 | std::copy(I->getText().begin(), I->getText().end(), | |
299 | FixItLine.begin() + HintCol); | |
300 | ||
301 | PrevHintEndCol = LastColumnModified; | |
302 | ||
303 | // For replacements, mark the removal range with '~'. | |
304 | // FIXME: Handle multibyte characters in the source line. | |
305 | unsigned LastCol; | |
306 | if (R.End.getPointer() >= LineEnd) | |
307 | LastCol = LineEnd - LineStart; | |
308 | else | |
309 | LastCol = R.End.getPointer() - LineStart; | |
310 | ||
311 | std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~'); | |
312 | } | |
313 | } | |
314 | ||
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]; | |
320 | ++OutCol; | |
321 | continue; | |
322 | } | |
323 | ||
324 | // If we have a tab, emit at least one space, then round up to 8 columns. | |
325 | do { | |
326 | S << ' '; | |
327 | ++OutCol; | |
328 | } while ((OutCol % TabStop) != 0); | |
329 | } | |
330 | S << '\n'; | |
331 | } | |
332 | ||
333 | static bool isNonASCII(char c) { | |
334 | return c & 0x80; | |
335 | } | |
223e47cc LB |
336 | |
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(); | |
341 | ||
342 | if (ShowColors) | |
343 | S.changeColor(raw_ostream::SAVEDCOLOR, true); | |
344 | ||
345 | if (ProgName && ProgName[0]) | |
346 | S << ProgName << ": "; | |
347 | ||
348 | if (!Filename.empty()) { | |
349 | if (Filename == "-") | |
350 | S << "<stdin>"; | |
351 | else | |
352 | S << Filename; | |
353 | ||
354 | if (LineNo != -1) { | |
355 | S << ':' << LineNo; | |
356 | if (ColumnNo != -1) | |
357 | S << ':' << (ColumnNo+1); | |
358 | } | |
359 | S << ": "; | |
360 | } | |
361 | ||
362 | switch (Kind) { | |
363 | case SourceMgr::DK_Error: | |
364 | if (ShowColors) | |
365 | S.changeColor(raw_ostream::RED, true); | |
366 | S << "error: "; | |
367 | break; | |
368 | case SourceMgr::DK_Warning: | |
369 | if (ShowColors) | |
370 | S.changeColor(raw_ostream::MAGENTA, true); | |
371 | S << "warning: "; | |
372 | break; | |
373 | case SourceMgr::DK_Note: | |
374 | if (ShowColors) | |
375 | S.changeColor(raw_ostream::BLACK, true); | |
376 | S << "note: "; | |
377 | break; | |
378 | } | |
379 | ||
380 | if (ShowColors) { | |
381 | S.resetColor(); | |
382 | S.changeColor(raw_ostream::SAVEDCOLOR, true); | |
383 | } | |
384 | ||
385 | S << Message << '\n'; | |
386 | ||
387 | if (ShowColors) | |
388 | S.resetColor(); | |
389 | ||
390 | if (LineNo == -1 || ColumnNo == -1) | |
391 | return; | |
392 | ||
970d7e83 LB |
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); | |
401 | return; | |
402 | } | |
403 | size_t NumColumns = LineContents.size(); | |
404 | ||
223e47cc | 405 | // Build the line with the caret and ranges. |
970d7e83 | 406 | std::string CaretLine(NumColumns+1, ' '); |
223e47cc LB |
407 | |
408 | // Expand any ranges. | |
409 | for (unsigned r = 0, e = Ranges.size(); r != e; ++r) { | |
410 | std::pair<unsigned, unsigned> R = Ranges[r]; | |
970d7e83 LB |
411 | std::fill(&CaretLine[R.first], |
412 | &CaretLine[std::min((size_t)R.second, CaretLine.size())], | |
413 | '~'); | |
223e47cc | 414 | } |
970d7e83 LB |
415 | |
416 | // Add any fix-its. | |
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())); | |
422 | ||
223e47cc | 423 | // Finally, plop on the caret. |
970d7e83 | 424 | if (unsigned(ColumnNo) <= NumColumns) |
223e47cc LB |
425 | CaretLine[ColumnNo] = '^'; |
426 | else | |
970d7e83 | 427 | CaretLine[NumColumns] = '^'; |
223e47cc LB |
428 | |
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 | |
431 | // least. | |
432 | CaretLine.erase(CaretLine.find_last_not_of(' ')+1); | |
433 | ||
970d7e83 | 434 | printSourceLine(S, LineContents); |
223e47cc LB |
435 | |
436 | if (ShowColors) | |
437 | S.changeColor(raw_ostream::GREEN, true); | |
438 | ||
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') { | |
442 | S << CaretLine[i]; | |
443 | ++OutCol; | |
444 | continue; | |
445 | } | |
446 | ||
447 | // Okay, we have a tab. Insert the appropriate number of characters. | |
448 | do { | |
449 | S << CaretLine[i]; | |
450 | ++OutCol; | |
970d7e83 | 451 | } while ((OutCol % TabStop) != 0); |
223e47cc | 452 | } |
970d7e83 | 453 | S << '\n'; |
223e47cc LB |
454 | |
455 | if (ShowColors) | |
456 | S.resetColor(); | |
970d7e83 LB |
457 | |
458 | // Print out the replacement line, matching tabs in the source line. | |
459 | if (FixItInsertionLine.empty()) | |
460 | return; | |
223e47cc | 461 | |
1a4d82fc | 462 | for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) { |
970d7e83 LB |
463 | if (i >= LineContents.size() || LineContents[i] != '\t') { |
464 | S << FixItInsertionLine[i]; | |
465 | ++OutCol; | |
466 | continue; | |
467 | } | |
468 | ||
469 | // Okay, we have a tab. Insert the appropriate number of characters. | |
470 | do { | |
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] != ' ') | |
478 | ++i; | |
479 | ++OutCol; | |
480 | } while (((OutCol % TabStop) != 0) && i != e); | |
481 | } | |
223e47cc LB |
482 | S << '\n'; |
483 | } |