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 
16 #include "CIndexer.h"
17 #include "CXString.h"
18 #include "CXSourceLocation.h"
19 #include "CXTranslationUnit.h"
20 
21 using namespace clang;
22 using namespace clang::cxstring;
23 
24 //===----------------------------------------------------------------------===//
25 // Internal predicates on CXSourceLocations.
26 //===----------------------------------------------------------------------===//
27 
28 static bool isASTUnitSourceLocation(const CXSourceLocation &L) {
29   // If the lowest bit is clear then the first ptr_data entry is a SourceManager
30   // pointer, or the CXSourceLocation is a null location.
31   return ((uintptr_t)L.ptr_data[0] & 0x1) == 0;
32 }
33 
34 //===----------------------------------------------------------------------===//
35 // Basic construction and comparison of CXSourceLocations and CXSourceRanges.
36 //===----------------------------------------------------------------------===//
37 
38 extern "C" {
39 
40 CXSourceLocation clang_getNullLocation() {
41   CXSourceLocation Result = { { 0, 0 }, 0 };
42   return Result;
43 }
44 
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 
51 CXSourceRange clang_getNullRange() {
52   CXSourceRange Result = { { 0, 0 }, 0, 0 };
53   return Result;
54 }
55 
56 CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) {
57   if (begin.ptr_data[0] != end.ptr_data[0] ||
58       begin.ptr_data[1] != end.ptr_data[1])
59     return clang_getNullRange();
60 
61   CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] },
62                            begin.int_data, end.int_data };
63 
64   return Result;
65 }
66 
67 
68 unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) {
69   return range1.ptr_data[0] == range2.ptr_data[0]
70     && range1.ptr_data[1] == range2.ptr_data[1]
71     && range1.begin_int_data == range2.begin_int_data
72     && range1.end_int_data == range2.end_int_data;
73 }
74 
75 int clang_Range_isNull(CXSourceRange range) {
76   return clang_equalRanges(range, clang_getNullRange());
77 }
78 
79 
80 CXSourceLocation clang_getRangeStart(CXSourceRange range) {
81   CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] },
82     range.begin_int_data };
83   return Result;
84 }
85 
86 CXSourceLocation clang_getRangeEnd(CXSourceRange range) {
87   CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] },
88     range.end_int_data };
89   return Result;
90 }
91 
92 } // end extern "C"
93 
94 //===----------------------------------------------------------------------===//
95 //  Getting CXSourceLocations and CXSourceRanges from a translation unit.
96 //===----------------------------------------------------------------------===//
97 
98 extern "C" {
99 
100 CXSourceLocation clang_getLocation(CXTranslationUnit tu,
101                                    CXFile file,
102                                    unsigned line,
103                                    unsigned column) {
104   if (!tu || !file)
105     return clang_getNullLocation();
106 
107   bool Logging = ::getenv("LIBCLANG_LOGGING");
108   ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData);
109   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
110   const FileEntry *File = static_cast<const FileEntry *>(file);
111   SourceLocation SLoc = CXXUnit->getLocation(File, line, column);
112   if (SLoc.isInvalid()) {
113     if (Logging)
114       llvm::errs() << "clang_getLocation(\"" << File->getName()
115       << "\", " << line << ", " << column << ") = invalid\n";
116     return clang_getNullLocation();
117   }
118 
119   if (Logging)
120     llvm::errs() << "clang_getLocation(\"" << File->getName()
121     << "\", " << line << ", " << column << ") = "
122     << SLoc.getRawEncoding() << "\n";
123 
124   return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc);
125 }
126 
127 CXSourceLocation clang_getLocationForOffset(CXTranslationUnit tu,
128                                             CXFile file,
129                                             unsigned offset) {
130   if (!tu || !file)
131     return clang_getNullLocation();
132 
133   ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData);
134 
135   SourceLocation SLoc
136     = CXXUnit->getLocation(static_cast<const FileEntry *>(file), offset);
137 
138   if (SLoc.isInvalid())
139     return clang_getNullLocation();
140 
141   return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc);
142 }
143 
144 } // end extern "C"
145 
146 //===----------------------------------------------------------------------===//
147 // Routines for expanding and manipulating CXSourceLocations, regardless
148 // of their origin.
149 //===----------------------------------------------------------------------===//
150 
151 static void createNullLocation(CXFile *file, unsigned *line,
152                                unsigned *column, unsigned *offset) {
153   if (file)
154     *file = 0;
155   if (line)
156     *line = 0;
157   if (column)
158     *column = 0;
159   if (offset)
160     *offset = 0;
161   return;
162 }
163 
164 static void createNullLocation(CXString *filename, unsigned *line,
165                                unsigned *column, unsigned *offset = 0) {
166   if (filename)
167     *filename = createCXString("");
168   if (line)
169     *line = 0;
170   if (column)
171     *column = 0;
172   if (offset)
173     *offset = 0;
174   return;
175 }
176 
177 extern "C" {
178 
179 void clang_getExpansionLocation(CXSourceLocation location,
180                                 CXFile *file,
181                                 unsigned *line,
182                                 unsigned *column,
183                                 unsigned *offset) {
184 
185   if (isASTUnitSourceLocation(location)) {
186     SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
187 
188     if (!location.ptr_data[0] || Loc.isInvalid()) {
189       createNullLocation(file, line, column, offset);
190       return;
191     }
192 
193     const SourceManager &SM =
194     *static_cast<const SourceManager*>(location.ptr_data[0]);
195     SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
196 
197     // Check that the FileID is invalid on the expansion location.
198     // This can manifest in invalid code.
199     FileID fileID = SM.getFileID(ExpansionLoc);
200     bool Invalid = false;
201     const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid);
202     if (Invalid || !sloc.isFile()) {
203       createNullLocation(file, line, column, offset);
204       return;
205     }
206 
207     if (file)
208       *file = (void *)SM.getFileEntryForSLocEntry(sloc);
209     if (line)
210       *line = SM.getExpansionLineNumber(ExpansionLoc);
211     if (column)
212       *column = SM.getExpansionColumnNumber(ExpansionLoc);
213     if (offset)
214       *offset = SM.getDecomposedLoc(ExpansionLoc).second;
215     return;
216   }
217 
218   // FIXME:
219   createNullLocation(file, line, column, offset);
220 }
221 
222 void clang_getPresumedLocation(CXSourceLocation location,
223                                CXString *filename,
224                                unsigned *line,
225                                unsigned *column) {
226 
227   if (isASTUnitSourceLocation(location)) {
228     SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
229 
230     if (!location.ptr_data[0] || Loc.isInvalid())
231       createNullLocation(filename, line, column);
232     else {
233       const SourceManager &SM =
234       *static_cast<const SourceManager*>(location.ptr_data[0]);
235       PresumedLoc PreLoc = SM.getPresumedLoc(Loc);
236 
237       if (filename)
238         *filename = createCXString(PreLoc.getFilename());
239       if (line)
240         *line = PreLoc.getLine();
241       if (column)
242         *column = PreLoc.getColumn();
243     }
244     return;
245   }
246 
247   // FIXME:
248   createNullLocation(filename, line, column);
249 }
250 
251 void clang_getInstantiationLocation(CXSourceLocation location,
252                                     CXFile *file,
253                                     unsigned *line,
254                                     unsigned *column,
255                                     unsigned *offset) {
256   // Redirect to new API.
257   clang_getExpansionLocation(location, file, line, column, offset);
258 }
259 
260 void clang_getSpellingLocation(CXSourceLocation location,
261                                CXFile *file,
262                                unsigned *line,
263                                unsigned *column,
264                                unsigned *offset) {
265 
266   if (isASTUnitSourceLocation(location)) {
267     SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
268 
269     if (!location.ptr_data[0] || Loc.isInvalid())
270       return createNullLocation(file, line, column, offset);
271 
272     const SourceManager &SM =
273     *static_cast<const SourceManager*>(location.ptr_data[0]);
274     SourceLocation SpellLoc = Loc;
275     if (SpellLoc.isMacroID()) {
276       SourceLocation SimpleSpellingLoc = SM.getImmediateSpellingLoc(SpellLoc);
277       if (SimpleSpellingLoc.isFileID() &&
278           SM.getFileEntryForID(SM.getDecomposedLoc(SimpleSpellingLoc).first))
279         SpellLoc = SimpleSpellingLoc;
280       else
281         SpellLoc = SM.getExpansionLoc(SpellLoc);
282     }
283 
284     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc);
285     FileID FID = LocInfo.first;
286     unsigned FileOffset = LocInfo.second;
287 
288     if (FID.isInvalid())
289       return createNullLocation(file, line, column, offset);
290 
291     if (file)
292       *file = (void *)SM.getFileEntryForID(FID);
293     if (line)
294       *line = SM.getLineNumber(FID, FileOffset);
295     if (column)
296       *column = SM.getColumnNumber(FID, FileOffset);
297     if (offset)
298       *offset = FileOffset;
299     return;
300   }
301 
302   // FIXME:
303   createNullLocation(file, line, column, offset);
304 }
305 
306 } // end extern "C"
307 
308