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