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
25using namespace clang;
26using namespace clang::cxindex;
27
28//===----------------------------------------------------------------------===//
29// Internal predicates on CXSourceLocations.
30//===----------------------------------------------------------------------===//
31
32static 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
42CXSourceLocation clang_getNullLocation() {
43 CXSourceLocation Result = { .ptr_data: { nullptr, nullptr }, .int_data: 0 };
44 return Result;
45}
46
47unsigned 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
53CXSourceRange clang_getNullRange() {
54 CXSourceRange Result = { .ptr_data: { nullptr, nullptr }, .begin_int_data: 0, .end_int_data: 0 };
55 return Result;
56}
57
58CXSourceRange 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
76unsigned 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
83int clang_Range_isNull(CXSourceRange range) {
84 return clang_equalRanges(range1: range, range2: clang_getNullRange());
85}
86
87
88CXSourceLocation 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
100CXSourceLocation 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
116CXSourceLocation 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
151CXSourceLocation 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
177static 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
189static 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
201int clang_Location_isInSystemHeader(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
212int 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
223void 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
264void 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
295void 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
304void 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 SourceLocation SpellLoc = SM.getSpellingLoc(Loc);
323 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc: SpellLoc);
324 FileID FID = LocInfo.first;
325 unsigned FileOffset = LocInfo.second;
326
327 if (FID.isInvalid())
328 return createNullLocation(file, line, column, offset);
329
330 if (file)
331 *file = cxfile::makeCXFile(FE: SM.getFileEntryRefForID(FID));
332 if (line)
333 *line = SM.getLineNumber(FID, FilePos: FileOffset);
334 if (column)
335 *column = SM.getColumnNumber(FID, FilePos: FileOffset);
336 if (offset)
337 *offset = FileOffset;
338}
339
340void clang_getFileLocation(CXSourceLocation location,
341 CXFile *file,
342 unsigned *line,
343 unsigned *column,
344 unsigned *offset) {
345 if (!isASTUnitSourceLocation(L: location)) {
346 CXLoadedDiagnostic::decodeLocation(location, file, line,
347 column, offset);
348 return;
349 }
350
351 SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data);
352
353 if (!location.ptr_data[0] || Loc.isInvalid())
354 return createNullLocation(file, line, column, offset);
355
356 const SourceManager &SM =
357 *static_cast<const SourceManager*>(location.ptr_data[0]);
358 SourceLocation FileLoc = SM.getFileLoc(Loc);
359 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc: FileLoc);
360 FileID FID = LocInfo.first;
361 unsigned FileOffset = LocInfo.second;
362
363 if (FID.isInvalid())
364 return createNullLocation(file, line, column, offset);
365
366 if (file)
367 *file = cxfile::makeCXFile(FE: SM.getFileEntryRefForID(FID));
368 if (line)
369 *line = SM.getLineNumber(FID, FilePos: FileOffset);
370 if (column)
371 *column = SM.getColumnNumber(FID, FilePos: FileOffset);
372 if (offset)
373 *offset = FileOffset;
374}
375