//===--- SourceLoc.cpp - SourceLoc and SourceRange implementations --------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/Basic/SourceLoc.h" #include "swift/Basic/SourceManager.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" using namespace swift; SourceLoc SourceManager::getCodeCompletionLoc() const { return getLocForBufferStart(CodeCompletionBufferID) .getAdvancedLoc(CodeCompletionOffset); } unsigned SourceManager::addNewSourceBuffer(llvm::MemoryBuffer *Buffer) { assert(Buffer); auto ID = LLVMSourceMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); BufIdentIDMap[Buffer->getBufferIdentifier()] = ID; return ID; } unsigned SourceManager::addMemBufferCopy(llvm::MemoryBuffer *Buffer) { return addMemBufferCopy(Buffer->getBuffer(), Buffer->getBufferIdentifier()); } unsigned SourceManager::addMemBufferCopy(StringRef InputData, StringRef BufIdentifier) { auto Buffer = llvm::MemoryBuffer::getMemBufferCopy(InputData, BufIdentifier); return addNewSourceBuffer(Buffer); } void SourceManager::openVirtualFile(SourceLoc loc, StringRef name, int lineOffset) { assert(!getVirtualFile(loc) && "must call closeVirtualFile first"); CharSourceRange fullRange = getRangeForBuffer(findBufferContainingLoc(loc)); SourceLoc end; auto nextRangeIter = VirtualFiles.upper_bound(loc.Value.getPointer()); if (nextRangeIter != VirtualFiles.end() && fullRange.contains(nextRangeIter->second.Range.getStart())) { end = nextRangeIter->second.Range.getStart(); } else { end = fullRange.getEnd(); } CharSourceRange range = CharSourceRange(*this, loc, end); VirtualFiles[end.Value.getPointer()] = { range, name, lineOffset }; } void SourceManager::closeVirtualFile(SourceLoc end) { auto *virtualFile = const_cast(getVirtualFile(end)); assert(virtualFile && "no open virtual file for this location"); CachedVFile = {}; CharSourceRange oldRange = virtualFile->Range; virtualFile->Range = CharSourceRange(*this, virtualFile->Range.getStart(), end); VirtualFiles[end.Value.getPointer()] = std::move(*virtualFile); bool existed = VirtualFiles.erase(oldRange.getEnd().Value.getPointer()); assert(existed); (void)existed; } const SourceManager::VirtualFile * SourceManager::getVirtualFile(SourceLoc Loc) const { const char *p = Loc.Value.getPointer(); if (CachedVFile.first == p) return CachedVFile.second; // Returns the first element that is >p. auto VFileIt = VirtualFiles.upper_bound(p); if (VFileIt != VirtualFiles.end() && VFileIt->second.Range.contains(Loc)) { CachedVFile = { p, &VFileIt->second }; return CachedVFile.second; } return nullptr; } Optional SourceManager::getIDForBufferIdentifier( StringRef BufIdentifier) { auto It = BufIdentIDMap.find(BufIdentifier); if (It == BufIdentIDMap.end()) return Nothing; return It->second; } const char *SourceManager::getIdentifierForBuffer(unsigned bufferID) const { auto *buffer = LLVMSourceMgr.getMemoryBuffer(bufferID); assert(buffer && "invalid buffer ID"); return buffer->getBufferIdentifier(); } CharSourceRange SourceManager::getRangeForBuffer(unsigned bufferID) const { auto *buffer = LLVMSourceMgr.getMemoryBuffer(bufferID); SourceLoc start{llvm::SMLoc::getFromPointer(buffer->getBufferStart())}; return CharSourceRange(start, buffer->getBufferSize()); } unsigned SourceManager::getLocOffsetInBuffer(SourceLoc Loc, unsigned BufferID) const { assert(Loc.isValid() && "location should be valid"); auto *Buffer = LLVMSourceMgr.getMemoryBuffer(BufferID); assert(Loc.Value.getPointer() >= Buffer->getBuffer().begin() && Loc.Value.getPointer() <= Buffer->getBuffer().end() && "Location is not from the specified buffer"); return Loc.Value.getPointer() - Buffer->getBuffer().begin(); } unsigned SourceManager::getByteDistance(SourceLoc Start, SourceLoc End) const { assert(Start.isValid() && "start location should be valid"); assert(End.isValid() && "end location should be valid"); #ifndef NDEBUG unsigned BufferID = findBufferContainingLoc(Start); auto *Buffer = LLVMSourceMgr.getMemoryBuffer(BufferID); assert(End.Value.getPointer() >= Buffer->getBuffer().begin() && End.Value.getPointer() <= Buffer->getBuffer().end() && "End location is not from the same buffer"); #endif // When we have a rope buffer, could be implemented in terms of // getLocOffsetInBuffer(). return End.Value.getPointer() - Start.Value.getPointer(); } StringRef SourceManager::extractText(CharSourceRange Range, Optional BufferID) const { assert(Range.isValid() && "range should be valid"); if (!BufferID) BufferID = findBufferContainingLoc(Range.getStart()); StringRef Buffer = LLVMSourceMgr.getMemoryBuffer(*BufferID)->getBuffer(); return Buffer.substr(getLocOffsetInBuffer(Range.getStart(), *BufferID), Range.getByteLength()); } unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const { assert(Loc.isValid()); // Search the buffers back-to front, so later alias buffers are // visited first. auto less_equal = std::less_equal(); for (unsigned i = LLVMSourceMgr.getNumBuffers(), e = 1; i >= e; --i) { auto Buf = LLVMSourceMgr.getMemoryBuffer(i); if (less_equal(Buf->getBufferStart(), Loc.Value.getPointer()) && // Use <= here so that a pointer to the null at the end of the buffer // is included as part of the buffer. less_equal(Loc.Value.getPointer(), Buf->getBufferEnd())) return i; } llvm_unreachable("no buffer containing location found"); } void SourceLoc::printLineAndColumn(raw_ostream &OS, const SourceManager &SM) const { if (isInvalid()) { OS << ""; return; } auto LineAndCol = SM.getLineAndColumn(*this); OS << "line:" << LineAndCol.first << ':' << LineAndCol.second; } void SourceLoc::print(raw_ostream &OS, const SourceManager &SM, unsigned &LastBufferID) const { if (isInvalid()) { OS << ""; return; } unsigned BufferID = SM.findBufferContainingLoc(*this); if (BufferID != LastBufferID) { OS << SM.getIdentifierForBuffer(BufferID); LastBufferID = BufferID; } else { OS << "line"; } auto LineAndCol = SM.getLineAndColumn(*this, BufferID); OS << ':' << LineAndCol.first << ':' << LineAndCol.second; } void SourceLoc::dump(const SourceManager &SM) const { print(llvm::errs(), SM); } void SourceRange::print(raw_ostream &OS, const SourceManager &SM, unsigned &LastBufferID, bool PrintText) const { OS << '['; Start.print(OS, SM, LastBufferID); OS << " - "; End.print(OS, SM, LastBufferID); OS << ']'; if (Start.isInvalid() || End.isInvalid()) return; if (PrintText) { OS << " RangeText=\"" << StringRef(Start.Value.getPointer(), End.Value.getPointer() - Start.Value.getPointer()+1) << '"'; } } void SourceRange::dump(const SourceManager &SM) const { print(llvm::errs(), SM); } CharSourceRange::CharSourceRange(const SourceManager &SM, SourceLoc Start, SourceLoc End) : Start(Start) { assert(Start.isValid() == End.isValid() && "Start and end should either both be valid or both be invalid!"); if (Start.isValid()) ByteLength = SM.getByteDistance(Start, End); }