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