CXSourceLocation.cpp   [plain text]


//===- CXSourceLocation.cpp - CXSourceLocations APIs ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines routines for manipulating CXSourceLocations.
//
//===----------------------------------------------------------------------===//

#include "clang/Frontend/ASTUnit.h"

#include "CIndexer.h"
#include "CXString.h"
#include "CXSourceLocation.h"
#include "CXTranslationUnit.h"
#include "CXLoadedDiagnostic.h"

using namespace clang;
using namespace clang::cxstring;

//===----------------------------------------------------------------------===//
// Internal predicates on CXSourceLocations.
//===----------------------------------------------------------------------===//

static bool isASTUnitSourceLocation(const CXSourceLocation &L) {
  // If the lowest bit is clear then the first ptr_data entry is a SourceManager
  // pointer, or the CXSourceLocation is a null location.
  return ((uintptr_t)L.ptr_data[0] & 0x1) == 0;
}

//===----------------------------------------------------------------------===//
// Basic construction and comparison of CXSourceLocations and CXSourceRanges.
//===----------------------------------------------------------------------===//

extern "C" {
  
CXSourceLocation clang_getNullLocation() {
  CXSourceLocation Result = { { 0, 0 }, 0 };
  return Result;
}

unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) {
  return (loc1.ptr_data[0] == loc2.ptr_data[0] &&
          loc1.ptr_data[1] == loc2.ptr_data[1] &&
          loc1.int_data == loc2.int_data);
}

CXSourceRange clang_getNullRange() {
  CXSourceRange Result = { { 0, 0 }, 0, 0 };
  return Result;
}

CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) {
  if (!isASTUnitSourceLocation(begin)) {
    if (isASTUnitSourceLocation(end))
      return clang_getNullRange();
    CXSourceRange Result = { { begin.ptr_data[0], end.ptr_data[0] }, 0, 0 };
    return Result;
  }
  
  if (begin.ptr_data[0] != end.ptr_data[0] ||
      begin.ptr_data[1] != end.ptr_data[1])
    return clang_getNullRange();
  
  CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] },
                           begin.int_data, end.int_data };

  return Result;
}

unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) {
  return range1.ptr_data[0] == range2.ptr_data[0]
    && range1.ptr_data[1] == range2.ptr_data[1]
    && range1.begin_int_data == range2.begin_int_data
    && range1.end_int_data == range2.end_int_data;
}

int clang_Range_isNull(CXSourceRange range) {
  return clang_equalRanges(range, clang_getNullRange());
}
  
  
CXSourceLocation clang_getRangeStart(CXSourceRange range) {
  // Special decoding for CXSourceLocations for CXLoadedDiagnostics.
  if ((uintptr_t)range.ptr_data[0] & 0x1) {
    CXSourceLocation Result = { { range.ptr_data[0], 0 }, 0 };
    return Result;    
  }
  
  CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] },
    range.begin_int_data };
  return Result;
}

CXSourceLocation clang_getRangeEnd(CXSourceRange range) {
  // Special decoding for CXSourceLocations for CXLoadedDiagnostics.
  if ((uintptr_t)range.ptr_data[0] & 0x1) {
    CXSourceLocation Result = { { range.ptr_data[1], 0 }, 0 };
    return Result;    
  }

  CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] },
    range.end_int_data };
  return Result;
}

} // end extern "C"

//===----------------------------------------------------------------------===//
//  Getting CXSourceLocations and CXSourceRanges from a translation unit.
//===----------------------------------------------------------------------===//

extern "C" {
  
CXSourceLocation clang_getLocation(CXTranslationUnit tu,
                                   CXFile file,
                                   unsigned line,
                                   unsigned column) {
  if (!tu || !file)
    return clang_getNullLocation();
  
  bool Logging = ::getenv("LIBCLANG_LOGGING");
  ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData);
  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
  const FileEntry *File = static_cast<const FileEntry *>(file);
  SourceLocation SLoc = CXXUnit->getLocation(File, line, column);
  if (SLoc.isInvalid()) {
    if (Logging)
      llvm::errs() << "clang_getLocation(\"" << File->getName() 
      << "\", " << line << ", " << column << ") = invalid\n";
    return clang_getNullLocation();
  }
  
  if (Logging)
    llvm::errs() << "clang_getLocation(\"" << File->getName() 
    << "\", " << line << ", " << column << ") = " 
    << SLoc.getRawEncoding() << "\n";
  
  return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc);
}
  
CXSourceLocation clang_getLocationForOffset(CXTranslationUnit tu,
                                            CXFile file,
                                            unsigned offset) {
  if (!tu || !file)
    return clang_getNullLocation();
  
  ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData);

  SourceLocation SLoc 
    = CXXUnit->getLocation(static_cast<const FileEntry *>(file), offset);

  if (SLoc.isInvalid())
    return clang_getNullLocation();
  
  return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc);
}

} // end extern "C"

//===----------------------------------------------------------------------===//
// Routines for expanding and manipulating CXSourceLocations, regardless
// of their origin.
//===----------------------------------------------------------------------===//

static void createNullLocation(CXFile *file, unsigned *line,
                               unsigned *column, unsigned *offset) {
  if (file)
    *file = 0;
  if (line)
    *line = 0;
  if (column)
    *column = 0;
  if (offset)
    *offset = 0;
  return;
}

static void createNullLocation(CXString *filename, unsigned *line,
                               unsigned *column, unsigned *offset = 0) {
  if (filename)
    *filename = createCXString("");
  if (line)
    *line = 0;
  if (column)
    *column = 0;
  if (offset)
    *offset = 0;
  return;
}

extern "C" {

void clang_getExpansionLocation(CXSourceLocation location,
                                CXFile *file,
                                unsigned *line,
                                unsigned *column,
                                unsigned *offset) {
  
  if (!isASTUnitSourceLocation(location)) {
    CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset);
    return;
  }

  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);

  if (!location.ptr_data[0] || Loc.isInvalid()) {
    createNullLocation(file, line, column, offset);
    return;
  }

  const SourceManager &SM =
  *static_cast<const SourceManager*>(location.ptr_data[0]);
  SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
  
  // Check that the FileID is invalid on the expansion location.
  // This can manifest in invalid code.
  FileID fileID = SM.getFileID(ExpansionLoc);
  bool Invalid = false;
  const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid);
  if (Invalid || !sloc.isFile()) {
    createNullLocation(file, line, column, offset);
    return;
  }
  
  if (file)
    *file = (void *)SM.getFileEntryForSLocEntry(sloc);
  if (line)
    *line = SM.getExpansionLineNumber(ExpansionLoc);
  if (column)
    *column = SM.getExpansionColumnNumber(ExpansionLoc);
  if (offset)
    *offset = SM.getDecomposedLoc(ExpansionLoc).second;
}

void clang_getPresumedLocation(CXSourceLocation location,
                               CXString *filename,
                               unsigned *line,
                               unsigned *column) {
  
  if (!isASTUnitSourceLocation(location)) {
    // Other SourceLocation implementations do not support presumed locations
    // at this time.
    createNullLocation(filename, line, column);
    return;
  }

  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);

  if (!location.ptr_data[0] || Loc.isInvalid())
    createNullLocation(filename, line, column);
  else {
    const SourceManager &SM =
    *static_cast<const SourceManager*>(location.ptr_data[0]);
    PresumedLoc PreLoc = SM.getPresumedLoc(Loc);
    
    if (filename)
      *filename = createCXString(PreLoc.getFilename());
    if (line)
      *line = PreLoc.getLine();
    if (column)
      *column = PreLoc.getColumn();
  }
}

void clang_getInstantiationLocation(CXSourceLocation location,
                                    CXFile *file,
                                    unsigned *line,
                                    unsigned *column,
                                    unsigned *offset) {
  // Redirect to new API.
  clang_getExpansionLocation(location, file, line, column, offset);
}

void clang_getSpellingLocation(CXSourceLocation location,
                               CXFile *file,
                               unsigned *line,
                               unsigned *column,
                               unsigned *offset) {
  
  if (!isASTUnitSourceLocation(location)) {
    CXLoadedDiagnostic::decodeLocation(location, file, line,
                                           column, offset);
    return;
  }
  
  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
  
  if (!location.ptr_data[0] || Loc.isInvalid())
    return createNullLocation(file, line, column, offset);
  
  const SourceManager &SM =
  *static_cast<const SourceManager*>(location.ptr_data[0]);
  SourceLocation SpellLoc = Loc;
  if (SpellLoc.isMacroID()) {
    SourceLocation SimpleSpellingLoc = SM.getImmediateSpellingLoc(SpellLoc);
    if (SimpleSpellingLoc.isFileID() &&
        SM.getFileEntryForID(SM.getDecomposedLoc(SimpleSpellingLoc).first))
      SpellLoc = SimpleSpellingLoc;
    else
      SpellLoc = SM.getExpansionLoc(SpellLoc);
  }
  
  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc);
  FileID FID = LocInfo.first;
  unsigned FileOffset = LocInfo.second;
  
  if (FID.isInvalid())
    return createNullLocation(file, line, column, offset);
  
  if (file)
    *file = (void *)SM.getFileEntryForID(FID);
  if (line)
    *line = SM.getLineNumber(FID, FileOffset);
  if (column)
    *column = SM.getColumnNumber(FID, FileOffset);
  if (offset)
    *offset = FileOffset;
}

} // end extern "C"