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