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