Coverage Report

Created: 2025-07-15 06:22

/src/keystone/llvm/lib/Support/SourceMgr.cpp
Line
Count
Source (jump to first uncovered line)
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
16
#include "llvm/Support/SourceMgr.h"
17
#include "llvm/ADT/Twine.h"
18
#include "llvm/Support/MemoryBuffer.h"
19
#include "llvm/Support/Path.h"
20
#include "llvm/Support/raw_ostream.h"
21
using namespace llvm_ks;
22
23
static const size_t TabStop = 8;
24
25
namespace {
26
  struct LineNoCacheTy {
27
    unsigned LastQueryBufferID;
28
    const char *LastQuery;
29
    unsigned LineNoOfQuery;
30
  };
31
}
32
33
2.80M
static LineNoCacheTy *getCache(void *Ptr) {
34
2.80M
  return (LineNoCacheTy*)Ptr;
35
2.80M
}
36
37
38
130k
SourceMgr::~SourceMgr() {
39
  // Delete the line # cache if allocated.
40
130k
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
41
18.8k
    delete Cache;
42
130k
}
43
44
unsigned SourceMgr::AddIncludeFile(const std::string &Filename,
45
                                   SMLoc IncludeLoc,
46
42
                                   std::string &IncludedFile) {
47
42
  IncludedFile = Filename;
48
42
  ErrorOr<std::unique_ptr<MemoryBuffer>> NewBufOrErr =
49
42
    MemoryBuffer::getFile(IncludedFile);
50
51
  // If the file didn't exist directly, see if it's in an include path.
52
42
  for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr;
53
42
       ++i) {
54
0
    IncludedFile =
55
0
        IncludeDirectories[i] + sys::path::get_separator().data() + Filename;
56
0
    NewBufOrErr = MemoryBuffer::getFile(IncludedFile);
57
0
  }
58
59
42
  if (!NewBufOrErr)
60
42
    return 0;
61
62
0
  return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc);
63
42
}
64
65
5.17M
unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
66
784M
  for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
67
783M
    if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
68
        // Use <= here so that a pointer to the null at the end of the buffer
69
        // is included as part of the buffer.
70
783M
        Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
71
3.96M
      return i + 1;
72
1.20M
  return 0;
73
5.17M
}
74
75
std::pair<unsigned, unsigned>
76
1.33M
SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const {
77
1.33M
  if (!BufferID)
78
0
    BufferID = FindBufferContainingLoc(Loc);
79
1.33M
  assert(BufferID && "Invalid Location!");
80
81
1.33M
  const MemoryBuffer *Buff = getMemoryBuffer(BufferID);
82
83
  // Count the number of \n's between the start of the file and the specified
84
  // location.
85
1.33M
  unsigned LineNo = 1;
86
87
1.33M
  const char *BufStart = Buff->getBufferStart();
88
1.33M
  const char *Ptr = BufStart;
89
90
  // If we have a line number cache, and if the query is to a later point in the
91
  // same file, start searching from the last query location.  This optimizes
92
  // for the case when multiple diagnostics come out of one file in order.
93
1.33M
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
94
1.31M
    if (Cache->LastQueryBufferID == BufferID &&
95
1.31M
        Cache->LastQuery <= Loc.getPointer()) {
96
164k
      Ptr = Cache->LastQuery;
97
164k
      LineNo = Cache->LineNoOfQuery;
98
164k
    }
99
100
  // Scan for the location being queried, keeping track of the number of lines
101
  // we see.
102
5.12G
  for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr)
103
5.12G
    if (*Ptr == '\n') ++LineNo;
104
105
  // Allocate the line number cache if it doesn't exist.
106
1.33M
  if (!LineNoCache)
107
18.8k
    LineNoCache = new LineNoCacheTy();
108
109
  // Update the line # cache.
110
1.33M
  LineNoCacheTy &Cache = *getCache(LineNoCache);
111
1.33M
  Cache.LastQueryBufferID = BufferID;
112
1.33M
  Cache.LastQuery = Ptr;
113
1.33M
  Cache.LineNoOfQuery = LineNo;
114
  
115
1.33M
  size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r");
116
1.33M
  if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0;
117
1.33M
  return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs);
118
1.33M
}
119
120
953k
void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
121
953k
  if (IncludeLoc == SMLoc()) return;  // Top of stack.
122
123
0
  unsigned CurBuf = FindBufferContainingLoc(IncludeLoc);
124
0
  assert(CurBuf && "Invalid or unspecified location!");
125
126
0
  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
127
128
0
  OS << "Included from "
129
0
     << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
130
0
     << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
131
0
}
132
133
134
SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
135
                                   const Twine &Msg,
136
                                   ArrayRef<SMRange> Ranges,
137
1.29M
                                   ArrayRef<SMFixIt> FixIts) const {
138
139
  // First thing to do: find the current buffer containing the specified
140
  // location to pull out the source line.
141
1.29M
  SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges;
142
1.29M
  std::pair<unsigned, unsigned> LineAndCol;
143
1.29M
  const char *BufferID = "<unknown>";
144
1.29M
  std::string LineStr;
145
  
146
1.29M
  if (Loc.isValid()) {
147
1.28M
    unsigned CurBuf = FindBufferContainingLoc(Loc);
148
1.28M
    assert(CurBuf && "Invalid or unspecified location!");
149
150
1.28M
    const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf);
151
1.28M
    BufferID = CurMB->getBufferIdentifier();
152
    
153
    // Scan backward to find the start of the line.
154
1.28M
    const char *LineStart = Loc.getPointer();
155
1.28M
    const char *BufStart = CurMB->getBufferStart();
156
53.1M
    while (LineStart != BufStart && LineStart[-1] != '\n' &&
157
53.1M
           LineStart[-1] != '\r')
158
51.8M
      --LineStart;
159
160
    // Get the end of the line.
161
1.28M
    const char *LineEnd = Loc.getPointer();
162
1.28M
    const char *BufEnd = CurMB->getBufferEnd();
163
69.1M
    while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
164
67.9M
      ++LineEnd;
165
1.28M
    LineStr = std::string(LineStart, LineEnd);
166
167
    // Convert any ranges to column ranges that only intersect the line of the
168
    // location.
169
1.28M
    for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
170
1.89k
      SMRange R = Ranges[i];
171
1.89k
      if (!R.isValid()) continue;
172
      
173
      // If the line doesn't contain any part of the range, then ignore it.
174
1.89k
      if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
175
0
        continue;
176
     
177
      // Ignore pieces of the range that go onto other lines.
178
1.89k
      if (R.Start.getPointer() < LineStart)
179
0
        R.Start = SMLoc::getFromPointer(LineStart);
180
1.89k
      if (R.End.getPointer() > LineEnd)
181
66
        R.End = SMLoc::getFromPointer(LineEnd);
182
      
183
      // Translate from SMLoc ranges to column ranges.
184
      // FIXME: Handle multibyte characters.
185
1.89k
      ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart,
186
1.89k
                                         R.End.getPointer()-LineStart));
187
1.89k
    }
188
189
1.28M
    LineAndCol = getLineAndColumn(Loc, CurBuf);
190
1.28M
  }
191
    
192
1.29M
  return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first,
193
1.29M
                      LineAndCol.second-1, Kind, Msg.str(),
194
1.29M
                      LineStr, ColRanges, FixIts);
195
1.29M
}
196
197
void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic,
198
1.29M
                             bool ShowColors) const {
199
  // Report the message with the diagnostic handler if present.
200
1.29M
  if (DiagHandler) {
201
1.29M
    DiagHandler(Diagnostic, DiagContext);
202
1.29M
    return;
203
1.29M
  }
204
205
0
  if (Diagnostic.getLoc().isValid()) {
206
0
    unsigned CurBuf = FindBufferContainingLoc(Diagnostic.getLoc());
207
0
    assert(CurBuf && "Invalid or unspecified location!");
208
0
    PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
209
0
  }
210
211
0
  Diagnostic.print(nullptr, OS, ShowColors);
212
0
}
213
214
void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc,
215
                             SourceMgr::DiagKind Kind,
216
                             const Twine &Msg, ArrayRef<SMRange> Ranges,
217
1.29M
                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
218
1.29M
  PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors);
219
1.29M
}
220
221
void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
222
                             const Twine &Msg, ArrayRef<SMRange> Ranges,
223
1.29M
                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
224
1.29M
  PrintMessage(llvm_ks::errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors);
225
1.29M
}
226
227
//===----------------------------------------------------------------------===//
228
// SMDiagnostic Implementation
229
//===----------------------------------------------------------------------===//
230
231
SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN,
232
                           int Line, int Col, SourceMgr::DiagKind Kind,
233
                           StringRef Msg, StringRef LineStr,
234
                           ArrayRef<std::pair<unsigned,unsigned> > Ranges,
235
                           ArrayRef<SMFixIt> Hints)
236
1.32M
  : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind),
237
1.32M
    Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()),
238
1.32M
    FixIts(Hints.begin(), Hints.end()) {
239
1.32M
  std::sort(FixIts.begin(), FixIts.end());
240
1.32M
}
241
242
static void buildFixItLine(std::string &CaretLine, std::string &FixItLine,
243
1.14M
                           ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){
244
1.14M
  if (FixIts.empty())
245
1.14M
    return;
246
247
1.28k
  const char *LineStart = SourceLine.begin();
248
1.28k
  const char *LineEnd = SourceLine.end();
249
250
1.28k
  size_t PrevHintEndCol = 0;
251
252
1.28k
  for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end();
253
2.57k
       I != E; ++I) {
254
    // If the fixit contains a newline or tab, ignore it.
255
1.28k
    if (I->getText().find_first_of("\n\r\t") != StringRef::npos)
256
0
      continue;
257
258
1.28k
    SMRange R = I->getRange();
259
260
    // If the line doesn't contain any part of the range, then ignore it.
261
1.28k
    if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
262
0
      continue;
263
264
    // Translate from SMLoc to column.
265
    // Ignore pieces of the range that go onto other lines.
266
    // FIXME: Handle multibyte characters in the source line.
267
1.28k
    unsigned FirstCol;
268
1.28k
    if (R.Start.getPointer() < LineStart)
269
0
      FirstCol = 0;
270
1.28k
    else
271
1.28k
      FirstCol = R.Start.getPointer() - LineStart;
272
273
    // If we inserted a long previous hint, push this one forwards, and add
274
    // an extra space to show that this is not part of the previous
275
    // completion. This is sort of the best we can do when two hints appear
276
    // to overlap.
277
    //
278
    // Note that if this hint is located immediately after the previous
279
    // hint, no space will be added, since the location is more important.
280
1.28k
    unsigned HintCol = FirstCol;
281
1.28k
    if (HintCol < PrevHintEndCol)
282
0
      HintCol = PrevHintEndCol + 1;
283
284
    // This relies on one byte per column in our fixit hints.
285
1.28k
    unsigned LastColumnModified = HintCol + I->getText().size();
286
1.28k
    if (LastColumnModified > FixItLine.size())
287
1.28k
      FixItLine.resize(LastColumnModified, ' ');
288
289
1.28k
    std::copy(I->getText().begin(), I->getText().end(),
290
1.28k
              FixItLine.begin() + HintCol);
291
292
1.28k
    PrevHintEndCol = LastColumnModified;
293
294
    // For replacements, mark the removal range with '~'.
295
    // FIXME: Handle multibyte characters in the source line.
296
1.28k
    unsigned LastCol;
297
1.28k
    if (R.End.getPointer() >= LineEnd)
298
104
      LastCol = LineEnd - LineStart;
299
1.18k
    else
300
1.18k
      LastCol = R.End.getPointer() - LineStart;
301
302
1.28k
    std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~');
303
1.28k
  }
304
1.28k
}
305
306
1.28M
static void printSourceLine(raw_ostream &S, StringRef LineContents) {
307
  // Print out the source line one character at a time, so we can expand tabs.
308
120M
  for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) {
309
119M
    if (LineContents[i] != '\t') {
310
118M
      S << LineContents[i];
311
118M
      ++OutCol;
312
118M
      continue;
313
118M
    }
314
315
    // If we have a tab, emit at least one space, then round up to 8 columns.
316
4.36M
    do {
317
4.36M
      S << ' ';
318
4.36M
      ++OutCol;
319
4.36M
    } while ((OutCol % TabStop) != 0);
320
792k
  }
321
1.28M
  S << '\n';
322
1.28M
}
323
324
96.5M
static bool isNonASCII(char c) {
325
96.5M
  return c & 0x80;
326
96.5M
}
327
328
void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors,
329
1.29M
                         bool ShowKindLabel) const {
330
  // Display colors only if OS supports colors.
331
1.29M
  ShowColors &= S.has_colors();
332
333
1.29M
  if (ShowColors)
334
1.29M
    S.changeColor(raw_ostream::SAVEDCOLOR, true);
335
336
1.29M
  if (ProgName && ProgName[0])
337
0
    S << ProgName << ": ";
338
339
1.29M
  if (!Filename.empty()) {
340
987k
    if (Filename == "-")
341
1.27k
      S << "<stdin>";
342
986k
    else
343
986k
      S << Filename;
344
345
987k
    if (LineNo != -1) {
346
987k
      S << ':' << LineNo;
347
987k
      if (ColumnNo != -1)
348
973k
        S << ':' << (ColumnNo+1);
349
987k
    }
350
987k
    S << ": ";
351
987k
  }
352
353
1.29M
  if (ShowKindLabel) {
354
1.29M
    switch (Kind) {
355
190k
    case SourceMgr::DK_Error:
356
190k
      if (ShowColors)
357
190k
        S.changeColor(raw_ostream::RED, true);
358
190k
      S << "error: ";
359
190k
      break;
360
183k
    case SourceMgr::DK_Warning:
361
183k
      if (ShowColors)
362
183k
        S.changeColor(raw_ostream::MAGENTA, true);
363
183k
      S << "warning: ";
364
183k
      break;
365
922k
    case SourceMgr::DK_Note:
366
922k
      if (ShowColors)
367
922k
        S.changeColor(raw_ostream::BLACK, true);
368
922k
      S << "note: ";
369
922k
      break;
370
1.29M
    }
371
372
1.29M
    if (ShowColors) {
373
1.29M
      S.resetColor();
374
1.29M
      S.changeColor(raw_ostream::SAVEDCOLOR, true);
375
1.29M
    }
376
1.29M
  }
377
378
1.29M
  S << Message << '\n';
379
380
1.29M
  if (ShowColors)
381
1.29M
    S.resetColor();
382
383
1.29M
  if (LineNo == -1 || ColumnNo == -1)
384
14.5k
    return;
385
386
  // FIXME: If there are multibyte or multi-column characters in the source, all
387
  // our ranges will be wrong. To do this properly, we'll need a byte-to-column
388
  // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
389
  // expanding them later, and bail out rather than show incorrect ranges and
390
  // misaligned fixits for any other odd characters.
391
1.28M
  if (std::find_if(LineContents.begin(), LineContents.end(), isNonASCII) !=
392
1.28M
      LineContents.end()) {
393
136k
    printSourceLine(S, LineContents);
394
136k
    return;
395
136k
  }
396
1.14M
  size_t NumColumns = LineContents.size();
397
398
  // Build the line with the caret and ranges.
399
1.14M
  std::string CaretLine(NumColumns+1, ' ');
400
  
401
  // Expand any ranges.
402
1.14M
  for (unsigned r = 0, e = Ranges.size(); r != e; ++r) {
403
1.41k
    std::pair<unsigned, unsigned> R = Ranges[r];
404
1.41k
    std::fill(&CaretLine[R.first],
405
1.41k
              &CaretLine[std::min((size_t)R.second, CaretLine.size())],
406
1.41k
              '~');
407
1.41k
  }
408
409
  // Add any fix-its.
410
  // FIXME: Find the beginning of the line properly for multibyte characters.
411
1.14M
  std::string FixItInsertionLine;
412
1.14M
  buildFixItLine(CaretLine, FixItInsertionLine, FixIts,
413
1.14M
                 makeArrayRef(Loc.getPointer() - ColumnNo,
414
1.14M
                              LineContents.size()));
415
416
  // Finally, plop on the caret.
417
1.14M
  if (unsigned(ColumnNo) <= NumColumns)
418
1.14M
    CaretLine[ColumnNo] = '^';
419
0
  else 
420
0
    CaretLine[NumColumns] = '^';
421
  
422
  // ... and remove trailing whitespace so the output doesn't wrap for it.  We
423
  // know that the line isn't completely empty because it has the caret in it at
424
  // least.
425
1.14M
  CaretLine.erase(CaretLine.find_last_not_of(' ')+1);
426
  
427
1.14M
  printSourceLine(S, LineContents);
428
429
1.14M
  if (ShowColors)
430
1.14M
    S.changeColor(raw_ostream::GREEN, true);
431
432
  // Print out the caret line, matching tabs in the source line.
433
44.6M
  for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) {
434
43.5M
    if (i >= LineContents.size() || LineContents[i] != '\t') {
435
43.2M
      S << CaretLine[i];
436
43.2M
      ++OutCol;
437
43.2M
      continue;
438
43.2M
    }
439
    
440
    // Okay, we have a tab.  Insert the appropriate number of characters.
441
1.86M
    do {
442
1.86M
      S << CaretLine[i];
443
1.86M
      ++OutCol;
444
1.86M
    } while ((OutCol % TabStop) != 0);
445
311k
  }
446
1.14M
  S << '\n';
447
448
1.14M
  if (ShowColors)
449
1.14M
    S.resetColor();
450
451
  // Print out the replacement line, matching tabs in the source line.
452
1.14M
  if (FixItInsertionLine.empty())
453
1.14M
    return;
454
  
455
254k
  for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) {
456
253k
    if (i >= LineContents.size() || LineContents[i] != '\t') {
457
252k
      S << FixItInsertionLine[i];
458
252k
      ++OutCol;
459
252k
      continue;
460
252k
    }
461
462
    // Okay, we have a tab.  Insert the appropriate number of characters.
463
3.05k
    do {
464
3.05k
      S << FixItInsertionLine[i];
465
      // FIXME: This is trying not to break up replacements, but then to re-sync
466
      // with the tabs between replacements. This will fail, though, if two
467
      // fix-it replacements are exactly adjacent, or if a fix-it contains a
468
      // space. Really we should be precomputing column widths, which we'll
469
      // need anyway for multibyte chars.
470
3.05k
      if (FixItInsertionLine[i] != ' ')
471
56
        ++i;
472
3.05k
      ++OutCol;
473
3.05k
    } while (((OutCol % TabStop) != 0) && i != e);
474
450
  }
475
1.28k
  S << '\n';
476
1.28k
}