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