/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 | } |