1 | //===- CXSourceLocation.cpp - CXSourceLocations APIs ------------*- C++ -*-===// |
---|---|
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 defines routines for manipulating CXSourceLocations. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "CXSourceLocation.h" |
14 | #include "CIndexer.h" |
15 | #include "CLog.h" |
16 | #include "CXFile.h" |
17 | #include "CXLoadedDiagnostic.h" |
18 | #include "CXString.h" |
19 | #include "CXTranslationUnit.h" |
20 | #include "clang/Basic/FileManager.h" |
21 | #include "clang/Frontend/ASTUnit.h" |
22 | #include "llvm/Support/Compiler.h" |
23 | #include "llvm/Support/Format.h" |
24 | |
25 | using namespace clang; |
26 | using namespace clang::cxindex; |
27 | |
28 | //===----------------------------------------------------------------------===// |
29 | // Internal predicates on CXSourceLocations. |
30 | //===----------------------------------------------------------------------===// |
31 | |
32 | static bool isASTUnitSourceLocation(const CXSourceLocation &L) { |
33 | // If the lowest bit is clear then the first ptr_data entry is a SourceManager |
34 | // pointer, or the CXSourceLocation is a null location. |
35 | return ((uintptr_t)L.ptr_data[0] & 0x1) == 0; |
36 | } |
37 | |
38 | //===----------------------------------------------------------------------===// |
39 | // Basic construction and comparison of CXSourceLocations and CXSourceRanges. |
40 | //===----------------------------------------------------------------------===// |
41 | |
42 | CXSourceLocation clang_getNullLocation() { |
43 | CXSourceLocation Result = { .ptr_data: { nullptr, nullptr }, .int_data: 0 }; |
44 | return Result; |
45 | } |
46 | |
47 | unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) { |
48 | return (loc1.ptr_data[0] == loc2.ptr_data[0] && |
49 | loc1.ptr_data[1] == loc2.ptr_data[1] && |
50 | loc1.int_data == loc2.int_data); |
51 | } |
52 | |
53 | unsigned clang_isBeforeInTranslationUnit(CXSourceLocation loc1, |
54 | CXSourceLocation loc2) { |
55 | const SourceLocation Loc1 = SourceLocation::getFromRawEncoding(Encoding: loc1.int_data); |
56 | const SourceLocation Loc2 = SourceLocation::getFromRawEncoding(Encoding: loc2.int_data); |
57 | |
58 | const SourceManager &SM = |
59 | *static_cast<const SourceManager *>(loc1.ptr_data[0]); |
60 | // Use the appropriate SourceManager method here rather than operator< because |
61 | // ordering is meaningful only if LHS and RHS have the same FileID. |
62 | return SM.isBeforeInTranslationUnit(LHS: Loc1, RHS: Loc2); |
63 | } |
64 | |
65 | CXSourceRange clang_getNullRange() { |
66 | CXSourceRange Result = { .ptr_data: { nullptr, nullptr }, .begin_int_data: 0, .end_int_data: 0 }; |
67 | return Result; |
68 | } |
69 | |
70 | CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { |
71 | if (!isASTUnitSourceLocation(L: begin)) { |
72 | if (isASTUnitSourceLocation(L: end)) |
73 | return clang_getNullRange(); |
74 | CXSourceRange Result = { .ptr_data: { begin.ptr_data[0], end.ptr_data[0] }, .begin_int_data: 0, .end_int_data: 0 }; |
75 | return Result; |
76 | } |
77 | |
78 | if (begin.ptr_data[0] != end.ptr_data[0] || |
79 | begin.ptr_data[1] != end.ptr_data[1]) |
80 | return clang_getNullRange(); |
81 | |
82 | CXSourceRange Result = { .ptr_data: { begin.ptr_data[0], begin.ptr_data[1] }, |
83 | .begin_int_data: begin.int_data, .end_int_data: end.int_data }; |
84 | |
85 | return Result; |
86 | } |
87 | |
88 | unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) { |
89 | return range1.ptr_data[0] == range2.ptr_data[0] |
90 | && range1.ptr_data[1] == range2.ptr_data[1] |
91 | && range1.begin_int_data == range2.begin_int_data |
92 | && range1.end_int_data == range2.end_int_data; |
93 | } |
94 | |
95 | int clang_Range_isNull(CXSourceRange range) { |
96 | return clang_equalRanges(range1: range, range2: clang_getNullRange()); |
97 | } |
98 | |
99 | |
100 | CXSourceLocation clang_getRangeStart(CXSourceRange range) { |
101 | // Special decoding for CXSourceLocations for CXLoadedDiagnostics. |
102 | if ((uintptr_t)range.ptr_data[0] & 0x1) { |
103 | CXSourceLocation Result = { .ptr_data: { range.ptr_data[0], nullptr }, .int_data: 0 }; |
104 | return Result; |
105 | } |
106 | |
107 | CXSourceLocation Result = { .ptr_data: { range.ptr_data[0], range.ptr_data[1] }, |
108 | .int_data: range.begin_int_data }; |
109 | return Result; |
110 | } |
111 | |
112 | CXSourceLocation clang_getRangeEnd(CXSourceRange range) { |
113 | // Special decoding for CXSourceLocations for CXLoadedDiagnostics. |
114 | if ((uintptr_t)range.ptr_data[0] & 0x1) { |
115 | CXSourceLocation Result = { .ptr_data: { range.ptr_data[1], nullptr }, .int_data: 0 }; |
116 | return Result; |
117 | } |
118 | |
119 | CXSourceLocation Result = { .ptr_data: { range.ptr_data[0], range.ptr_data[1] }, |
120 | .int_data: range.end_int_data }; |
121 | return Result; |
122 | } |
123 | |
124 | //===----------------------------------------------------------------------===// |
125 | // Getting CXSourceLocations and CXSourceRanges from a translation unit. |
126 | //===----------------------------------------------------------------------===// |
127 | |
128 | CXSourceLocation clang_getLocation(CXTranslationUnit TU, |
129 | CXFile file, |
130 | unsigned line, |
131 | unsigned column) { |
132 | if (cxtu::isNotUsableTU(TU)) { |
133 | LOG_BAD_TU(TU); |
134 | return clang_getNullLocation(); |
135 | } |
136 | if (!file) |
137 | return clang_getNullLocation(); |
138 | if (line == 0 || column == 0) |
139 | return clang_getNullLocation(); |
140 | |
141 | LogRef Log = Logger::make(name: __func__); |
142 | ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
143 | ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
144 | FileEntryRef File = *cxfile::getFileEntryRef(File: file); |
145 | SourceLocation SLoc = CXXUnit->getLocation(File, Line: line, Col: column); |
146 | if (SLoc.isInvalid()) { |
147 | if (Log) |
148 | *Log << llvm::format(Fmt: "(\"%s\", %d, %d) = invalid", |
149 | Vals: File.getName().str().c_str(), Vals: line, Vals: column); |
150 | return clang_getNullLocation(); |
151 | } |
152 | |
153 | CXSourceLocation CXLoc = |
154 | cxloc::translateSourceLocation(Context&: CXXUnit->getASTContext(), Loc: SLoc); |
155 | if (Log) |
156 | *Log << llvm::format(Fmt: "(\"%s\", %d, %d) = ", Vals: File.getName().str().c_str(), |
157 | Vals: line, Vals: column) |
158 | << CXLoc; |
159 | |
160 | return CXLoc; |
161 | } |
162 | |
163 | CXSourceLocation clang_getLocationForOffset(CXTranslationUnit TU, |
164 | CXFile file, |
165 | unsigned offset) { |
166 | if (cxtu::isNotUsableTU(TU)) { |
167 | LOG_BAD_TU(TU); |
168 | return clang_getNullLocation(); |
169 | } |
170 | if (!file) |
171 | return clang_getNullLocation(); |
172 | |
173 | ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
174 | |
175 | SourceLocation SLoc |
176 | = CXXUnit->getLocation(File: *cxfile::getFileEntryRef(File: file), Offset: offset); |
177 | |
178 | if (SLoc.isInvalid()) |
179 | return clang_getNullLocation(); |
180 | |
181 | return cxloc::translateSourceLocation(Context&: CXXUnit->getASTContext(), Loc: SLoc); |
182 | } |
183 | |
184 | //===----------------------------------------------------------------------===// |
185 | // Routines for expanding and manipulating CXSourceLocations, regardless |
186 | // of their origin. |
187 | //===----------------------------------------------------------------------===// |
188 | |
189 | static void createNullLocation(CXFile *file, unsigned *line, |
190 | unsigned *column, unsigned *offset) { |
191 | if (file) |
192 | *file = nullptr; |
193 | if (line) |
194 | *line = 0; |
195 | if (column) |
196 | *column = 0; |
197 | if (offset) |
198 | *offset = 0; |
199 | } |
200 | |
201 | static void createNullLocation(CXString *filename, unsigned *line, |
202 | unsigned *column, unsigned *offset = nullptr) { |
203 | if (filename) |
204 | *filename = cxstring::createEmpty(); |
205 | if (line) |
206 | *line = 0; |
207 | if (column) |
208 | *column = 0; |
209 | if (offset) |
210 | *offset = 0; |
211 | } |
212 | |
213 | int clang_Location_isInSystemHeader(CXSourceLocation location) { |
214 | const SourceLocation Loc = |
215 | SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
216 | if (Loc.isInvalid()) |
217 | return 0; |
218 | |
219 | const SourceManager &SM = |
220 | *static_cast<const SourceManager*>(location.ptr_data[0]); |
221 | return SM.isInSystemHeader(Loc); |
222 | } |
223 | |
224 | int clang_Location_isFromMainFile(CXSourceLocation location) { |
225 | const SourceLocation Loc = |
226 | SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
227 | if (Loc.isInvalid()) |
228 | return 0; |
229 | |
230 | const SourceManager &SM = |
231 | *static_cast<const SourceManager*>(location.ptr_data[0]); |
232 | return SM.isWrittenInMainFile(Loc); |
233 | } |
234 | |
235 | void clang_getExpansionLocation(CXSourceLocation location, |
236 | CXFile *file, |
237 | unsigned *line, |
238 | unsigned *column, |
239 | unsigned *offset) { |
240 | if (!isASTUnitSourceLocation(L: location)) { |
241 | CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); |
242 | return; |
243 | } |
244 | |
245 | SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
246 | |
247 | if (!location.ptr_data[0] || Loc.isInvalid()) { |
248 | createNullLocation(file, line, column, offset); |
249 | return; |
250 | } |
251 | |
252 | const SourceManager &SM = |
253 | *static_cast<const SourceManager*>(location.ptr_data[0]); |
254 | SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); |
255 | |
256 | // Check that the FileID is invalid on the expansion location. |
257 | // This can manifest in invalid code. |
258 | FileID fileID = SM.getFileID(SpellingLoc: ExpansionLoc); |
259 | bool Invalid = false; |
260 | const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(FID: fileID, Invalid: &Invalid); |
261 | if (Invalid || !sloc.isFile()) { |
262 | createNullLocation(file, line, column, offset); |
263 | return; |
264 | } |
265 | |
266 | if (file) |
267 | *file = cxfile::makeCXFile(FE: SM.getFileEntryRefForID(FID: fileID)); |
268 | if (line) |
269 | *line = SM.getExpansionLineNumber(Loc: ExpansionLoc); |
270 | if (column) |
271 | *column = SM.getExpansionColumnNumber(Loc: ExpansionLoc); |
272 | if (offset) |
273 | *offset = SM.getDecomposedLoc(Loc: ExpansionLoc).second; |
274 | } |
275 | |
276 | void clang_getPresumedLocation(CXSourceLocation location, |
277 | CXString *filename, |
278 | unsigned *line, |
279 | unsigned *column) { |
280 | if (!isASTUnitSourceLocation(L: location)) { |
281 | // Other SourceLocation implementations do not support presumed locations |
282 | // at this time. |
283 | createNullLocation(filename, line, column); |
284 | return; |
285 | } |
286 | |
287 | SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
288 | |
289 | if (!location.ptr_data[0] || Loc.isInvalid()) { |
290 | createNullLocation(filename, line, column); |
291 | return; |
292 | } |
293 | |
294 | const SourceManager &SM = |
295 | *static_cast<const SourceManager *>(location.ptr_data[0]); |
296 | PresumedLoc PreLoc = SM.getPresumedLoc(Loc); |
297 | if (PreLoc.isInvalid()) { |
298 | createNullLocation(filename, line, column); |
299 | return; |
300 | } |
301 | |
302 | if (filename) *filename = cxstring::createRef(String: PreLoc.getFilename()); |
303 | if (line) *line = PreLoc.getLine(); |
304 | if (column) *column = PreLoc.getColumn(); |
305 | } |
306 | |
307 | void clang_getInstantiationLocation(CXSourceLocation location, |
308 | CXFile *file, |
309 | unsigned *line, |
310 | unsigned *column, |
311 | unsigned *offset) { |
312 | // Redirect to new API. |
313 | clang_getExpansionLocation(location, file, line, column, offset); |
314 | } |
315 | |
316 | void clang_getSpellingLocation(CXSourceLocation location, |
317 | CXFile *file, |
318 | unsigned *line, |
319 | unsigned *column, |
320 | unsigned *offset) { |
321 | if (!isASTUnitSourceLocation(L: location)) { |
322 | CXLoadedDiagnostic::decodeLocation(location, file, line, |
323 | column, offset); |
324 | return; |
325 | } |
326 | |
327 | SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
328 | |
329 | if (!location.ptr_data[0] || Loc.isInvalid()) |
330 | return createNullLocation(file, line, column, offset); |
331 | |
332 | const SourceManager &SM = |
333 | *static_cast<const SourceManager*>(location.ptr_data[0]); |
334 | SourceLocation SpellLoc = SM.getSpellingLoc(Loc); |
335 | std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc: SpellLoc); |
336 | FileID FID = LocInfo.first; |
337 | unsigned FileOffset = LocInfo.second; |
338 | |
339 | if (FID.isInvalid()) |
340 | return createNullLocation(file, line, column, offset); |
341 | |
342 | if (file) |
343 | *file = cxfile::makeCXFile(FE: SM.getFileEntryRefForID(FID)); |
344 | if (line) |
345 | *line = SM.getLineNumber(FID, FilePos: FileOffset); |
346 | if (column) |
347 | *column = SM.getColumnNumber(FID, FilePos: FileOffset); |
348 | if (offset) |
349 | *offset = FileOffset; |
350 | } |
351 | |
352 | void clang_getFileLocation(CXSourceLocation location, |
353 | CXFile *file, |
354 | unsigned *line, |
355 | unsigned *column, |
356 | unsigned *offset) { |
357 | if (!isASTUnitSourceLocation(L: location)) { |
358 | CXLoadedDiagnostic::decodeLocation(location, file, line, |
359 | column, offset); |
360 | return; |
361 | } |
362 | |
363 | SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data); |
364 | |
365 | if (!location.ptr_data[0] || Loc.isInvalid()) |
366 | return createNullLocation(file, line, column, offset); |
367 | |
368 | const SourceManager &SM = |
369 | *static_cast<const SourceManager*>(location.ptr_data[0]); |
370 | SourceLocation FileLoc = SM.getFileLoc(Loc); |
371 | std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc: FileLoc); |
372 | FileID FID = LocInfo.first; |
373 | unsigned FileOffset = LocInfo.second; |
374 | |
375 | if (FID.isInvalid()) |
376 | return createNullLocation(file, line, column, offset); |
377 | |
378 | if (file) |
379 | *file = cxfile::makeCXFile(FE: SM.getFileEntryRefForID(FID)); |
380 | if (line) |
381 | *line = SM.getLineNumber(FID, FilePos: FileOffset); |
382 | if (column) |
383 | *column = SM.getColumnNumber(FID, FilePos: FileOffset); |
384 | if (offset) |
385 | *offset = FileOffset; |
386 | } |
387 |
Definitions
- isASTUnitSourceLocation
- clang_getNullLocation
- clang_equalLocations
- clang_isBeforeInTranslationUnit
- clang_getNullRange
- clang_getRange
- clang_equalRanges
- clang_Range_isNull
- clang_getRangeStart
- clang_getRangeEnd
- clang_getLocation
- clang_getLocationForOffset
- createNullLocation
- createNullLocation
- clang_Location_isInSystemHeader
- clang_Location_isFromMainFile
- clang_getExpansionLocation
- clang_getPresumedLocation
- clang_getInstantiationLocation
- clang_getSpellingLocation
Improve your Profiling and Debugging skills
Find out more