387 lines · cpp
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-exception6//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 25using namespace clang;26using namespace clang::cxindex;27 28//===----------------------------------------------------------------------===//29// Internal predicates on CXSourceLocations.30//===----------------------------------------------------------------------===//31 32static bool isASTUnitSourceLocation(const CXSourceLocation &L) {33 // If the lowest bit is clear then the first ptr_data entry is a SourceManager34 // 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 42CXSourceLocation clang_getNullLocation() {43 CXSourceLocation Result = { { nullptr, nullptr }, 0 };44 return Result;45}46 47unsigned 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 53unsigned 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< because61 // ordering is meaningful only if LHS and RHS have the same FileID.62 return SM.isBeforeInTranslationUnit(Loc1, Loc2);63}64 65CXSourceRange clang_getNullRange() {66 CXSourceRange Result = { { nullptr, nullptr }, 0, 0 };67 return Result;68}69 70CXSourceRange 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 88unsigned 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_data92 && range1.end_int_data == range2.end_int_data;93}94 95int clang_Range_isNull(CXSourceRange range) {96 return clang_equalRanges(range, clang_getNullRange());97}98 99 100CXSourceLocation 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 112CXSourceLocation 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 128CXSourceLocation 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 163CXSourceLocation 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, regardless186// of their origin.187//===----------------------------------------------------------------------===//188 189static 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 201static 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 213int 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 224int 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 235void 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 276void 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 locations282 // 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 307void 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 316void 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 FileIDAndOffset 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 352void 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 FileIDAndOffset 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