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 "CXFile.h" 17 #include "CXLoadedDiagnostic.h" 18 #include "CXString.h" 19 #include "CXTranslationUnit.h" 20 #include "clang/Basic/FileManager.h" 21 #include "clang/Frontend/ASTUnit.h" 22 #include "llvm/Support/Compiler.h" 23 #include "llvm/Support/Format.h" 24 25 using namespace clang; 26 using namespace clang::cxindex; 27 28 //===----------------------------------------------------------------------===// 29 // Internal predicates on CXSourceLocations. 30 //===----------------------------------------------------------------------===// 31 32 static bool isASTUnitSourceLocation(const CXSourceLocation &L) { 33 // If the lowest bit is clear then the first ptr_data entry is a SourceManager 34 // pointer, or the CXSourceLocation is a null location. 35 return ((uintptr_t)L.ptr_data[0] & 0x1) == 0; 36 } 37 38 //===----------------------------------------------------------------------===// 39 // Basic construction and comparison of CXSourceLocations and CXSourceRanges. 40 //===----------------------------------------------------------------------===// 41 42 CXSourceLocation clang_getNullLocation() { 43 CXSourceLocation Result = { { nullptr, nullptr }, 0 }; 44 return Result; 45 } 46 47 unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) { 48 return (loc1.ptr_data[0] == loc2.ptr_data[0] && 49 loc1.ptr_data[1] == loc2.ptr_data[1] && 50 loc1.int_data == loc2.int_data); 51 } 52 53 unsigned clang_isBeforeInTranslationUnit(CXSourceLocation loc1, 54 CXSourceLocation loc2) { 55 const SourceLocation Loc1 = SourceLocation::getFromRawEncoding(loc1.int_data); 56 const SourceLocation Loc2 = SourceLocation::getFromRawEncoding(loc2.int_data); 57 58 const SourceManager &SM = 59 *static_cast<const SourceManager *>(loc1.ptr_data[0]); 60 // Use the appropriate SourceManager method here rather than operator< because 61 // ordering is meaningful only if LHS and RHS have the same FileID. 62 return SM.isBeforeInTranslationUnit(Loc1, Loc2); 63 } 64 65 CXSourceRange clang_getNullRange() { 66 CXSourceRange Result = { { nullptr, nullptr }, 0, 0 }; 67 return Result; 68 } 69 70 CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { 71 if (!isASTUnitSourceLocation(begin)) { 72 if (isASTUnitSourceLocation(end)) 73 return clang_getNullRange(); 74 CXSourceRange Result = { { begin.ptr_data[0], end.ptr_data[0] }, 0, 0 }; 75 return Result; 76 } 77 78 if (begin.ptr_data[0] != end.ptr_data[0] || 79 begin.ptr_data[1] != end.ptr_data[1]) 80 return clang_getNullRange(); 81 82 CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] }, 83 begin.int_data, end.int_data }; 84 85 return Result; 86 } 87 88 unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) { 89 return range1.ptr_data[0] == range2.ptr_data[0] 90 && range1.ptr_data[1] == range2.ptr_data[1] 91 && range1.begin_int_data == range2.begin_int_data 92 && range1.end_int_data == range2.end_int_data; 93 } 94 95 int clang_Range_isNull(CXSourceRange range) { 96 return clang_equalRanges(range, clang_getNullRange()); 97 } 98 99 100 CXSourceLocation clang_getRangeStart(CXSourceRange range) { 101 // Special decoding for CXSourceLocations for CXLoadedDiagnostics. 102 if ((uintptr_t)range.ptr_data[0] & 0x1) { 103 CXSourceLocation Result = { { range.ptr_data[0], nullptr }, 0 }; 104 return Result; 105 } 106 107 CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, 108 range.begin_int_data }; 109 return Result; 110 } 111 112 CXSourceLocation clang_getRangeEnd(CXSourceRange range) { 113 // Special decoding for CXSourceLocations for CXLoadedDiagnostics. 114 if ((uintptr_t)range.ptr_data[0] & 0x1) { 115 CXSourceLocation Result = { { range.ptr_data[1], nullptr }, 0 }; 116 return Result; 117 } 118 119 CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, 120 range.end_int_data }; 121 return Result; 122 } 123 124 //===----------------------------------------------------------------------===// 125 // Getting CXSourceLocations and CXSourceRanges from a translation unit. 126 //===----------------------------------------------------------------------===// 127 128 CXSourceLocation clang_getLocation(CXTranslationUnit TU, 129 CXFile file, 130 unsigned line, 131 unsigned column) { 132 if (cxtu::isNotUsableTU(TU)) { 133 LOG_BAD_TU(TU); 134 return clang_getNullLocation(); 135 } 136 if (!file) 137 return clang_getNullLocation(); 138 if (line == 0 || column == 0) 139 return clang_getNullLocation(); 140 141 LogRef Log = Logger::make(__func__); 142 ASTUnit *CXXUnit = cxtu::getASTUnit(TU); 143 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 144 FileEntryRef File = *cxfile::getFileEntryRef(file); 145 SourceLocation SLoc = CXXUnit->getLocation(File, line, column); 146 if (SLoc.isInvalid()) { 147 if (Log) 148 *Log << llvm::format("(\"%s\", %d, %d) = invalid", 149 File.getName().str().c_str(), line, column); 150 return clang_getNullLocation(); 151 } 152 153 CXSourceLocation CXLoc = 154 cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); 155 if (Log) 156 *Log << llvm::format("(\"%s\", %d, %d) = ", File.getName().str().c_str(), 157 line, column) 158 << CXLoc; 159 160 return CXLoc; 161 } 162 163 CXSourceLocation clang_getLocationForOffset(CXTranslationUnit TU, 164 CXFile file, 165 unsigned offset) { 166 if (cxtu::isNotUsableTU(TU)) { 167 LOG_BAD_TU(TU); 168 return clang_getNullLocation(); 169 } 170 if (!file) 171 return clang_getNullLocation(); 172 173 ASTUnit *CXXUnit = cxtu::getASTUnit(TU); 174 175 SourceLocation SLoc 176 = CXXUnit->getLocation(*cxfile::getFileEntryRef(file), offset); 177 178 if (SLoc.isInvalid()) 179 return clang_getNullLocation(); 180 181 return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); 182 } 183 184 //===----------------------------------------------------------------------===// 185 // Routines for expanding and manipulating CXSourceLocations, regardless 186 // of their origin. 187 //===----------------------------------------------------------------------===// 188 189 static void createNullLocation(CXFile *file, unsigned *line, 190 unsigned *column, unsigned *offset) { 191 if (file) 192 *file = nullptr; 193 if (line) 194 *line = 0; 195 if (column) 196 *column = 0; 197 if (offset) 198 *offset = 0; 199 } 200 201 static void createNullLocation(CXString *filename, unsigned *line, 202 unsigned *column, unsigned *offset = nullptr) { 203 if (filename) 204 *filename = cxstring::createEmpty(); 205 if (line) 206 *line = 0; 207 if (column) 208 *column = 0; 209 if (offset) 210 *offset = 0; 211 } 212 213 int clang_Location_isInSystemHeader(CXSourceLocation location) { 214 const SourceLocation Loc = 215 SourceLocation::getFromRawEncoding(location.int_data); 216 if (Loc.isInvalid()) 217 return 0; 218 219 const SourceManager &SM = 220 *static_cast<const SourceManager*>(location.ptr_data[0]); 221 return SM.isInSystemHeader(Loc); 222 } 223 224 int clang_Location_isFromMainFile(CXSourceLocation location) { 225 const SourceLocation Loc = 226 SourceLocation::getFromRawEncoding(location.int_data); 227 if (Loc.isInvalid()) 228 return 0; 229 230 const SourceManager &SM = 231 *static_cast<const SourceManager*>(location.ptr_data[0]); 232 return SM.isWrittenInMainFile(Loc); 233 } 234 235 void clang_getExpansionLocation(CXSourceLocation location, 236 CXFile *file, 237 unsigned *line, 238 unsigned *column, 239 unsigned *offset) { 240 if (!isASTUnitSourceLocation(location)) { 241 CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); 242 return; 243 } 244 245 SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); 246 247 if (!location.ptr_data[0] || Loc.isInvalid()) { 248 createNullLocation(file, line, column, offset); 249 return; 250 } 251 252 const SourceManager &SM = 253 *static_cast<const SourceManager*>(location.ptr_data[0]); 254 SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); 255 256 // Check that the FileID is invalid on the expansion location. 257 // This can manifest in invalid code. 258 FileID fileID = SM.getFileID(ExpansionLoc); 259 bool Invalid = false; 260 const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); 261 if (Invalid || !sloc.isFile()) { 262 createNullLocation(file, line, column, offset); 263 return; 264 } 265 266 if (file) 267 *file = cxfile::makeCXFile(SM.getFileEntryRefForID(fileID)); 268 if (line) 269 *line = SM.getExpansionLineNumber(ExpansionLoc); 270 if (column) 271 *column = SM.getExpansionColumnNumber(ExpansionLoc); 272 if (offset) 273 *offset = SM.getDecomposedLoc(ExpansionLoc).second; 274 } 275 276 void clang_getPresumedLocation(CXSourceLocation location, 277 CXString *filename, 278 unsigned *line, 279 unsigned *column) { 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 307 void 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 316 void clang_getSpellingLocation(CXSourceLocation location, 317 CXFile *file, 318 unsigned *line, 319 unsigned *column, 320 unsigned *offset) { 321 if (!isASTUnitSourceLocation(location)) { 322 CXLoadedDiagnostic::decodeLocation(location, file, line, 323 column, offset); 324 return; 325 } 326 327 SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); 328 329 if (!location.ptr_data[0] || Loc.isInvalid()) 330 return createNullLocation(file, line, column, offset); 331 332 const SourceManager &SM = 333 *static_cast<const SourceManager*>(location.ptr_data[0]); 334 SourceLocation SpellLoc = SM.getSpellingLoc(Loc); 335 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc); 336 FileID FID = LocInfo.first; 337 unsigned FileOffset = LocInfo.second; 338 339 if (FID.isInvalid()) 340 return createNullLocation(file, line, column, offset); 341 342 if (file) 343 *file = cxfile::makeCXFile(SM.getFileEntryRefForID(FID)); 344 if (line) 345 *line = SM.getLineNumber(FID, FileOffset); 346 if (column) 347 *column = SM.getColumnNumber(FID, FileOffset); 348 if (offset) 349 *offset = FileOffset; 350 } 351 352 void clang_getFileLocation(CXSourceLocation location, 353 CXFile *file, 354 unsigned *line, 355 unsigned *column, 356 unsigned *offset) { 357 if (!isASTUnitSourceLocation(location)) { 358 CXLoadedDiagnostic::decodeLocation(location, file, line, 359 column, offset); 360 return; 361 } 362 363 SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); 364 365 if (!location.ptr_data[0] || Loc.isInvalid()) 366 return createNullLocation(file, line, column, offset); 367 368 const SourceManager &SM = 369 *static_cast<const SourceManager*>(location.ptr_data[0]); 370 SourceLocation FileLoc = SM.getFileLoc(Loc); 371 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(FileLoc); 372 FileID FID = LocInfo.first; 373 unsigned FileOffset = LocInfo.second; 374 375 if (FID.isInvalid()) 376 return createNullLocation(file, line, column, offset); 377 378 if (file) 379 *file = cxfile::makeCXFile(SM.getFileEntryRefForID(FID)); 380 if (line) 381 *line = SM.getLineNumber(FID, FileOffset); 382 if (column) 383 *column = SM.getColumnNumber(FID, FileOffset); 384 if (offset) 385 *offset = FileOffset; 386 } 387