Coverage Report

Created: 2025-08-28 06:48

/src/hermes/external/llvh/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 "llvh/Support/SourceMgr.h"
17
#include "llvh/ADT/ArrayRef.h"
18
#include "llvh/ADT/STLExtras.h"
19
#include "llvh/ADT/SmallVector.h"
20
#include "llvh/ADT/StringRef.h"
21
#include "llvh/ADT/Twine.h"
22
#include "llvh/Support/ErrorOr.h"
23
#include "llvh/Support/Locale.h"
24
#include "llvh/Support/MemoryBuffer.h"
25
#include "llvh/Support/Path.h"
26
#include "llvh/Support/SMLoc.h"
27
#include "llvh/Support/raw_ostream.h"
28
#include <algorithm>
29
#include <cassert>
30
#include <cstddef>
31
#include <limits>
32
#include <memory>
33
#include <string>
34
#include <utility>
35
36
using namespace llvh;
37
38
static const size_t TabStop = 8;
39
40
unsigned SourceMgr::AddIncludeFile(const std::string &Filename,
41
                                   SMLoc IncludeLoc,
42
0
                                   std::string &IncludedFile) {
43
0
  IncludedFile = Filename;
44
0
  ErrorOr<std::unique_ptr<MemoryBuffer>> NewBufOrErr =
45
0
    MemoryBuffer::getFile(IncludedFile);
46
47
  // If the file didn't exist directly, see if it's in an include path.
48
0
  for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr;
49
0
       ++i) {
50
0
    IncludedFile =
51
0
        IncludeDirectories[i] + sys::path::get_separator().data() + Filename;
52
0
    NewBufOrErr = MemoryBuffer::getFile(IncludedFile);
53
0
  }
54
55
0
  if (!NewBufOrErr)
56
0
    return 0;
57
58
0
  return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc);
59
0
}
60
61
29.7k
unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
62
  // Check the last buffer we found. Most searches are likely to be in the same
63
  // one.
64
29.7k
  if (LLVM_LIKELY(
65
29.7k
          LastFoundBufId &&
66
29.7k
          Loc.getPointer() >=
67
29.7k
              Buffers[LastFoundBufId - 1].Buffer->getBufferStart() &&
68
29.7k
          Loc.getPointer() <=
69
29.7k
              Buffers[LastFoundBufId - 1].Buffer->getBufferEnd())) {
70
29.6k
    return LastFoundBufId;
71
29.6k
  }
72
73
103
  auto it = BufferEnds.lower_bound(Loc.getPointer());
74
103
  if (it != BufferEnds.end() &&
75
103
      Loc.getPointer() >= Buffers[it->second - 1].Buffer->getBufferStart()) {
76
103
    return LastFoundBufId = it->second;
77
103
  }
78
79
0
  return 0;
80
103
}
81
82
template <typename T>
83
94.3k
std::vector<T> *SourceMgr::SrcBuffer::getOffsets() const {
84
  // Ensure OffsetCache is allocated and populated with offsets of all the
85
  // '\n' bytes.
86
94.3k
  if (!OffsetCache.isNull())
87
94.2k
    return OffsetCache.get<std::vector<T> *>();
88
89
103
  std::vector<T> *Offsets = new std::vector<T>();
90
103
  OffsetCache = Offsets;
91
103
  size_t Sz = Buffer->getBufferSize();
92
103
  assert(Sz <= std::numeric_limits<T>::max());
93
103
  StringRef S = Buffer->getBuffer();
94
28.5M
  for (size_t N = 0; N < Sz; ++N) {
95
28.5M
    if (S[N] == '\n') {
96
115k
      Offsets->push_back(static_cast<T>(N));
97
115k
    }
98
28.5M
  }
99
100
103
  return Offsets;
101
103
}
Unexecuted instantiation: std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >* llvh::SourceMgr::SrcBuffer::getOffsets<unsigned char>() const
std::__1::vector<unsigned short, std::__1::allocator<unsigned short> >* llvh::SourceMgr::SrcBuffer::getOffsets<unsigned short>() const
Line
Count
Source
83
62.7k
std::vector<T> *SourceMgr::SrcBuffer::getOffsets() const {
84
  // Ensure OffsetCache is allocated and populated with offsets of all the
85
  // '\n' bytes.
86
62.7k
  if (!OffsetCache.isNull())
87
62.7k
    return OffsetCache.get<std::vector<T> *>();
88
89
1
  std::vector<T> *Offsets = new std::vector<T>();
90
1
  OffsetCache = Offsets;
91
1
  size_t Sz = Buffer->getBufferSize();
92
1
  assert(Sz <= std::numeric_limits<T>::max());
93
1
  StringRef S = Buffer->getBuffer();
94
41.9k
  for (size_t N = 0; N < Sz; ++N) {
95
41.9k
    if (S[N] == '\n') {
96
9
      Offsets->push_back(static_cast<T>(N));
97
9
    }
98
41.9k
  }
99
100
1
  return Offsets;
101
1
}
std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >* llvh::SourceMgr::SrcBuffer::getOffsets<unsigned int>() const
Line
Count
Source
83
31.5k
std::vector<T> *SourceMgr::SrcBuffer::getOffsets() const {
84
  // Ensure OffsetCache is allocated and populated with offsets of all the
85
  // '\n' bytes.
86
31.5k
  if (!OffsetCache.isNull())
87
31.4k
    return OffsetCache.get<std::vector<T> *>();
88
89
102
  std::vector<T> *Offsets = new std::vector<T>();
90
102
  OffsetCache = Offsets;
91
102
  size_t Sz = Buffer->getBufferSize();
92
102
  assert(Sz <= std::numeric_limits<T>::max());
93
102
  StringRef S = Buffer->getBuffer();
94
28.5M
  for (size_t N = 0; N < Sz; ++N) {
95
28.5M
    if (S[N] == '\n') {
96
115k
      Offsets->push_back(static_cast<T>(N));
97
115k
    }
98
28.5M
  }
99
100
102
  return Offsets;
101
102
}
Unexecuted instantiation: std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >* llvh::SourceMgr::SrcBuffer::getOffsets<unsigned long>() const
102
103
template <typename T>
104
std::pair<StringRef, unsigned> SourceMgr::SrcBuffer::getLineNumber(
105
29.7k
    const char *Ptr) const {
106
29.7k
  std::vector<T> *Offsets = getOffsets<T>();
107
108
29.7k
  const char *BufStart = Buffer->getBufferStart();
109
29.7k
  assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
110
29.7k
  ptrdiff_t PtrDiff = Ptr - BufStart;
111
29.7k
  assert(PtrDiff >= 0 && static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
112
29.7k
  T PtrOffset = static_cast<T>(PtrDiff);
113
114
  // std::lower_bound returns the first EOL offset that's not-less-than
115
  // PtrOffset, meaning the EOL that _ends the line_ that PtrOffset is on
116
  // (including if PtrOffset refers to the EOL itself). If there's no such
117
  // EOL, returns end().
118
29.7k
  auto EOL = std::lower_bound(Offsets->begin(), Offsets->end(), PtrOffset);
119
120
  // The start of the line is the previous line end + 1.
121
29.7k
  const char *LineStart =
122
29.7k
      EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
123
124
  // The end of the line is the EOL inclusive or the end of the buffer exclusive.
125
29.7k
  const char *LineEnd =
126
29.7k
      EOL != Offsets->end() ? BufStart + *EOL + 1 : Buffer->getBufferEnd();
127
128
  // Lines count from 1, so add 1 to the distance from the 0th line.
129
29.7k
  return {StringRef(LineStart, LineEnd - LineStart), static_cast<unsigned>((1 + (EOL - Offsets->begin())))};
130
29.7k
}
Unexecuted instantiation: std::__1::pair<llvh::StringRef, unsigned int> llvh::SourceMgr::SrcBuffer::getLineNumber<unsigned char>(char const*) const
std::__1::pair<llvh::StringRef, unsigned int> llvh::SourceMgr::SrcBuffer::getLineNumber<unsigned short>(char const*) const
Line
Count
Source
105
20.9k
    const char *Ptr) const {
106
20.9k
  std::vector<T> *Offsets = getOffsets<T>();
107
108
20.9k
  const char *BufStart = Buffer->getBufferStart();
109
20.9k
  assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
110
20.9k
  ptrdiff_t PtrDiff = Ptr - BufStart;
111
20.9k
  assert(PtrDiff >= 0 && static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
112
20.9k
  T PtrOffset = static_cast<T>(PtrDiff);
113
114
  // std::lower_bound returns the first EOL offset that's not-less-than
115
  // PtrOffset, meaning the EOL that _ends the line_ that PtrOffset is on
116
  // (including if PtrOffset refers to the EOL itself). If there's no such
117
  // EOL, returns end().
118
20.9k
  auto EOL = std::lower_bound(Offsets->begin(), Offsets->end(), PtrOffset);
119
120
  // The start of the line is the previous line end + 1.
121
20.9k
  const char *LineStart =
122
20.9k
      EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
123
124
  // The end of the line is the EOL inclusive or the end of the buffer exclusive.
125
20.9k
  const char *LineEnd =
126
20.9k
      EOL != Offsets->end() ? BufStart + *EOL + 1 : Buffer->getBufferEnd();
127
128
  // Lines count from 1, so add 1 to the distance from the 0th line.
129
20.9k
  return {StringRef(LineStart, LineEnd - LineStart), static_cast<unsigned>((1 + (EOL - Offsets->begin())))};
130
20.9k
}
std::__1::pair<llvh::StringRef, unsigned int> llvh::SourceMgr::SrcBuffer::getLineNumber<unsigned int>(char const*) const
Line
Count
Source
105
8.86k
    const char *Ptr) const {
106
8.86k
  std::vector<T> *Offsets = getOffsets<T>();
107
108
8.86k
  const char *BufStart = Buffer->getBufferStart();
109
8.86k
  assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
110
8.86k
  ptrdiff_t PtrDiff = Ptr - BufStart;
111
8.86k
  assert(PtrDiff >= 0 && static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
112
8.86k
  T PtrOffset = static_cast<T>(PtrDiff);
113
114
  // std::lower_bound returns the first EOL offset that's not-less-than
115
  // PtrOffset, meaning the EOL that _ends the line_ that PtrOffset is on
116
  // (including if PtrOffset refers to the EOL itself). If there's no such
117
  // EOL, returns end().
118
8.86k
  auto EOL = std::lower_bound(Offsets->begin(), Offsets->end(), PtrOffset);
119
120
  // The start of the line is the previous line end + 1.
121
8.86k
  const char *LineStart =
122
8.86k
      EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
123
124
  // The end of the line is the EOL inclusive or the end of the buffer exclusive.
125
8.86k
  const char *LineEnd =
126
8.86k
      EOL != Offsets->end() ? BufStart + *EOL + 1 : Buffer->getBufferEnd();
127
128
  // Lines count from 1, so add 1 to the distance from the 0th line.
129
8.86k
  return {StringRef(LineStart, LineEnd - LineStart), static_cast<unsigned>((1 + (EOL - Offsets->begin())))};
130
8.86k
}
Unexecuted instantiation: std::__1::pair<llvh::StringRef, unsigned int> llvh::SourceMgr::SrcBuffer::getLineNumber<unsigned long>(char const*) const
131
132
template<typename T>
133
64.5k
StringRef SourceMgr::SrcBuffer::getLineRef(unsigned line) const {
134
64.5k
  assert(line >= 1 && "line number must be 1-based");
135
64.5k
  --line;
136
137
64.5k
  const char *BufStart = Buffer->getBufferStart();
138
64.5k
  std::vector<T> *Offsets = getOffsets<T>();
139
64.5k
  size_t size = Offsets->size();
140
64.5k
  if (line < size) {
141
43.4k
    auto EOL = Offsets->begin() + line;
142
143
    // The start of the line is the previous line end + 1.
144
43.4k
    const char *LineStart =
145
43.4k
        EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
146
147
    // The end of the line is the EOL inclusive.
148
43.4k
    const char *LineEnd = BufStart + *EOL + 1;
149
150
43.4k
    return StringRef(LineStart, LineEnd - LineStart);
151
43.4k
  } else {
152
    // Asking for the last line?
153
21.0k
    if (line == size) {
154
20.9k
      const char *LineStart =
155
20.9k
          size != 0 ? BufStart + Offsets->back() + 1 : BufStart;
156
20.9k
      const char *LineEnd = Buffer->getBufferEnd();
157
20.9k
      return StringRef(LineStart, LineEnd - LineStart);
158
20.9k
    } else {
159
116
      return StringRef(Buffer->getBufferEnd(), 0);
160
116
    }
161
21.0k
  }
162
64.5k
}
Unexecuted instantiation: llvh::StringRef llvh::SourceMgr::SrcBuffer::getLineRef<unsigned char>(unsigned int) const
llvh::StringRef llvh::SourceMgr::SrcBuffer::getLineRef<unsigned short>(unsigned int) const
Line
Count
Source
133
41.8k
StringRef SourceMgr::SrcBuffer::getLineRef(unsigned line) const {
134
41.8k
  assert(line >= 1 && "line number must be 1-based");
135
41.8k
  --line;
136
137
41.8k
  const char *BufStart = Buffer->getBufferStart();
138
41.8k
  std::vector<T> *Offsets = getOffsets<T>();
139
41.8k
  size_t size = Offsets->size();
140
41.8k
  if (line < size) {
141
20.9k
    auto EOL = Offsets->begin() + line;
142
143
    // The start of the line is the previous line end + 1.
144
20.9k
    const char *LineStart =
145
20.9k
        EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
146
147
    // The end of the line is the EOL inclusive.
148
20.9k
    const char *LineEnd = BufStart + *EOL + 1;
149
150
20.9k
    return StringRef(LineStart, LineEnd - LineStart);
151
20.9k
  } else {
152
    // Asking for the last line?
153
20.9k
    if (line == size) {
154
20.9k
      const char *LineStart =
155
20.9k
          size != 0 ? BufStart + Offsets->back() + 1 : BufStart;
156
20.9k
      const char *LineEnd = Buffer->getBufferEnd();
157
20.9k
      return StringRef(LineStart, LineEnd - LineStart);
158
20.9k
    } else {
159
2
      return StringRef(Buffer->getBufferEnd(), 0);
160
2
    }
161
20.9k
  }
162
41.8k
}
llvh::StringRef llvh::SourceMgr::SrcBuffer::getLineRef<unsigned int>(unsigned int) const
Line
Count
Source
133
22.7k
StringRef SourceMgr::SrcBuffer::getLineRef(unsigned line) const {
134
22.7k
  assert(line >= 1 && "line number must be 1-based");
135
22.7k
  --line;
136
137
22.7k
  const char *BufStart = Buffer->getBufferStart();
138
22.7k
  std::vector<T> *Offsets = getOffsets<T>();
139
22.7k
  size_t size = Offsets->size();
140
22.7k
  if (line < size) {
141
22.5k
    auto EOL = Offsets->begin() + line;
142
143
    // The start of the line is the previous line end + 1.
144
22.5k
    const char *LineStart =
145
22.5k
        EOL != Offsets->begin() ? BufStart + EOL[-1] + 1 : BufStart;
146
147
    // The end of the line is the EOL inclusive.
148
22.5k
    const char *LineEnd = BufStart + *EOL + 1;
149
150
22.5k
    return StringRef(LineStart, LineEnd - LineStart);
151
22.5k
  } else {
152
    // Asking for the last line?
153
151
    if (line == size) {
154
37
      const char *LineStart =
155
37
          size != 0 ? BufStart + Offsets->back() + 1 : BufStart;
156
37
      const char *LineEnd = Buffer->getBufferEnd();
157
37
      return StringRef(LineStart, LineEnd - LineStart);
158
114
    } else {
159
114
      return StringRef(Buffer->getBufferEnd(), 0);
160
114
    }
161
151
  }
162
22.7k
}
Unexecuted instantiation: llvh::StringRef llvh::SourceMgr::SrcBuffer::getLineRef<unsigned long>(unsigned int) const
163
164
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&Other)
165
289
  : Buffer(std::move(Other.Buffer)),
166
289
    OffsetCache(Other.OffsetCache),
167
289
    IncludeLoc(Other.IncludeLoc) {
168
289
  Other.OffsetCache = nullptr;
169
289
}
170
171
485
SourceMgr::SrcBuffer::~SrcBuffer() {
172
485
  if (!OffsetCache.isNull()) {
173
103
    if (OffsetCache.is<std::vector<uint8_t>*>())
174
0
      delete OffsetCache.get<std::vector<uint8_t>*>();
175
103
    else if (OffsetCache.is<std::vector<uint16_t>*>())
176
1
      delete OffsetCache.get<std::vector<uint16_t>*>();
177
102
    else if (OffsetCache.is<std::vector<uint32_t>*>())
178
102
      delete OffsetCache.get<std::vector<uint32_t>*>();
179
0
    else
180
0
      delete OffsetCache.get<std::vector<uint64_t>*>();
181
103
    OffsetCache = nullptr;
182
103
  }
183
485
}
184
185
29.7k
std::pair<StringRef, unsigned> SourceMgr::FindLine(SMLoc Loc, unsigned int BufferID) const {
186
29.7k
  if (!BufferID)
187
0
    BufferID = FindBufferContainingLoc(Loc);
188
29.7k
  assert(BufferID && "Invalid Location!");
189
190
29.7k
  auto &SB = getBufferInfo(BufferID);
191
29.7k
  const char *Ptr = Loc.getPointer();
192
193
29.7k
  size_t Sz = SB.Buffer->getBufferSize();
194
29.7k
  if (Sz <= std::numeric_limits<uint8_t>::max())
195
0
    return SB.getLineNumber<uint8_t>(Ptr);
196
29.7k
  else if (Sz <= std::numeric_limits<uint16_t>::max())
197
20.9k
    return SB.getLineNumber<uint16_t>(Ptr);
198
8.86k
  else if (Sz <= std::numeric_limits<uint32_t>::max())
199
8.86k
    return SB.getLineNumber<uint32_t>(Ptr);
200
0
  else
201
0
    return SB.getLineNumber<uint64_t>(Ptr);
202
29.7k
}
203
204
64.5k
StringRef SourceMgr::getLineRef(unsigned line, unsigned BufferID) const {
205
64.5k
  assert(BufferID != 0 && "BufferID must be specified");
206
64.5k
  auto &SB = getBufferInfo(BufferID);
207
64.5k
  size_t Sz = SB.Buffer->getBufferSize();
208
64.5k
  if (Sz <= std::numeric_limits<uint8_t>::max())
209
0
    return SB.getLineRef<uint8_t>(line);
210
64.5k
  else if (Sz <= std::numeric_limits<uint16_t>::max())
211
41.8k
    return SB.getLineRef<uint16_t>(line);
212
22.7k
  else if (Sz <= std::numeric_limits<uint32_t>::max())
213
22.7k
    return SB.getLineRef<uint32_t>(line);
214
0
  else
215
0
    return SB.getLineRef<uint64_t>(line);
216
64.5k
}
217
218
std::pair<unsigned, unsigned>
219
387
SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const {
220
387
  auto LineRefAndNo = FindLine(Loc, BufferID);
221
387
  return std::make_pair(LineRefAndNo.second,
222
387
                        static_cast<unsigned>(Loc.getPointer() - LineRefAndNo.first.data() + 1));
223
387
}
224
225
0
void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
226
0
  if (IncludeLoc == SMLoc()) return;  // Top of stack.
227
228
0
  unsigned CurBuf = FindBufferContainingLoc(IncludeLoc);
229
0
  assert(CurBuf && "Invalid or unspecified location!");
230
231
0
  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
232
233
0
  OS << "Included from "
234
0
     << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
235
0
     << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
236
0
}
237
238
SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
239
                                   const Twine &Msg,
240
                                   ArrayRef<SMRange> Ranges,
241
420
                                   ArrayRef<SMFixIt> FixIts) const {
242
  // First thing to do: find the current buffer containing the specified
243
  // location to pull out the source line.
244
420
  SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges;
245
420
  std::pair<unsigned, unsigned> LineAndCol;
246
420
  StringRef BufferID = "<unknown>";
247
420
  std::string LineStr;
248
249
420
  if (Loc.isValid()) {
250
387
    unsigned CurBuf = FindBufferContainingLoc(Loc);
251
387
    assert(CurBuf && "Invalid or unspecified location!");
252
253
387
    const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf);
254
387
    BufferID = CurMB->getBufferIdentifier();
255
256
    // Scan backward to find the start of the line.
257
387
    const char *LineStart = Loc.getPointer();
258
387
    const char *BufStart = CurMB->getBufferStart();
259
10.9M
    while (LineStart != BufStart && LineStart[-1] != '\n' &&
260
10.9M
           LineStart[-1] != '\r')
261
10.9M
      --LineStart;
262
263
    // Get the end of the line.
264
387
    const char *LineEnd = Loc.getPointer();
265
387
    const char *BufEnd = CurMB->getBufferEnd();
266
13.6M
    while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
267
13.6M
      ++LineEnd;
268
387
    LineStr = std::string(LineStart, LineEnd);
269
270
    // Convert any ranges to column ranges that only intersect the line of the
271
    // location.
272
751
    for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
273
364
      SMRange R = Ranges[i];
274
364
      if (!R.isValid()) continue;
275
276
      // If the line doesn't contain any part of the range, then ignore it.
277
364
      if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
278
0
        continue;
279
280
      // Ignore pieces of the range that go onto other lines.
281
364
      if (R.Start.getPointer() < LineStart)
282
0
        R.Start = SMLoc::getFromPointer(LineStart);
283
364
      if (R.End.getPointer() > LineEnd)
284
0
        R.End = SMLoc::getFromPointer(LineEnd);
285
286
      // Translate from SMLoc ranges to column ranges.
287
      // FIXME: Handle multibyte characters.
288
364
      ColRanges.push_back(std::make_pair(static_cast<unsigned>(R.Start.getPointer()-LineStart),
289
364
                                         static_cast<unsigned>(R.End.getPointer()-LineStart)));
290
364
    }
291
292
387
    LineAndCol = getLineAndColumn(Loc, CurBuf);
293
387
  }
294
295
420
  return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first,
296
420
                      LineAndCol.second-1, Kind, Msg.str(),
297
420
                      LineStr, ColRanges, FixIts);
298
420
}
299
300
void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic,
301
420
                             bool ShowColors) const {
302
  // Report the message with the diagnostic handler if present.
303
420
  if (DiagHandler) {
304
420
    DiagHandler(Diagnostic, DiagContext);
305
420
    return;
306
420
  }
307
308
0
  if (Diagnostic.getLoc().isValid()) {
309
0
    unsigned CurBuf = FindBufferContainingLoc(Diagnostic.getLoc());
310
0
    assert(CurBuf && "Invalid or unspecified location!");
311
0
    PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
312
0
  }
313
314
0
  Diagnostic.print(nullptr, OS, ShowColors);
315
0
}
316
317
void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc,
318
                             SourceMgr::DiagKind Kind,
319
                             const Twine &Msg, ArrayRef<SMRange> Ranges,
320
420
                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
321
420
  PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors);
322
420
}
323
324
void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
325
                             const Twine &Msg, ArrayRef<SMRange> Ranges,
326
420
                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
327
420
  PrintMessage(errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors);
328
420
}
329
330
//===----------------------------------------------------------------------===//
331
// SMDiagnostic Implementation
332
//===----------------------------------------------------------------------===//
333
334
SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN,
335
                           int Line, int Col, SourceMgr::DiagKind Kind,
336
                           StringRef Msg, StringRef LineStr,
337
                           ArrayRef<std::pair<unsigned,unsigned>> Ranges,
338
                           ArrayRef<SMFixIt> Hints)
339
420
  : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind),
340
420
    Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()),
341
420
    FixIts(Hints.begin(), Hints.end()) {
342
420
  llvh::sort(FixIts);
343
420
}
344
345
static void buildFixItLine(std::string &CaretLine, std::string &FixItLine,
346
0
                           ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){
347
0
  if (FixIts.empty())
348
0
    return;
349
350
0
  const char *LineStart = SourceLine.begin();
351
0
  const char *LineEnd = SourceLine.end();
352
353
0
  size_t PrevHintEndCol = 0;
354
355
0
  for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end();
356
0
       I != E; ++I) {
357
    // If the fixit contains a newline or tab, ignore it.
358
0
    if (I->getText().find_first_of("\n\r\t") != StringRef::npos)
359
0
      continue;
360
361
0
    SMRange R = I->getRange();
362
363
    // If the line doesn't contain any part of the range, then ignore it.
364
0
    if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
365
0
      continue;
366
367
    // Translate from SMLoc to column.
368
    // Ignore pieces of the range that go onto other lines.
369
    // FIXME: Handle multibyte characters in the source line.
370
0
    unsigned FirstCol;
371
0
    if (R.Start.getPointer() < LineStart)
372
0
      FirstCol = 0;
373
0
    else
374
0
      FirstCol = R.Start.getPointer() - LineStart;
375
376
    // If we inserted a long previous hint, push this one forwards, and add
377
    // an extra space to show that this is not part of the previous
378
    // completion. This is sort of the best we can do when two hints appear
379
    // to overlap.
380
    //
381
    // Note that if this hint is located immediately after the previous
382
    // hint, no space will be added, since the location is more important.
383
0
    unsigned HintCol = FirstCol;
384
0
    if (HintCol < PrevHintEndCol)
385
0
      HintCol = PrevHintEndCol + 1;
386
387
    // FIXME: This assertion is intended to catch unintended use of multibyte
388
    // characters in fixits. If we decide to do this, we'll have to track
389
    // separate byte widths for the source and fixit lines.
390
0
    assert((size_t)sys::locale::columnWidth(I->getText()) ==
391
0
           I->getText().size());
392
393
    // This relies on one byte per column in our fixit hints.
394
0
    unsigned LastColumnModified = HintCol + I->getText().size();
395
0
    if (LastColumnModified > FixItLine.size())
396
0
      FixItLine.resize(LastColumnModified, ' ');
397
398
0
    std::copy(I->getText().begin(), I->getText().end(),
399
0
              FixItLine.begin() + HintCol);
400
401
0
    PrevHintEndCol = LastColumnModified;
402
403
    // For replacements, mark the removal range with '~'.
404
    // FIXME: Handle multibyte characters in the source line.
405
0
    unsigned LastCol;
406
0
    if (R.End.getPointer() >= LineEnd)
407
0
      LastCol = LineEnd - LineStart;
408
0
    else
409
0
      LastCol = R.End.getPointer() - LineStart;
410
411
0
    std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~');
412
0
  }
413
0
}
414
415
0
static void printSourceLine(raw_ostream &S, StringRef LineContents) {
416
  // Print out the source line one character at a time, so we can expand tabs.
417
0
  for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) {
418
0
    size_t NextTab = LineContents.find('\t', i);
419
    // If there were no tabs left, print the rest, we are done.
420
0
    if (NextTab == StringRef::npos) {
421
0
      S << LineContents.drop_front(i);
422
0
      break;
423
0
    }
424
425
    // Otherwise, print from i to NextTab.
426
0
    S << LineContents.slice(i, NextTab);
427
0
    OutCol += NextTab - i;
428
0
    i = NextTab;
429
430
    // If we have a tab, emit at least one space, then round up to 8 columns.
431
0
    do {
432
0
      S << ' ';
433
0
      ++OutCol;
434
0
    } while ((OutCol % TabStop) != 0);
435
0
  }
436
0
  S << '\n';
437
0
}
438
439
0
static bool isNonASCII(char c) {
440
0
  return c & 0x80;
441
0
}
442
443
void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors,
444
0
                         bool ShowKindLabel) const {
445
  // Display colors only if OS supports colors.
446
0
  ShowColors &= S.has_colors();
447
448
0
  if (ShowColors)
449
0
    S.changeColor(raw_ostream::SAVEDCOLOR, true);
450
451
0
  if (ProgName && ProgName[0])
452
0
    S << ProgName << ": ";
453
454
0
  if (!Filename.empty()) {
455
0
    if (Filename == "-")
456
0
      S << "<stdin>";
457
0
    else
458
0
      S << Filename;
459
460
0
    if (LineNo != -1) {
461
0
      S << ':' << LineNo;
462
0
      if (ColumnNo != -1)
463
0
        S << ':' << (ColumnNo+1);
464
0
    }
465
0
    S << ": ";
466
0
  }
467
468
0
  if (ShowKindLabel) {
469
0
    switch (Kind) {
470
0
    case SourceMgr::DK_Error:
471
0
      if (ShowColors)
472
0
        S.changeColor(raw_ostream::RED, true);
473
0
      S << "error: ";
474
0
      break;
475
0
    case SourceMgr::DK_Warning:
476
0
      if (ShowColors)
477
0
        S.changeColor(raw_ostream::MAGENTA, true);
478
0
      S << "warning: ";
479
0
      break;
480
0
    case SourceMgr::DK_Note:
481
0
      if (ShowColors)
482
0
        S.changeColor(raw_ostream::BLACK, true);
483
0
      S << "note: ";
484
0
      break;
485
0
    case SourceMgr::DK_Remark:
486
0
      if (ShowColors)
487
0
        S.changeColor(raw_ostream::BLUE, true);
488
0
      S << "remark: ";
489
0
      break;
490
0
    }
491
492
0
    if (ShowColors) {
493
0
      S.resetColor();
494
0
      S.changeColor(raw_ostream::SAVEDCOLOR, true);
495
0
    }
496
0
  }
497
498
0
  S << Message << '\n';
499
500
0
  if (ShowColors)
501
0
    S.resetColor();
502
503
0
  if (LineNo == -1 || ColumnNo == -1)
504
0
    return;
505
506
  // FIXME: If there are multibyte or multi-column characters in the source, all
507
  // our ranges will be wrong. To do this properly, we'll need a byte-to-column
508
  // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
509
  // expanding them later, and bail out rather than show incorrect ranges and
510
  // misaligned fixits for any other odd characters.
511
0
  if (find_if(LineContents, isNonASCII) != LineContents.end()) {
512
0
    printSourceLine(S, LineContents);
513
0
    return;
514
0
  }
515
0
  size_t NumColumns = LineContents.size();
516
517
  // Build the line with the caret and ranges.
518
0
  std::string CaretLine(NumColumns+1, ' ');
519
520
  // Expand any ranges.
521
0
  for (unsigned r = 0, e = Ranges.size(); r != e; ++r) {
522
0
    std::pair<unsigned, unsigned> R = Ranges[r];
523
0
    std::fill(&CaretLine[R.first],
524
0
              &CaretLine[std::min((size_t)R.second, CaretLine.size())],
525
0
              '~');
526
0
  }
527
528
  // Add any fix-its.
529
  // FIXME: Find the beginning of the line properly for multibyte characters.
530
0
  std::string FixItInsertionLine;
531
0
  buildFixItLine(CaretLine, FixItInsertionLine, FixIts,
532
0
                 makeArrayRef(Loc.getPointer() - ColumnNo,
533
0
                              LineContents.size()));
534
535
  // Finally, plop on the caret.
536
0
  if (unsigned(ColumnNo) <= NumColumns)
537
0
    CaretLine[ColumnNo] = '^';
538
0
  else
539
0
    CaretLine[NumColumns] = '^';
540
541
  // ... and remove trailing whitespace so the output doesn't wrap for it.  We
542
  // know that the line isn't completely empty because it has the caret in it at
543
  // least.
544
0
  CaretLine.erase(CaretLine.find_last_not_of(' ')+1);
545
546
0
  printSourceLine(S, LineContents);
547
548
0
  if (ShowColors)
549
0
    S.changeColor(raw_ostream::GREEN, true);
550
551
  // Print out the caret line, matching tabs in the source line.
552
0
  for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) {
553
0
    if (i >= LineContents.size() || LineContents[i] != '\t') {
554
0
      S << CaretLine[i];
555
0
      ++OutCol;
556
0
      continue;
557
0
    }
558
559
    // Okay, we have a tab.  Insert the appropriate number of characters.
560
0
    do {
561
0
      S << CaretLine[i];
562
0
      ++OutCol;
563
0
    } while ((OutCol % TabStop) != 0);
564
0
  }
565
0
  S << '\n';
566
567
0
  if (ShowColors)
568
0
    S.resetColor();
569
570
  // Print out the replacement line, matching tabs in the source line.
571
0
  if (FixItInsertionLine.empty())
572
0
    return;
573
574
0
  for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) {
575
0
    if (i >= LineContents.size() || LineContents[i] != '\t') {
576
0
      S << FixItInsertionLine[i];
577
0
      ++OutCol;
578
0
      continue;
579
0
    }
580
581
    // Okay, we have a tab.  Insert the appropriate number of characters.
582
0
    do {
583
0
      S << FixItInsertionLine[i];
584
      // FIXME: This is trying not to break up replacements, but then to re-sync
585
      // with the tabs between replacements. This will fail, though, if two
586
      // fix-it replacements are exactly adjacent, or if a fix-it contains a
587
      // space. Really we should be precomputing column widths, which we'll
588
      // need anyway for multibyte chars.
589
0
      if (FixItInsertionLine[i] != ' ')
590
0
        ++i;
591
0
      ++OutCol;
592
0
    } while (((OutCol % TabStop) != 0) && i != e);
593
0
  }
594
0
  S << '\n';
595
0
}