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 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 CXSourceLocation clang_getNullLocation() { 42 CXSourceLocation Result = { { nullptr, nullptr }, 0 }; 43 return Result; 44 } 45 46 unsigned 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 52 CXSourceRange clang_getNullRange() { 53 CXSourceRange Result = { { nullptr, nullptr }, 0, 0 }; 54 return Result; 55 } 56 57 CXSourceRange 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 75 unsigned 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 82 int clang_Range_isNull(CXSourceRange range) { 83 return clang_equalRanges(range, clang_getNullRange()); 84 } 85 86 87 CXSourceLocation 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 99 CXSourceLocation 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 115 CXSourceLocation 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 150 CXSourceLocation 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 176 static 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 188 static 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 200 int 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 211 int 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 222 void 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 263 void 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 294 void 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 303 void 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 340 void 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