Coverage Report

Created: 2025-09-04 06:34

/src/hermes/lib/Support/SourceErrorManager.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include "hermes/Support/SourceErrorManager.h"
9
#include "hermes/Support/UTF8.h"
10
11
#include "llvh/ADT/DenseMap.h"
12
#include "llvh/Support/raw_ostream.h"
13
14
namespace hermes {
15
16
static const char sTooManyErrors[] = "too many errors emitted";
17
18
0
SourceErrorManager::ICoordTranslator::~ICoordTranslator() = default;
19
20
SourceErrorManager::SourceErrorManager()
21
54
    : warningStatuses_((unsigned)Warning::_NumWarnings, true),
22
54
      warningsAreErrors_((unsigned)Warning::_NumWarnings, false) {
23
54
  sm_.setDiagHandler(SourceErrorManager::printDiagnostic, this);
24
54
}
25
26
void SourceErrorManager::BufferedMessage::addNote(
27
    std::vector<MessageData> &bufferedNotes,
28
    DiagKind dk,
29
    SMLoc loc,
30
    SMRange sm,
31
0
    std::string &&msg) {
32
0
  bufferedNotes.emplace_back(dk, loc, sm, std::move(msg));
33
34
0
  if (!noteCount_)
35
0
    firstNote_ = bufferedNotes.size() - 1;
36
0
  ++noteCount_;
37
0
}
38
39
llvh::iterator_range<const SourceErrorManager::MessageData *>
40
SourceErrorManager::BufferedMessage::notes(
41
10
    const std::vector<MessageData> &bufferedNotes) const {
42
10
  if (!noteCount_)
43
10
    return {nullptr, nullptr};
44
0
  return {
45
0
      bufferedNotes.data() + firstNote_,
46
0
      bufferedNotes.data() + firstNote_ + noteCount_};
47
10
}
48
49
41
void SourceErrorManager::enableBuffering() {
50
41
  ++bufferingEnabled_;
51
41
  assert(bufferingEnabled_ != 0 && "unsigned counter overflow");
52
41
}
53
54
41
void SourceErrorManager::disableBuffering() {
55
41
  assert(bufferingEnabled_ != 0 && "unsigned counter underflow");
56
57
41
  if (--bufferingEnabled_ != 0)
58
0
    return;
59
60
  // Sort all messages.
61
41
  std::sort(
62
41
      bufferedMessages_.begin(),
63
41
      bufferedMessages_.end(),
64
41
      [](const BufferedMessage &a, const BufferedMessage &b) {
65
        // Make sure the "too many errors" message is always last.
66
5
        if (a.dk == DK_Error && !a.loc.isValid() && a.msg == sTooManyErrors)
67
5
          return false;
68
0
        if (b.dk == DK_Error && !b.loc.isValid() && b.msg == sTooManyErrors)
69
0
          return true;
70
0
        return a.loc.getPointer() < b.loc.getPointer();
71
0
      });
72
73
  // Print them.
74
41
  for (const auto &bm : bufferedMessages_) {
75
10
    doPrintMessage(bm.dk, bm.loc, bm.sm, bm.msg);
76
10
    for (const auto &note : bm.notes(bufferedNotes_))
77
0
      doPrintMessage(note.dk, note.loc, note.sm, note.msg);
78
10
  }
79
80
  // Clean the buffer.
81
41
  bufferedMessages_.clear();
82
41
  bufferedNotes_.clear();
83
41
}
84
85
unsigned SourceErrorManager::addNewSourceBuffer(
86
101
    std::unique_ptr<llvh::MemoryBuffer> f) {
87
101
  unsigned bufId = sm_.AddNewSourceBuffer(std::move(f), SMLoc{});
88
101
  assert(
89
101
      !isVirtualBufferId(bufId) && "unexpected virtual buf id from SourceMgr");
90
101
  return bufId;
91
101
}
92
93
/// Add a source buffer which maps to a filename. It doesn't contain any
94
/// source and the only operation that can be performed on that buffer is to
95
/// obtain the filename.
96
unsigned SourceErrorManager::addNewVirtualSourceBuffer(
97
0
    llvh::StringRef fileName) {
98
0
  return indexToVirtualBufferId(virtualBufferNames_.insert(fileName));
99
0
}
100
101
36
llvh::StringRef SourceErrorManager::getBufferFileName(unsigned bufId) const {
102
36
  if (isVirtualBufferId(bufId))
103
0
    return virtualBufferNames_[virtualBufferIdToIndex(bufId)];
104
36
  else
105
36
    return sm_.getMemoryBuffer(bufId)->getBufferIdentifier();
106
36
}
107
108
void SourceErrorManager::dumpCoords(
109
    llvh::raw_ostream &OS,
110
0
    const SourceCoords &coords) {
111
0
  if (coords.isValid()) {
112
0
    OS << getSourceUrl(coords.bufId) << ":" << coords.line << "," << coords.col;
113
0
  } else {
114
0
    OS << "none:0,0";
115
0
  }
116
0
}
117
118
0
void SourceErrorManager::dumpCoords(llvh::raw_ostream &OS, SMLoc loc) {
119
0
  SourceCoords coords;
120
0
  findBufferLineAndLoc(loc, coords);
121
0
  dumpCoords(OS, coords);
122
0
}
123
124
void SourceErrorManager::countAndGenMessage(
125
    DiagKind dk,
126
    SMLoc loc,
127
    SMRange sm,
128
166
    const Twine &msg) {
129
166
  ++messageCount_[dk];
130
166
  doGenMessage(dk, loc, sm, msg);
131
132
166
  if (LLVM_UNLIKELY(dk == DK_Error && messageCount_[DK_Error] == errorLimit_)) {
133
18
    errorLimitReached_ = true;
134
18
    doGenMessage(DK_Error, {}, {}, sTooManyErrors);
135
18
  }
136
166
}
137
138
void SourceErrorManager::doGenMessage(
139
    hermes::SourceErrorManager::DiagKind dk,
140
    llvh::SMLoc loc,
141
    llvh::SMRange sm,
142
184
    llvh::Twine const &msg) {
143
184
  if (bufferingEnabled_) {
144
    // If this message is a note, try to associate it with the last message.
145
    // Note that theoretically the first buffered message could be a note, so
146
    // we play it safe here (even though it should never happen).
147
10
    if (dk == DK_Note && !bufferedMessages_.empty()) {
148
0
      bufferedMessages_.back().addNote(bufferedNotes_, dk, loc, sm, msg.str());
149
10
    } else {
150
10
      bufferedMessages_.emplace_back(dk, loc, sm, msg.str());
151
10
    }
152
174
  } else {
153
174
    doPrintMessage(dk, loc, sm, msg);
154
174
  }
155
184
}
156
157
void SourceErrorManager::doPrintMessage(
158
    DiagKind dk,
159
    SMLoc loc,
160
    SMRange sm,
161
184
    const Twine &msg) {
162
184
  sm_.PrintMessage(
163
184
      loc,
164
184
      static_cast<llvh::SourceMgr::DiagKind>(dk),
165
184
      msg,
166
184
      sm.isValid() ? llvh::ArrayRef<SMRange>(sm)
167
184
                   : llvh::ArrayRef<SMRange>(llvh::None),
168
184
      llvh::None,
169
184
      outputOptions_.showColors);
170
184
}
171
172
void SourceErrorManager::message(
173
    hermes::SourceErrorManager::DiagKind dk,
174
    llvh::SMLoc loc,
175
    llvh::SMRange sm,
176
    llvh::Twine const &msg,
177
    hermes::Warning w,
178
173
    Subsystem subsystem) {
179
173
  assert(dk <= DK_Note);
180
173
  if (suppressMessages_) {
181
0
    if (*suppressMessages_ == Subsystem::Unspecified) {
182
0
      return;
183
0
    }
184
0
    if (subsystem == *suppressMessages_) {
185
0
      return;
186
0
    }
187
0
  }
188
  // Suppress all messages once the error limit has been reached.
189
173
  if (LLVM_UNLIKELY(errorLimitReached_))
190
7
    return;
191
166
  if (dk == DK_Warning && !isWarningEnabled(w)) {
192
0
    lastMessageSuppressed_ = true;
193
0
    return;
194
0
  }
195
  // Automatically suppress notes if the last message was suppressed.
196
166
  if (dk == DK_Note && lastMessageSuppressed_)
197
0
    return;
198
166
  lastMessageSuppressed_ = false;
199
200
  /// Optionally upgrade warnings into errors.
201
166
  if (dk == DK_Warning && isWarningAnError(w)) {
202
0
    dk = DK_Error;
203
0
  }
204
166
  assert(static_cast<unsigned>(dk) < kMessageCountSize && "bounds check");
205
206
166
  if (externalMessageBuffer_) {
207
0
    externalMessageBuffer_->addMessage(dk, loc, sm, msg);
208
0
    return;
209
0
  }
210
211
166
  countAndGenMessage(dk, loc, sm, msg);
212
166
}
213
214
void SourceErrorManager::message(
215
    DiagKind dk,
216
    SMLoc loc,
217
    SMRange sm,
218
    const Twine &msg,
219
25
    Subsystem subsystem) {
220
25
  message(dk, loc, sm, msg, Warning::NoWarning, subsystem);
221
25
}
222
223
void SourceErrorManager::message(
224
    DiagKind dk,
225
    SMRange sm,
226
    const Twine &msg,
227
4
    Subsystem subsystem) {
228
4
  message(dk, sm.Start, sm, msg, subsystem);
229
4
}
230
231
void SourceErrorManager::message(
232
    DiagKind dk,
233
    SMLoc loc,
234
    const Twine &msg,
235
19
    Subsystem subsystem) {
236
19
  message(dk, loc, SMRange{}, msg, subsystem);
237
19
}
238
239
auto SourceErrorManager::findBufferAndLine(SMLoc loc) const
240
3.76k
    -> llvh::Optional<LineCoord> {
241
3.76k
  if (!loc.isValid())
242
0
    return llvh::None;
243
244
3.76k
  auto bufId = sm_.FindBufferContainingLoc(loc);
245
3.76k
  if (!bufId)
246
0
    return llvh::None;
247
248
3.76k
  auto lineRefAndNo = sm_.FindLine(loc, bufId);
249
250
3.76k
  return LineCoord{bufId, lineRefAndNo.second, lineRefAndNo.first};
251
3.76k
}
252
253
/// Adjust the source location backwards making sure it doesn't point to \r or
254
/// in the middle of a utf-8 sequence.
255
950k
static inline SMLoc adjustSourceLocation(const char *bufStart, SMLoc loc) {
256
950k
  const char *ptr = loc.getPointer();
257
  // In the very unlikely case that `loc` points to a '\r', we skip backwards
258
  // until we find another character, while being careful not to fall off the
259
  // beginning of the buffer.
260
950k
  if (LLVM_UNLIKELY(*ptr == '\r') ||
261
950k
      LLVM_UNLIKELY(isUTF8ContinuationByte(*ptr))) {
262
2
    do {
263
2
      if (LLVM_UNLIKELY(ptr == bufStart)) {
264
        // This is highly unlikely but theoretically possible. There were only
265
        // '\r' between `loc` and the start of the buffer.
266
0
        break;
267
0
      }
268
2
      --ptr;
269
2
    } while (*ptr == '\r' || isUTF8ContinuationByte(*ptr));
270
1
  }
271
0
  return SMLoc::getFromPointer(ptr);
272
950k
}
273
274
956k
static bool locInside(llvh::StringRef str, SMLoc loc) {
275
956k
  const char *ptr = loc.getPointer();
276
956k
  return ptr >= str.begin() && ptr < str.end();
277
956k
}
278
279
inline void SourceErrorManager::FindLineCache::fillCoords(
280
    SMLoc loc,
281
950k
    SourceCoords &result) {
282
950k
  loc = adjustSourceLocation(lineRef.data(), loc);
283
950k
  result.bufId = bufferId;
284
950k
  result.line = lineNo;
285
950k
  result.col = loc.getPointer() - lineRef.data() + 1;
286
950k
}
287
288
950k
bool SourceErrorManager::findBufferLineAndLoc(SMLoc loc, SourceCoords &result) {
289
950k
  if (!loc.isValid()) {
290
0
    result.bufId = 0;
291
0
    return false;
292
0
  }
293
294
950k
  if (findLineCache_.bufferId) {
295
    // Check the cache with the hope that the lookup is within the last line or
296
    // the next line.
297
950k
    if (locInside(findLineCache_.lineRef, loc)) {
298
944k
      findLineCache_.fillCoords(loc, result);
299
944k
      return true;
300
944k
    }
301
5.81k
    if (locInside(findLineCache_.nextLineRef, loc)) {
302
2.08k
      ++findLineCache_.lineNo;
303
2.08k
      findLineCache_.lineRef = findLineCache_.nextLineRef;
304
2.08k
      findLineCache_.nextLineRef =
305
2.08k
          sm_.getLineRef(findLineCache_.lineNo + 1, findLineCache_.bufferId);
306
307
2.08k
      findLineCache_.fillCoords(loc, result);
308
2.08k
      return true;
309
2.08k
    }
310
311
3.73k
    findLineCache_.bufferId = 0;
312
3.73k
  }
313
314
3.76k
  auto lineCoord = findBufferAndLine(loc);
315
3.76k
  if (!lineCoord) {
316
0
    result.bufId = 0;
317
0
    return false;
318
0
  }
319
320
  // Populate the cache.
321
3.76k
  findLineCache_.bufferId = lineCoord->bufId;
322
3.76k
  findLineCache_.lineNo = lineCoord->lineNo;
323
3.76k
  findLineCache_.lineRef = lineCoord->lineRef;
324
3.76k
  findLineCache_.nextLineRef =
325
3.76k
      sm_.getLineRef(findLineCache_.lineNo + 1, lineCoord->bufId);
326
327
3.76k
  findLineCache_.fillCoords(loc, result);
328
3.76k
  return true;
329
3.76k
}
330
331
bool SourceErrorManager::findBufferLineAndLoc(
332
    llvh::SMLoc loc,
333
    hermes::SourceErrorManager::SourceCoords &result,
334
950k
    bool translate) {
335
950k
  if (!findBufferLineAndLoc(loc, result))
336
0
    return false;
337
950k
  if (translate && translator_)
338
0
    translator_->translate(result);
339
950k
  return true;
340
950k
}
341
342
0
uint32_t SourceErrorManager::findBufferIdForLoc(SMLoc loc) const {
343
0
  return sm_.FindBufferContainingLoc(loc);
344
0
}
345
346
const llvh::MemoryBuffer *SourceErrorManager::findBufferForLoc(
347
0
    SMLoc loc) const {
348
0
  uint32_t bufID = findBufferIdForLoc(loc);
349
0
  if (bufID == 0) {
350
0
    return nullptr;
351
0
  }
352
0
  return sm_.getMemoryBuffer(bufID);
353
0
}
354
355
0
SMLoc SourceErrorManager::findSMLocFromCoords(SourceCoords coords) {
356
0
  if (!coords.isValid())
357
0
    return {};
358
359
  // TODO: optimize this with caching, etc.
360
0
  auto *buffer = getSourceBuffer(coords.bufId);
361
0
  if (!buffer)
362
0
    return {};
363
364
0
  const char *cur = buffer->getBufferStart();
365
0
  const char *end = buffer->getBufferEnd();
366
367
  // Loop until we find the line or we reach EOF.
368
0
  unsigned lineNumber = 1;
369
0
  const char *lineEnd;
370
0
  while ((lineEnd = (const char *)std::memchr(cur, '\n', end - cur)) !=
371
0
             nullptr &&
372
0
         lineNumber != coords.line) {
373
0
    ++lineNumber;
374
0
    cur = lineEnd + 1;
375
0
  }
376
377
  // If we didn't find LF, the end of the buffer is the end of the line.
378
0
  if (!lineEnd)
379
0
    lineEnd = end;
380
381
  // The last line we found is [cur..lineEnd) and its number is lineNumber.
382
  // Is it the right one?
383
0
  if (lineNumber != coords.line)
384
0
    return {};
385
386
  // Trim a CR at start and end to account for all crazy line endings.
387
0
  if (cur != lineEnd && *cur == '\r')
388
0
    ++cur;
389
0
  if (cur != lineEnd && *(lineEnd - 1) == '\r')
390
0
    --lineEnd;
391
392
  // Special case for empty line.
393
0
  if (cur == lineEnd) {
394
    // Column 1 or 0 in an empty line should work.
395
0
    if (coords.col <= 1)
396
0
      return SMLoc::getFromPointer(cur);
397
0
    return {};
398
0
  }
399
400
  // Check for presence of UTF-8.
401
0
  bool utf8 = false;
402
0
  for (const char *p = cur; p != lineEnd; ++p) {
403
0
    if (LLVM_UNLIKELY(*p & 0x80)) {
404
0
      utf8 = true;
405
0
      break;
406
0
    }
407
0
  }
408
409
  // ASCII is easy - just add the offset.
410
0
  if (LLVM_LIKELY(!utf8)) {
411
    // Is the column in range?
412
0
    if (coords.col > (size_t)(lineEnd - cur))
413
0
      return {};
414
0
    return SMLoc::getFromPointer(cur + coords.col - 1);
415
0
  }
416
417
  // Scan for the column while accounting for multi-byte characters.
418
0
  unsigned column = 0;
419
0
  for (; cur != lineEnd; ++cur) {
420
    // Skip continuation bytes.
421
0
    if (isUTF8ContinuationByte(*cur))
422
0
      continue;
423
0
    if (++column == coords.col)
424
0
      return SMLoc::getFromPointer(cur);
425
0
  }
426
427
0
  return {};
428
0
}
429
430
/// Given an SMDiagnostic, return {sourceLine, caretLine}, respecting the error
431
/// output options
432
std::pair<std::string, std::string> SourceErrorManager::buildSourceAndCaretLine(
433
    const llvh::SMDiagnostic &diag,
434
0
    SourceErrorOutputOptions opts) {
435
  // Decode our source line to UTF-32
436
  // Ignore errors (UTF-8 errors will become replacement character)
437
  // Don't try to decode past embedded nulls
438
  // Map from narrow byte to column as we go
439
0
  std::vector<uint32_t> narrowByteToColumn;
440
0
  std::u32string sourceLine;
441
0
  std::string narrowSourceLine = diag.getLineContents();
442
0
  const char *cursor = narrowSourceLine.c_str();
443
0
  while (*cursor) {
444
0
    const char *prev = cursor;
445
0
    sourceLine.push_back(decodeUTF8<true>(cursor, [](const llvh::Twine &) {}));
446
0
    while (prev++ < cursor) {
447
0
      narrowByteToColumn.push_back(sourceLine.size() - 1);
448
0
    }
449
0
  }
450
0
  const size_t numColumns = sourceLine.size();
451
452
  // Widening helper
453
0
  auto widenColumn = [&](unsigned narrowColumn) -> unsigned {
454
0
    return narrowColumn < narrowByteToColumn.size()
455
0
        ? narrowByteToColumn[narrowColumn]
456
0
        : numColumns;
457
0
  };
458
459
  // Widen the caret column and ranges using our map
460
  // Ranges are of the form [first, last)
461
0
  assert(diag.getColumnNo() >= 0);
462
0
  const size_t columnNo = widenColumn(diag.getColumnNo());
463
0
  std::vector<std::pair<unsigned, unsigned>> ranges;
464
0
  for (const auto &r : diag.getRanges()) {
465
0
    ranges.emplace_back(widenColumn(r.first), widenColumn(r.second));
466
0
  }
467
468
  // Build the line with the caret and ranges.
469
0
  std::string caretLine(numColumns + 1, ' ');
470
0
  for (const auto &range : ranges) {
471
0
    if (range.first < caretLine.size()) {
472
0
      std::fill(
473
0
          &caretLine[range.first],
474
0
          &caretLine[std::min((size_t)range.second, caretLine.size())],
475
0
          '~');
476
0
    }
477
0
  }
478
0
  caretLine[std::min(size_t(columnNo), numColumns)] = '^';
479
0
  caretLine.erase(caretLine.find_last_not_of(' ') + 1);
480
481
  // Expand tabs to spaces in both the source and caret line
482
0
  const size_t tabStop = SourceErrorOutputOptions::TabStop;
483
0
  for (size_t pos = sourceLine.find('\t'); pos < sourceLine.size();
484
0
       pos = sourceLine.find('\t', pos)) {
485
0
    size_t expandCount = tabStop - (pos % tabStop);
486
0
    sourceLine.replace(pos, 1, expandCount, ' ');
487
0
    if (pos < caretLine.size()) {
488
      // Reuse the character in the caretLine, so that tabs in tildes expand to
489
      // more tildes
490
0
      caretLine.replace(pos, 1, expandCount, caretLine[pos]);
491
0
    }
492
0
    pos += expandCount;
493
0
  }
494
495
  // Trim the lines to respect preferredMaxErrorWidth
496
  // "Focus" around the caret, and any range intersecting it
497
  // Note ranges are of the form [start, end) and not [start, length)
498
0
  int focusStart = columnNo;
499
0
  int focusLength = 1;
500
0
  for (const auto &r : ranges) {
501
0
    if (r.first <= size_t(columnNo) && size_t(columnNo) < r.second) {
502
0
      focusStart = r.first;
503
0
      focusLength = r.second - r.first;
504
0
      break;
505
0
    }
506
0
  }
507
0
  size_t desiredLineLength = std::max(
508
0
      opts.preferredMaxErrorWidth,
509
0
      focusLength + SourceErrorOutputOptions::MinimumSourceContext);
510
0
  if (sourceLine.size() > desiredLineLength) {
511
0
    int focusCenter = focusStart + focusLength / 2;
512
0
    int leftTrimAmount = focusCenter - desiredLineLength / 2;
513
0
    if (leftTrimAmount > 0) {
514
0
      caretLine.erase(0, leftTrimAmount);
515
0
      sourceLine.erase(0, leftTrimAmount);
516
0
      std::fill(sourceLine.begin(), sourceLine.begin() + 3, '.');
517
0
    }
518
0
    if (sourceLine.size() > desiredLineLength) {
519
      // Trim on the right
520
0
      caretLine.erase(std::min(caretLine.size(), desiredLineLength));
521
0
      sourceLine.erase(desiredLineLength);
522
0
      std::fill(sourceLine.end() - 3, sourceLine.end(), '.');
523
0
    }
524
0
  }
525
526
  // Convert sourceLine back to narrow
527
0
  narrowSourceLine.clear();
528
0
  for (uint32_t c : sourceLine) {
529
0
    char buffer[UTF8CodepointMaxBytes] = {};
530
0
    char *buffCursor = buffer;
531
0
    encodeUTF8(buffCursor, c);
532
0
    narrowSourceLine.append(buffer, buffCursor);
533
0
  }
534
0
  return {std::move(narrowSourceLine), std::move(caretLine)};
535
0
}
536
537
void SourceErrorManager::printDiagnostic(
538
    const llvh::SMDiagnostic &diag,
539
0
    void *ctx) {
540
0
  using llvh::raw_ostream;
541
0
  const SourceErrorManager *self = static_cast<SourceErrorManager *>(ctx);
542
0
  const SourceErrorOutputOptions opts = self->outputOptions_;
543
0
  auto &S = llvh::errs();
544
545
0
  llvh::StringRef filename = diag.getFilename();
546
0
  int lineNo = diag.getLineNo();
547
0
  int columnNo = diag.getColumnNo();
548
549
  // Helpers to conditionally set or reset a color
550
0
  auto changeColor = [&](raw_ostream::Colors color) {
551
0
    if (opts.showColors)
552
0
      S.changeColor(color, true);
553
0
  };
554
555
0
  auto resetColor = [&]() {
556
0
    if (opts.showColors)
557
0
      S.resetColor();
558
0
  };
559
560
0
  changeColor(raw_ostream::SAVEDCOLOR);
561
0
  if (!filename.empty()) {
562
0
    S << (filename == "-" ? "<stdin>" : filename);
563
0
    if (lineNo != -1) {
564
0
      S << ':' << lineNo;
565
0
      if (columnNo != -1)
566
0
        S << ':' << (columnNo + 1);
567
0
    }
568
0
    S << ": ";
569
0
  }
570
571
0
  switch (diag.getKind()) {
572
0
    case llvh::SourceMgr::DK_Error:
573
0
      changeColor(raw_ostream::RED);
574
0
      S << "error: ";
575
0
      break;
576
0
    case llvh::SourceMgr::DK_Warning:
577
0
      changeColor(raw_ostream::MAGENTA);
578
0
      S << "warning: ";
579
0
      break;
580
0
    case llvh::SourceMgr::DK_Note:
581
0
      changeColor(raw_ostream::BLACK);
582
0
      S << "note: ";
583
0
      break;
584
0
    case llvh::SourceMgr::DK_Remark:
585
0
      changeColor(raw_ostream::BLACK);
586
0
      S << "remark: ";
587
0
      break;
588
0
  }
589
590
0
  resetColor();
591
0
  changeColor(raw_ostream::SAVEDCOLOR);
592
0
  S << diag.getMessage() << '\n';
593
0
  resetColor();
594
595
0
  if (lineNo == -1 || columnNo == -1)
596
0
    return;
597
598
0
  std::string sourceLine;
599
0
  std::string caretLine;
600
0
  std::tie(sourceLine, caretLine) = buildSourceAndCaretLine(diag, opts);
601
602
  // Check for non-ASCII characters, which may have a width > 1
603
  // If we find them, don't try to show the caret line
604
  // TODO: bravely teach buildSourceAndCaretLine to use wcwidth(), lifting this
605
  // restriction
606
0
  bool showCaret = isAllASCII(sourceLine.begin(), sourceLine.end());
607
608
0
  S << sourceLine << '\n';
609
0
  if (showCaret) {
610
0
    changeColor(raw_ostream::GREEN);
611
0
    S << caretLine << '\n';
612
0
    resetColor();
613
0
  }
614
0
}
615
616
1.33k
SMLoc SourceErrorManager::convertEndToLocation(SMRange range) {
617
  // If the range is empty, return the starting point.
618
1.33k
  if (range.Start == range.End)
619
0
    return range.Start;
620
621
1.33k
  return SMLoc::getFromPointer(range.End.getPointer() - 1);
622
1.33k
}
623
624
} // namespace hermes