Coverage Report

Created: 2020-06-26 05:44

/home/arjun/llvm-project/llvm/lib/Support/SourceMgr.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file implements the SourceMgr class.  This class is used as a simple
10
// substrate for diagnostics, #include handling, and other low level things for
11
// simple parsers.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#include "llvm/Support/SourceMgr.h"
16
#include "llvm/ADT/ArrayRef.h"
17
#include "llvm/ADT/STLExtras.h"
18
#include "llvm/ADT/SmallVector.h"
19
#include "llvm/ADT/StringRef.h"
20
#include "llvm/ADT/Twine.h"
21
#include "llvm/Support/ErrorOr.h"
22
#include "llvm/Support/Locale.h"
23
#include "llvm/Support/MemoryBuffer.h"
24
#include "llvm/Support/Path.h"
25
#include "llvm/Support/SMLoc.h"
26
#include "llvm/Support/WithColor.h"
27
#include "llvm/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 llvm;
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
0
47
0
  // 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
0
55
0
  if (!NewBufOrErr)
56
0
    return 0;
57
0
58
0
  return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc);
59
0
}
60
61
0
unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
62
0
  for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
63
0
    if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
64
0
        // Use <= here so that a pointer to the null at the end of the buffer
65
0
        // is included as part of the buffer.
66
0
        Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
67
0
      return i + 1;
68
0
  return 0;
69
0
}
70
71
template <typename T>
72
static std::vector<T> &GetOrCreateOffsetCache(void *&OffsetCache,
73
0
                                              MemoryBuffer *Buffer) {
74
0
  if (OffsetCache)
75
0
    return *static_cast<std::vector<T> *>(OffsetCache);
76
0
77
0
  // Lazily fill in the offset cache.
78
0
  auto *Offsets = new std::vector<T>();
79
0
  size_t Sz = Buffer->getBufferSize();
80
0
  assert(Sz <= std::numeric_limits<T>::max());
81
0
  StringRef S = Buffer->getBuffer();
82
0
  for (size_t N = 0; N < Sz; ++N) {
83
0
    if (S[N] == '\n')
84
0
      Offsets->push_back(static_cast<T>(N));
85
0
  }
86
0
87
0
  OffsetCache = Offsets;
88
0
  return *Offsets;
89
0
}
Unexecuted instantiation: SourceMgr.cpp:_ZL22GetOrCreateOffsetCacheIhERSt6vectorIT_SaIS1_EERPvPN4llvm12MemoryBufferE
Unexecuted instantiation: SourceMgr.cpp:_ZL22GetOrCreateOffsetCacheItERSt6vectorIT_SaIS1_EERPvPN4llvm12MemoryBufferE
Unexecuted instantiation: SourceMgr.cpp:_ZL22GetOrCreateOffsetCacheIjERSt6vectorIT_SaIS1_EERPvPN4llvm12MemoryBufferE
Unexecuted instantiation: SourceMgr.cpp:_ZL22GetOrCreateOffsetCacheImERSt6vectorIT_SaIS1_EERPvPN4llvm12MemoryBufferE
90
91
template <typename T>
92
0
unsigned SourceMgr::SrcBuffer::getLineNumberSpecialized(const char *Ptr) const {
93
0
  std::vector<T> &Offsets =
94
0
      GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get());
95
0
96
0
  const char *BufStart = Buffer->getBufferStart();
97
0
  assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
98
0
  ptrdiff_t PtrDiff = Ptr - BufStart;
99
0
  assert(PtrDiff >= 0 &&
100
0
         static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
101
0
  T PtrOffset = static_cast<T>(PtrDiff);
102
0
103
0
  // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get
104
0
  // the line number.
105
0
  return llvm::lower_bound(Offsets, PtrOffset) - Offsets.begin() + 1;
106
0
}
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer24getLineNumberSpecializedIhEEjPKc
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer24getLineNumberSpecializedItEEjPKc
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer24getLineNumberSpecializedIjEEjPKc
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer24getLineNumberSpecializedImEEjPKc
107
108
/// Look up a given \p Ptr in in the buffer, determining which line it came
109
/// from.
110
0
unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const {
111
0
  size_t Sz = Buffer->getBufferSize();
112
0
  if (Sz <= std::numeric_limits<uint8_t>::max())
113
0
    return getLineNumberSpecialized<uint8_t>(Ptr);
114
0
  else if (Sz <= std::numeric_limits<uint16_t>::max())
115
0
    return getLineNumberSpecialized<uint16_t>(Ptr);
116
0
  else if (Sz <= std::numeric_limits<uint32_t>::max())
117
0
    return getLineNumberSpecialized<uint32_t>(Ptr);
118
0
  else
119
0
    return getLineNumberSpecialized<uint64_t>(Ptr);
120
0
}
121
122
template <typename T>
123
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
124
0
    unsigned LineNo) const {
125
0
  std::vector<T> &Offsets =
126
0
      GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get());
127
0
128
0
  // We start counting line and column numbers from 1.
129
0
  if (LineNo != 0)
130
0
    --LineNo;
131
0
132
0
  const char *BufStart = Buffer->getBufferStart();
133
0
134
0
  // The offset cache contains the location of the \n for the specified line,
135
0
  // we want the start of the line.  As such, we look for the previous entry.
136
0
  if (LineNo == 0)
137
0
    return BufStart;
138
0
  if (LineNo > Offsets.size())
139
0
    return nullptr;
140
0
  return BufStart + Offsets[LineNo - 1] + 1;
141
0
}
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer34getPointerForLineNumberSpecializedIhEEPKcj
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer34getPointerForLineNumberSpecializedItEEPKcj
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer34getPointerForLineNumberSpecializedIjEEPKcj
Unexecuted instantiation: _ZNK4llvm9SourceMgr9SrcBuffer34getPointerForLineNumberSpecializedImEEPKcj
142
143
/// Return a pointer to the first character of the specified line number or
144
/// null if the line number is invalid.
145
const char *
146
0
SourceMgr::SrcBuffer::getPointerForLineNumber(unsigned LineNo) const {
147
0
  size_t Sz = Buffer->getBufferSize();
148
0
  if (Sz <= std::numeric_limits<uint8_t>::max())
149
0
    return getPointerForLineNumberSpecialized<uint8_t>(LineNo);
150
0
  else if (Sz <= std::numeric_limits<uint16_t>::max())
151
0
    return getPointerForLineNumberSpecialized<uint16_t>(LineNo);
152
0
  else if (Sz <= std::numeric_limits<uint32_t>::max())
153
0
    return getPointerForLineNumberSpecialized<uint32_t>(LineNo);
154
0
  else
155
0
    return getPointerForLineNumberSpecialized<uint64_t>(LineNo);
156
0
}
157
158
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&Other)
159
    : Buffer(std::move(Other.Buffer)), OffsetCache(Other.OffsetCache),
160
0
      IncludeLoc(Other.IncludeLoc) {
161
0
  Other.OffsetCache = nullptr;
162
0
}
163
164
0
SourceMgr::SrcBuffer::~SrcBuffer() {
165
0
  if (OffsetCache) {
166
0
    size_t Sz = Buffer->getBufferSize();
167
0
    if (Sz <= std::numeric_limits<uint8_t>::max())
168
0
      delete static_cast<std::vector<uint8_t> *>(OffsetCache);
169
0
    else if (Sz <= std::numeric_limits<uint16_t>::max())
170
0
      delete static_cast<std::vector<uint16_t> *>(OffsetCache);
171
0
    else if (Sz <= std::numeric_limits<uint32_t>::max())
172
0
      delete static_cast<std::vector<uint32_t> *>(OffsetCache);
173
0
    else
174
0
      delete static_cast<std::vector<uint64_t> *>(OffsetCache);
175
0
    OffsetCache = nullptr;
176
0
  }
177
0
}
178
179
std::pair<unsigned, unsigned>
180
0
SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const {
181
0
  if (!BufferID)
182
0
    BufferID = FindBufferContainingLoc(Loc);
183
0
  assert(BufferID && "Invalid Location!");
184
0
185
0
  auto &SB = getBufferInfo(BufferID);
186
0
  const char *Ptr = Loc.getPointer();
187
0
188
0
  unsigned LineNo = SB.getLineNumber(Ptr);
189
0
  const char *BufStart = SB.Buffer->getBufferStart();
190
0
  size_t NewlineOffs = StringRef(BufStart, Ptr - BufStart).find_last_of("\n\r");
191
0
  if (NewlineOffs == StringRef::npos)
192
0
    NewlineOffs = ~(size_t)0;
193
0
  return std::make_pair(LineNo, Ptr - BufStart - NewlineOffs);
194
0
}
195
196
/// Given a line and column number in a mapped buffer, turn it into an SMLoc.
197
/// This will return a null SMLoc if the line/column location is invalid.
198
SMLoc SourceMgr::FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo,
199
0
                                         unsigned ColNo) {
200
0
  auto &SB = getBufferInfo(BufferID);
201
0
  const char *Ptr = SB.getPointerForLineNumber(LineNo);
202
0
  if (!Ptr)
203
0
    return SMLoc();
204
0
205
0
  // We start counting line and column numbers from 1.
206
0
  if (ColNo != 0)
207
0
    --ColNo;
208
0
209
0
  // If we have a column number, validate it.
210
0
  if (ColNo) {
211
0
    // Make sure the location is within the current line.
212
0
    if (Ptr + ColNo > SB.Buffer->getBufferEnd())
213
0
      return SMLoc();
214
0
215
0
    // Make sure there is no newline in the way.
216
0
    if (StringRef(Ptr, ColNo).find_first_of("\n\r") != StringRef::npos)
217
0
      return SMLoc();
218
0
219
0
    Ptr += ColNo;
220
0
  }
221
0
222
0
  return SMLoc::getFromPointer(Ptr);
223
0
}
224
225
0
void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
226
0
  if (IncludeLoc == SMLoc())
227
0
    return; // Top of stack.
228
0
229
0
  unsigned CurBuf = FindBufferContainingLoc(IncludeLoc);
230
0
  assert(CurBuf && "Invalid or unspecified location!");
231
0
232
0
  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);
233
0
234
0
  OS << "Included from " << 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, ArrayRef<SMRange> Ranges,
240
0
                                   ArrayRef<SMFixIt> FixIts) const {
241
0
  // First thing to do: find the current buffer containing the specified
242
0
  // location to pull out the source line.
243
0
  SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges;
244
0
  std::pair<unsigned, unsigned> LineAndCol;
245
0
  StringRef BufferID = "<unknown>";
246
0
  std::string LineStr;
247
0
248
0
  if (Loc.isValid()) {
249
0
    unsigned CurBuf = FindBufferContainingLoc(Loc);
250
0
    assert(CurBuf && "Invalid or unspecified location!");
251
0
252
0
    const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf);
253
0
    BufferID = CurMB->getBufferIdentifier();
254
0
255
0
    // Scan backward to find the start of the line.
256
0
    const char *LineStart = Loc.getPointer();
257
0
    const char *BufStart = CurMB->getBufferStart();
258
0
    while (LineStart != BufStart && LineStart[-1] != '\n' &&
259
0
           LineStart[-1] != '\r')
260
0
      --LineStart;
261
0
262
0
    // Get the end of the line.
263
0
    const char *LineEnd = Loc.getPointer();
264
0
    const char *BufEnd = CurMB->getBufferEnd();
265
0
    while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
266
0
      ++LineEnd;
267
0
    LineStr = std::string(LineStart, LineEnd);
268
0
269
0
    // Convert any ranges to column ranges that only intersect the line of the
270
0
    // location.
271
0
    for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
272
0
      SMRange R = Ranges[i];
273
0
      if (!R.isValid())
274
0
        continue;
275
0
276
0
      // If the line doesn't contain any part of the range, then ignore it.
277
0
      if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
278
0
        continue;
279
0
280
0
      // Ignore pieces of the range that go onto other lines.
281
0
      if (R.Start.getPointer() < LineStart)
282
0
        R.Start = SMLoc::getFromPointer(LineStart);
283
0
      if (R.End.getPointer() > LineEnd)
284
0
        R.End = SMLoc::getFromPointer(LineEnd);
285
0
286
0
      // Translate from SMLoc ranges to column ranges.
287
0
      // FIXME: Handle multibyte characters.
288
0
      ColRanges.push_back(std::make_pair(R.Start.getPointer() - LineStart,
289
0
                                         R.End.getPointer() - LineStart));
290
0
    }
291
0
292
0
    LineAndCol = getLineAndColumn(Loc, CurBuf);
293
0
  }
294
0
295
0
  return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first,
296
0
                      LineAndCol.second - 1, Kind, Msg.str(), LineStr,
297
0
                      ColRanges, FixIts);
298
0
}
299
300
void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic,
301
0
                             bool ShowColors) const {
302
0
  // Report the message with the diagnostic handler if present.
303
0
  if (DiagHandler) {
304
0
    DiagHandler(Diagnostic, DiagContext);
305
0
    return;
306
0
  }
307
0
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
0
314
0
  Diagnostic.print(nullptr, OS, ShowColors);
315
0
}
316
317
void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc,
318
                             SourceMgr::DiagKind Kind, const Twine &Msg,
319
                             ArrayRef<SMRange> Ranges, ArrayRef<SMFixIt> FixIts,
320
0
                             bool ShowColors) const {
321
0
  PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors);
322
0
}
323
324
void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind,
325
                             const Twine &Msg, ArrayRef<SMRange> Ranges,
326
0
                             ArrayRef<SMFixIt> FixIts, bool ShowColors) const {
327
0
  PrintMessage(errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors);
328
0
}
329
330
//===----------------------------------------------------------------------===//
331
// SMFixIt Implementation
332
//===----------------------------------------------------------------------===//
333
334
SMFixIt::SMFixIt(SMRange R, const Twine &Replacement)
335
0
    : Range(R), Text(Replacement.str()) {
336
0
  assert(R.isValid());
337
0
}
338
339
//===----------------------------------------------------------------------===//
340
// SMDiagnostic Implementation
341
//===----------------------------------------------------------------------===//
342
343
SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line,
344
                           int Col, SourceMgr::DiagKind Kind, StringRef Msg,
345
                           StringRef LineStr,
346
                           ArrayRef<std::pair<unsigned, unsigned>> Ranges,
347
                           ArrayRef<SMFixIt> Hints)
348
    : SM(&sm), Loc(L), Filename(std::string(FN)), LineNo(Line), ColumnNo(Col),
349
      Kind(Kind), Message(std::string(Msg)), LineContents(std::string(LineStr)),
350
0
      Ranges(Ranges.vec()), FixIts(Hints.begin(), Hints.end()) {
351
0
  llvm::sort(FixIts);
352
0
}
353
354
static void buildFixItLine(std::string &CaretLine, std::string &FixItLine,
355
                           ArrayRef<SMFixIt> FixIts,
356
0
                           ArrayRef<char> SourceLine) {
357
0
  if (FixIts.empty())
358
0
    return;
359
0
360
0
  const char *LineStart = SourceLine.begin();
361
0
  const char *LineEnd = SourceLine.end();
362
0
363
0
  size_t PrevHintEndCol = 0;
364
0
365
0
  for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end(); I != E;
366
0
       ++I) {
367
0
    // If the fixit contains a newline or tab, ignore it.
368
0
    if (I->getText().find_first_of("\n\r\t") != StringRef::npos)
369
0
      continue;
370
0
371
0
    SMRange R = I->getRange();
372
0
373
0
    // If the line doesn't contain any part of the range, then ignore it.
374
0
    if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart)
375
0
      continue;
376
0
377
0
    // Translate from SMLoc to column.
378
0
    // Ignore pieces of the range that go onto other lines.
379
0
    // FIXME: Handle multibyte characters in the source line.
380
0
    unsigned FirstCol;
381
0
    if (R.Start.getPointer() < LineStart)
382
0
      FirstCol = 0;
383
0
    else
384
0
      FirstCol = R.Start.getPointer() - LineStart;
385
0
386
0
    // If we inserted a long previous hint, push this one forwards, and add
387
0
    // an extra space to show that this is not part of the previous
388
0
    // completion. This is sort of the best we can do when two hints appear
389
0
    // to overlap.
390
0
    //
391
0
    // Note that if this hint is located immediately after the previous
392
0
    // hint, no space will be added, since the location is more important.
393
0
    unsigned HintCol = FirstCol;
394
0
    if (HintCol < PrevHintEndCol)
395
0
      HintCol = PrevHintEndCol + 1;
396
0
397
0
    // FIXME: This assertion is intended to catch unintended use of multibyte
398
0
    // characters in fixits. If we decide to do this, we'll have to track
399
0
    // separate byte widths for the source and fixit lines.
400
0
    assert((size_t)sys::locale::columnWidth(I->getText()) ==
401
0
           I->getText().size());
402
0
403
0
    // This relies on one byte per column in our fixit hints.
404
0
    unsigned LastColumnModified = HintCol + I->getText().size();
405
0
    if (LastColumnModified > FixItLine.size())
406
0
      FixItLine.resize(LastColumnModified, ' ');
407
0
408
0
    std::copy(I->getText().begin(), I->getText().end(),
409
0
              FixItLine.begin() + HintCol);
410
0
411
0
    PrevHintEndCol = LastColumnModified;
412
0
413
0
    // For replacements, mark the removal range with '~'.
414
0
    // FIXME: Handle multibyte characters in the source line.
415
0
    unsigned LastCol;
416
0
    if (R.End.getPointer() >= LineEnd)
417
0
      LastCol = LineEnd - LineStart;
418
0
    else
419
0
      LastCol = R.End.getPointer() - LineStart;
420
0
421
0
    std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~');
422
0
  }
423
0
}
424
425
0
static void printSourceLine(raw_ostream &S, StringRef LineContents) {
426
0
  // Print out the source line one character at a time, so we can expand tabs.
427
0
  for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) {
428
0
    size_t NextTab = LineContents.find('\t', i);
429
0
    // If there were no tabs left, print the rest, we are done.
430
0
    if (NextTab == StringRef::npos) {
431
0
      S << LineContents.drop_front(i);
432
0
      break;
433
0
    }
434
0
435
0
    // Otherwise, print from i to NextTab.
436
0
    S << LineContents.slice(i, NextTab);
437
0
    OutCol += NextTab - i;
438
0
    i = NextTab;
439
0
440
0
    // If we have a tab, emit at least one space, then round up to 8 columns.
441
0
    do {
442
0
      S << ' ';
443
0
      ++OutCol;
444
0
    } while ((OutCol % TabStop) != 0);
445
0
  }
446
0
  S << '\n';
447
0
}
448
449
0
static bool isNonASCII(char c) { return c & 0x80; }
450
451
void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, bool ShowColors,
452
0
                         bool ShowKindLabel) const {
453
0
  {
454
0
    WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors);
455
0
456
0
    if (ProgName && ProgName[0])
457
0
      S << ProgName << ": ";
458
0
459
0
    if (!Filename.empty()) {
460
0
      if (Filename == "-")
461
0
        S << "<stdin>";
462
0
      else
463
0
        S << Filename;
464
0
465
0
      if (LineNo != -1) {
466
0
        S << ':' << LineNo;
467
0
        if (ColumnNo != -1)
468
0
          S << ':' << (ColumnNo + 1);
469
0
      }
470
0
      S << ": ";
471
0
    }
472
0
  }
473
0
474
0
  if (ShowKindLabel) {
475
0
    switch (Kind) {
476
0
    case SourceMgr::DK_Error:
477
0
      WithColor::error(OS, "", !ShowColors);
478
0
      break;
479
0
    case SourceMgr::DK_Warning:
480
0
      WithColor::warning(OS, "", !ShowColors);
481
0
      break;
482
0
    case SourceMgr::DK_Note:
483
0
      WithColor::note(OS, "", !ShowColors);
484
0
      break;
485
0
    case SourceMgr::DK_Remark:
486
0
      WithColor::remark(OS, "", !ShowColors);
487
0
      break;
488
0
    }
489
0
  }
490
0
491
0
  WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors)
492
0
      << Message << '\n';
493
0
494
0
  if (LineNo == -1 || ColumnNo == -1)
495
0
    return;
496
0
497
0
  // FIXME: If there are multibyte or multi-column characters in the source, all
498
0
  // our ranges will be wrong. To do this properly, we'll need a byte-to-column
499
0
  // map like Clang's TextDiagnostic. For now, we'll just handle tabs by
500
0
  // expanding them later, and bail out rather than show incorrect ranges and
501
0
  // misaligned fixits for any other odd characters.
502
0
  if (find_if(LineContents, isNonASCII) != LineContents.end()) {
503
0
    printSourceLine(OS, LineContents);
504
0
    return;
505
0
  }
506
0
  size_t NumColumns = LineContents.size();
507
0
508
0
  // Build the line with the caret and ranges.
509
0
  std::string CaretLine(NumColumns + 1, ' ');
510
0
511
0
  // Expand any ranges.
512
0
  for (unsigned r = 0, e = Ranges.size(); r != e; ++r) {
513
0
    std::pair<unsigned, unsigned> R = Ranges[r];
514
0
    std::fill(&CaretLine[R.first],
515
0
              &CaretLine[std::min((size_t)R.second, CaretLine.size())], '~');
516
0
  }
517
0
518
0
  // Add any fix-its.
519
0
  // FIXME: Find the beginning of the line properly for multibyte characters.
520
0
  std::string FixItInsertionLine;
521
0
  buildFixItLine(
522
0
      CaretLine, FixItInsertionLine, FixIts,
523
0
      makeArrayRef(Loc.getPointer() - ColumnNo, LineContents.size()));
524
0
525
0
  // Finally, plop on the caret.
526
0
  if (unsigned(ColumnNo) <= NumColumns)
527
0
    CaretLine[ColumnNo] = '^';
528
0
  else
529
0
    CaretLine[NumColumns] = '^';
530
0
531
0
  // ... and remove trailing whitespace so the output doesn't wrap for it.  We
532
0
  // know that the line isn't completely empty because it has the caret in it at
533
0
  // least.
534
0
  CaretLine.erase(CaretLine.find_last_not_of(' ') + 1);
535
0
536
0
  printSourceLine(OS, LineContents);
537
0
538
0
  {
539
0
    WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors);
540
0
541
0
    // Print out the caret line, matching tabs in the source line.
542
0
    for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) {
543
0
      if (i >= LineContents.size() || LineContents[i] != '\t') {
544
0
        S << CaretLine[i];
545
0
        ++OutCol;
546
0
        continue;
547
0
      }
548
0
549
0
      // Okay, we have a tab.  Insert the appropriate number of characters.
550
0
      do {
551
0
        S << CaretLine[i];
552
0
        ++OutCol;
553
0
      } while ((OutCol % TabStop) != 0);
554
0
    }
555
0
    S << '\n';
556
0
  }
557
0
558
0
  // Print out the replacement line, matching tabs in the source line.
559
0
  if (FixItInsertionLine.empty())
560
0
    return;
561
0
562
0
  for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) {
563
0
    if (i >= LineContents.size() || LineContents[i] != '\t') {
564
0
      OS << FixItInsertionLine[i];
565
0
      ++OutCol;
566
0
      continue;
567
0
    }
568
0
569
0
    // Okay, we have a tab.  Insert the appropriate number of characters.
570
0
    do {
571
0
      OS << FixItInsertionLine[i];
572
0
      // FIXME: This is trying not to break up replacements, but then to re-sync
573
0
      // with the tabs between replacements. This will fail, though, if two
574
0
      // fix-it replacements are exactly adjacent, or if a fix-it contains a
575
0
      // space. Really we should be precomputing column widths, which we'll
576
0
      // need anyway for multibyte chars.
577
0
      if (FixItInsertionLine[i] != ' ')
578
0
        ++i;
579
0
      ++OutCol;
580
0
    } while (((OutCol % TabStop) != 0) && i != e);
581
0
  }
582
0
  OS << '\n';
583
0
}