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
24using namespace clang;
25using namespace clang::cxindex;
26
27//===----------------------------------------------------------------------===//
28// Internal predicates on CXSourceLocations.
29//===----------------------------------------------------------------------===//
30
31static 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
41extern "C" {
42
43CXSourceLocation clang_getNullLocation() {
44  CXSourceLocation Result = { { nullptr, nullptr }, 0 };
45  return Result;
46}
47
48unsigned 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
54CXSourceRange clang_getNullRange() {
55  CXSourceRange Result = { { nullptr, nullptr }, 0, 0 };
56  return Result;
57}
58
59CXSourceRange 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
77unsigned 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
84int clang_Range_isNull(CXSourceRange range) {
85  return clang_equalRanges(range, clang_getNullRange());
86}
87
88
89CXSourceLocation 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
101CXSourceLocation 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
119extern "C" {
120
121CXSourceLocation 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(LLVM_FUNCTION_NAME);
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(), 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(), line, column)
150         << CXLoc;
151
152  return CXLoc;
153}
154
155CXSourceLocation clang_getLocationForOffset(CXTranslationUnit TU,
156                                            CXFile file,
157                                            unsigned offset) {
158  if (cxtu::isNotUsableTU(TU)) {
159    LOG_BAD_TU(TU);
160    return clang_getNullLocation();
161  }
162  if (!file)
163    return clang_getNullLocation();
164
165  ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
166
167  SourceLocation SLoc
168    = CXXUnit->getLocation(static_cast<const FileEntry *>(file), offset);
169
170  if (SLoc.isInvalid())
171    return clang_getNullLocation();
172
173  return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc);
174}
175
176} // end extern "C"
177
178//===----------------------------------------------------------------------===//
179// Routines for expanding and manipulating CXSourceLocations, regardless
180// of their origin.
181//===----------------------------------------------------------------------===//
182
183static void createNullLocation(CXFile *file, unsigned *line,
184                               unsigned *column, unsigned *offset) {
185  if (file)
186    *file = nullptr;
187  if (line)
188    *line = 0;
189  if (column)
190    *column = 0;
191  if (offset)
192    *offset = 0;
193  return;
194}
195
196static 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  return;
207}
208
209extern "C" {
210
211int clang_Location_isInSystemHeader(CXSourceLocation location) {
212  const SourceLocation Loc =
213    SourceLocation::getFromRawEncoding(location.int_data);
214  if (Loc.isInvalid())
215    return 0;
216
217  const SourceManager &SM =
218    *static_cast<const SourceManager*>(location.ptr_data[0]);
219  return SM.isInSystemHeader(Loc);
220}
221
222int clang_Location_isFromMainFile(CXSourceLocation location) {
223  const SourceLocation Loc =
224    SourceLocation::getFromRawEncoding(location.int_data);
225  if (Loc.isInvalid())
226    return 0;
227
228  const SourceManager &SM =
229    *static_cast<const SourceManager*>(location.ptr_data[0]);
230  return SM.isWrittenInMainFile(Loc);
231}
232
233void clang_getExpansionLocation(CXSourceLocation location,
234                                CXFile *file,
235                                unsigned *line,
236                                unsigned *column,
237                                unsigned *offset) {
238
239  if (!isASTUnitSourceLocation(location)) {
240    CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset);
241    return;
242  }
243
244  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
245
246  if (!location.ptr_data[0] || Loc.isInvalid()) {
247    createNullLocation(file, line, column, offset);
248    return;
249  }
250
251  const SourceManager &SM =
252  *static_cast<const SourceManager*>(location.ptr_data[0]);
253  SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
254
255  // Check that the FileID is invalid on the expansion location.
256  // This can manifest in invalid code.
257  FileID fileID = SM.getFileID(ExpansionLoc);
258  bool Invalid = false;
259  const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid);
260  if (Invalid || !sloc.isFile()) {
261    createNullLocation(file, line, column, offset);
262    return;
263  }
264
265  if (file)
266    *file = const_cast<FileEntry *>(SM.getFileEntryForSLocEntry(sloc));
267  if (line)
268    *line = SM.getExpansionLineNumber(ExpansionLoc);
269  if (column)
270    *column = SM.getExpansionColumnNumber(ExpansionLoc);
271  if (offset)
272    *offset = SM.getDecomposedLoc(ExpansionLoc).second;
273}
274
275void clang_getPresumedLocation(CXSourceLocation location,
276                               CXString *filename,
277                               unsigned *line,
278                               unsigned *column) {
279
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
307void 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
316void clang_getSpellingLocation(CXSourceLocation location,
317                               CXFile *file,
318                               unsigned *line,
319                               unsigned *column,
320                               unsigned *offset) {
321
322  if (!isASTUnitSourceLocation(location)) {
323    CXLoadedDiagnostic::decodeLocation(location, file, line,
324                                           column, offset);
325    return;
326  }
327
328  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
329
330  if (!location.ptr_data[0] || Loc.isInvalid())
331    return createNullLocation(file, line, column, offset);
332
333  const SourceManager &SM =
334  *static_cast<const SourceManager*>(location.ptr_data[0]);
335  // FIXME: This should call SourceManager::getSpellingLoc().
336  SourceLocation SpellLoc = SM.getFileLoc(Loc);
337  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc);
338  FileID FID = LocInfo.first;
339  unsigned FileOffset = LocInfo.second;
340
341  if (FID.isInvalid())
342    return createNullLocation(file, line, column, offset);
343
344  if (file)
345    *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID));
346  if (line)
347    *line = SM.getLineNumber(FID, FileOffset);
348  if (column)
349    *column = SM.getColumnNumber(FID, FileOffset);
350  if (offset)
351    *offset = FileOffset;
352}
353
354void clang_getFileLocation(CXSourceLocation location,
355                           CXFile *file,
356                           unsigned *line,
357                           unsigned *column,
358                           unsigned *offset) {
359
360  if (!isASTUnitSourceLocation(location)) {
361    CXLoadedDiagnostic::decodeLocation(location, file, line,
362                                           column, offset);
363    return;
364  }
365
366  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
367
368  if (!location.ptr_data[0] || Loc.isInvalid())
369    return createNullLocation(file, line, column, offset);
370
371  const SourceManager &SM =
372  *static_cast<const SourceManager*>(location.ptr_data[0]);
373  SourceLocation FileLoc = SM.getFileLoc(Loc);
374  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(FileLoc);
375  FileID FID = LocInfo.first;
376  unsigned FileOffset = LocInfo.second;
377
378  if (FID.isInvalid())
379    return createNullLocation(file, line, column, offset);
380
381  if (file)
382    *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID));
383  if (line)
384    *line = SM.getLineNumber(FID, FileOffset);
385  if (column)
386    *column = SM.getColumnNumber(FID, FileOffset);
387  if (offset)
388    *offset = FileOffset;
389}
390
391} // end extern "C"
392