1 //===- CXSourceLocation.cpp - CXSourceLocations APIs ------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines routines for manipulating CXSourceLocations.
11 //===----------------------------------------------------------------------===//
13 #include "CXSourceLocation.h"
17 #include "CXLoadedDiagnostic.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"
25 using namespace clang
;
26 using namespace clang::cxindex
;
28 //===----------------------------------------------------------------------===//
29 // Internal predicates on CXSourceLocations.
30 //===----------------------------------------------------------------------===//
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;
38 //===----------------------------------------------------------------------===//
39 // Basic construction and comparison of CXSourceLocations and CXSourceRanges.
40 //===----------------------------------------------------------------------===//
42 CXSourceLocation
clang_getNullLocation() {
43 CXSourceLocation Result
= { { nullptr, nullptr }, 0 };
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
);
53 CXSourceRange
clang_getNullRange() {
54 CXSourceRange Result
= { { nullptr, nullptr }, 0, 0 };
58 CXSourceRange
clang_getRange(CXSourceLocation begin
, CXSourceLocation end
) {
59 if (!isASTUnitSourceLocation(begin
)) {
60 if (isASTUnitSourceLocation(end
))
61 return clang_getNullRange();
62 CXSourceRange Result
= { { begin
.ptr_data
[0], end
.ptr_data
[0] }, 0, 0 };
66 if (begin
.ptr_data
[0] != end
.ptr_data
[0] ||
67 begin
.ptr_data
[1] != end
.ptr_data
[1])
68 return clang_getNullRange();
70 CXSourceRange Result
= { { begin
.ptr_data
[0], begin
.ptr_data
[1] },
71 begin
.int_data
, end
.int_data
};
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
;
83 int clang_Range_isNull(CXSourceRange range
) {
84 return clang_equalRanges(range
, clang_getNullRange());
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
= { { range
.ptr_data
[0], nullptr }, 0 };
95 CXSourceLocation Result
= { { range
.ptr_data
[0], range
.ptr_data
[1] },
96 range
.begin_int_data
};
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
= { { range
.ptr_data
[1], nullptr }, 0 };
107 CXSourceLocation Result
= { { range
.ptr_data
[0], range
.ptr_data
[1] },
108 range
.end_int_data
};
112 //===----------------------------------------------------------------------===//
113 // Getting CXSourceLocations and CXSourceRanges from a translation unit.
114 //===----------------------------------------------------------------------===//
116 CXSourceLocation
clang_getLocation(CXTranslationUnit TU
,
120 if (cxtu::isNotUsableTU(TU
)) {
122 return clang_getNullLocation();
125 return clang_getNullLocation();
126 if (line
== 0 || column
== 0)
127 return clang_getNullLocation();
129 LogRef Log
= Logger::make(__func__
);
130 ASTUnit
*CXXUnit
= cxtu::getASTUnit(TU
);
131 ASTUnit::ConcurrencyCheck
Check(*CXXUnit
);
132 FileEntryRef File
= *cxfile::getFileEntryRef(file
);
133 SourceLocation SLoc
= CXXUnit
->getLocation(File
, line
, column
);
134 if (SLoc
.isInvalid()) {
136 *Log
<< llvm::format("(\"%s\", %d, %d) = invalid",
137 File
.getName().str().c_str(), line
, column
);
138 return clang_getNullLocation();
141 CXSourceLocation CXLoc
=
142 cxloc::translateSourceLocation(CXXUnit
->getASTContext(), SLoc
);
144 *Log
<< llvm::format("(\"%s\", %d, %d) = ", File
.getName().str().c_str(),
151 CXSourceLocation
clang_getLocationForOffset(CXTranslationUnit TU
,
154 if (cxtu::isNotUsableTU(TU
)) {
156 return clang_getNullLocation();
159 return clang_getNullLocation();
161 ASTUnit
*CXXUnit
= cxtu::getASTUnit(TU
);
164 = CXXUnit
->getLocation(*cxfile::getFileEntryRef(file
), offset
);
166 if (SLoc
.isInvalid())
167 return clang_getNullLocation();
169 return cxloc::translateSourceLocation(CXXUnit
->getASTContext(), SLoc
);
172 //===----------------------------------------------------------------------===//
173 // Routines for expanding and manipulating CXSourceLocations, regardless
175 //===----------------------------------------------------------------------===//
177 static void createNullLocation(CXFile
*file
, unsigned *line
,
178 unsigned *column
, unsigned *offset
) {
189 static void createNullLocation(CXString
*filename
, unsigned *line
,
190 unsigned *column
, unsigned *offset
= nullptr) {
192 *filename
= cxstring::createEmpty();
201 int clang_Location_isInSystemHeader(CXSourceLocation location
) {
202 const SourceLocation Loc
=
203 SourceLocation::getFromRawEncoding(location
.int_data
);
207 const SourceManager
&SM
=
208 *static_cast<const SourceManager
*>(location
.ptr_data
[0]);
209 return SM
.isInSystemHeader(Loc
);
212 int clang_Location_isFromMainFile(CXSourceLocation location
) {
213 const SourceLocation Loc
=
214 SourceLocation::getFromRawEncoding(location
.int_data
);
218 const SourceManager
&SM
=
219 *static_cast<const SourceManager
*>(location
.ptr_data
[0]);
220 return SM
.isWrittenInMainFile(Loc
);
223 void clang_getExpansionLocation(CXSourceLocation location
,
228 if (!isASTUnitSourceLocation(location
)) {
229 CXLoadedDiagnostic::decodeLocation(location
, file
, line
, column
, offset
);
233 SourceLocation Loc
= SourceLocation::getFromRawEncoding(location
.int_data
);
235 if (!location
.ptr_data
[0] || Loc
.isInvalid()) {
236 createNullLocation(file
, line
, column
, offset
);
240 const SourceManager
&SM
=
241 *static_cast<const SourceManager
*>(location
.ptr_data
[0]);
242 SourceLocation ExpansionLoc
= SM
.getExpansionLoc(Loc
);
244 // Check that the FileID is invalid on the expansion location.
245 // This can manifest in invalid code.
246 FileID fileID
= SM
.getFileID(ExpansionLoc
);
247 bool Invalid
= false;
248 const SrcMgr::SLocEntry
&sloc
= SM
.getSLocEntry(fileID
, &Invalid
);
249 if (Invalid
|| !sloc
.isFile()) {
250 createNullLocation(file
, line
, column
, offset
);
255 *file
= cxfile::makeCXFile(SM
.getFileEntryRefForID(fileID
));
257 *line
= SM
.getExpansionLineNumber(ExpansionLoc
);
259 *column
= SM
.getExpansionColumnNumber(ExpansionLoc
);
261 *offset
= SM
.getDecomposedLoc(ExpansionLoc
).second
;
264 void clang_getPresumedLocation(CXSourceLocation location
,
268 if (!isASTUnitSourceLocation(location
)) {
269 // Other SourceLocation implementations do not support presumed locations
271 createNullLocation(filename
, line
, column
);
275 SourceLocation Loc
= SourceLocation::getFromRawEncoding(location
.int_data
);
277 if (!location
.ptr_data
[0] || Loc
.isInvalid()) {
278 createNullLocation(filename
, line
, column
);
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
);
290 if (filename
) *filename
= cxstring::createRef(PreLoc
.getFilename());
291 if (line
) *line
= PreLoc
.getLine();
292 if (column
) *column
= PreLoc
.getColumn();
295 void clang_getInstantiationLocation(CXSourceLocation location
,
300 // Redirect to new API.
301 clang_getExpansionLocation(location
, file
, line
, column
, offset
);
304 void clang_getSpellingLocation(CXSourceLocation location
,
309 if (!isASTUnitSourceLocation(location
)) {
310 CXLoadedDiagnostic::decodeLocation(location
, file
, line
,
315 SourceLocation Loc
= SourceLocation::getFromRawEncoding(location
.int_data
);
317 if (!location
.ptr_data
[0] || Loc
.isInvalid())
318 return createNullLocation(file
, line
, column
, offset
);
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(SpellLoc
);
325 FileID FID
= LocInfo
.first
;
326 unsigned FileOffset
= LocInfo
.second
;
329 return createNullLocation(file
, line
, column
, offset
);
332 *file
= cxfile::makeCXFile(SM
.getFileEntryRefForID(FID
));
334 *line
= SM
.getLineNumber(FID
, FileOffset
);
336 *column
= SM
.getColumnNumber(FID
, FileOffset
);
338 *offset
= FileOffset
;
341 void clang_getFileLocation(CXSourceLocation location
,
346 if (!isASTUnitSourceLocation(location
)) {
347 CXLoadedDiagnostic::decodeLocation(location
, file
, line
,
352 SourceLocation Loc
= SourceLocation::getFromRawEncoding(location
.int_data
);
354 if (!location
.ptr_data
[0] || Loc
.isInvalid())
355 return createNullLocation(file
, line
, column
, offset
);
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(FileLoc
);
361 FileID FID
= LocInfo
.first
;
362 unsigned FileOffset
= LocInfo
.second
;
365 return createNullLocation(file
, line
, column
, offset
);
368 *file
= cxfile::makeCXFile(SM
.getFileEntryRefForID(FID
));
370 *line
= SM
.getLineNumber(FID
, FileOffset
);
372 *column
= SM
.getColumnNumber(FID
, FileOffset
);
374 *offset
= FileOffset
;