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