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