Home | History | Annotate | Line # | Download | only in libclang
      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