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