From 86a4d0dd6a630639aab7715323ed068940e650af Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 3 Feb 2011 17:17:35 +0000 Subject: [PATCH] Teach SourceManager::getLocation() how to cope with a source file whose inode has changed since the file was first created and that is being seen through a different path name (e.g., due to symlinks or relative path elements), such that its FileEntry pointer doesn't match a known FileEntry pointer. Since this requires a system call (to stat()), we only perform this deeper checking if we can't find the file by comparing FileEntry pointers. Also, add a micro-optimization where we don't bother to compute line numbers when given the location (1, 1). This improves the efficiency of clang_getLocationForOffset(). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124800 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/SourceManager.h | 2 +- lib/Basic/SourceManager.cpp | 102 +++++++++++++++++++++++----- tools/libclang/CIndex.cpp | 18 +++-- 3 files changed, 99 insertions(+), 23 deletions(-) diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index a19272f29d..c0fbd089e7 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -783,7 +783,7 @@ public: /// If the source file is included multiple times, the source location will /// be based upon the first inclusion. SourceLocation getLocation(const FileEntry *SourceFile, - unsigned Line, unsigned Col) const; + unsigned Line, unsigned Col); /// \brief Determines the order of 2 source locations in the translation unit. /// diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index e476cb2e29..9d5569aa39 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -23,6 +24,7 @@ #include #include #include +#include using namespace clang; using namespace SrcMgr; @@ -1107,38 +1109,58 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const { // Other miscellaneous methods. //===----------------------------------------------------------------------===// +/// \brief Retrieve the inode for the given file entry, if possible. +/// +/// This routine involves a system call, and therefore should only be used +/// in non-performance-critical code. +static llvm::Optional getActualFileInode(const FileEntry *File) { + if (!File) + return llvm::Optional(); + + struct stat StatBuf; + if (::stat(File->getName(), &StatBuf)) + return llvm::Optional(); + + return StatBuf.st_ino; +} + /// \brief Get the source location for the given file:line:col triplet. /// /// If the source file is included multiple times, the source location will /// be based upon the first inclusion. SourceLocation SourceManager::getLocation(const FileEntry *SourceFile, - unsigned Line, unsigned Col) const { + unsigned Line, unsigned Col) { assert(SourceFile && "Null source file!"); assert(Line && Col && "Line and column should start from 1!"); - fileinfo_iterator FI = FileInfos.find(SourceFile); - if (FI == FileInfos.end()) - return SourceLocation(); - ContentCache *Content = FI->second; - - // If this is the first use of line information for this buffer, compute the - /// SourceLineCache for it on demand. - if (Content->SourceLineCache == 0) { - bool MyInvalid = false; - ComputeLineNumbers(Diag, Content, ContentCacheAlloc, *this, MyInvalid); - if (MyInvalid) - return SourceLocation(); - } - // Find the first file ID that corresponds to the given file. FileID FirstFID; // First, check the main file ID, since it is common to look for a // location in the main file. + llvm::Optional SourceFileInode; + llvm::Optional SourceFileName; if (!MainFileID.isInvalid()) { const SLocEntry &MainSLoc = getSLocEntry(MainFileID); - if (MainSLoc.isFile() && MainSLoc.getFile().getContentCache() == Content) - FirstFID = MainFileID; + if (MainSLoc.isFile()) { + const ContentCache *MainContentCache + = MainSLoc.getFile().getContentCache(); + if (MainContentCache->Entry == SourceFile) + FirstFID = MainFileID; + else if (MainContentCache) { + // Fall back: check whether we have the same base name and inode + // as the main file. + const FileEntry *MainFile = MainContentCache->Entry; + SourceFileName = llvm::sys::path::filename(SourceFile->getName()); + if (*SourceFileName == llvm::sys::path::filename(MainFile->getName())) { + SourceFileInode = getActualFileInode(SourceFile); + if (SourceFileInode == getActualFileInode(MainFile)) { + FirstFID = MainFileID; + SourceFile = MainFile; + } + } + } + } } if (FirstFID.isInvalid()) { @@ -1146,16 +1168,60 @@ SourceLocation SourceManager::getLocation(const FileEntry *SourceFile, // through all of the source locations. for (unsigned I = 0, N = sloc_entry_size(); I != N; ++I) { const SLocEntry &SLoc = getSLocEntry(I); - if (SLoc.isFile() && SLoc.getFile().getContentCache() == Content) { + if (SLoc.isFile() && + SLoc.getFile().getContentCache() && + SLoc.getFile().getContentCache()->Entry == SourceFile) { FirstFID = FileID::get(I); break; } } } + + // If we haven't found what we want yet, try again, but this time stat() + // each of the files in case the files have changed since we originally + // parsed the file. + if (FirstFID.isInvalid() && + (SourceFileName || + (SourceFileName = llvm::sys::path::filename(SourceFile->getName()))) && + (SourceFileInode || + (SourceFileInode = getActualFileInode(SourceFile)))) { + for (unsigned I = 0, N = sloc_entry_size(); I != N; ++I) { + const SLocEntry &SLoc = getSLocEntry(I); + if (SLoc.isFile()) { + const ContentCache *FileContentCache + = SLoc.getFile().getContentCache(); + const FileEntry *Entry =FileContentCache? FileContentCache->Entry : 0; + if (Entry && + *SourceFileName == llvm::sys::path::filename(Entry->getName()) && + SourceFileInode == getActualFileInode(Entry)) { + FirstFID = FileID::get(I); + SourceFile = Entry; + break; + } + } + } + } if (FirstFID.isInvalid()) return SourceLocation(); + if (Line == 1 && Col == 1) + return getLocForStartOfFile(FirstFID); + + ContentCache *Content + = const_cast(getOrCreateContentCache(SourceFile)); + if (!Content) + return SourceLocation(); + + // If this is the first use of line information for this buffer, compute the + /// SourceLineCache for it on demand. + if (Content->SourceLineCache == 0) { + bool MyInvalid = false; + ComputeLineNumbers(Diag, Content, ContentCacheAlloc, *this, MyInvalid); + if (MyInvalid) + return SourceLocation(); + } + if (Line > Content->NumLines) { unsigned Size = Content->getBuffer(Diag, *this)->getBufferSize(); if (Size > 0) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index ec0f54fa1a..32e0039b0d 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -2483,12 +2483,22 @@ CXSourceLocation clang_getLocation(CXTranslationUnit tu, if (!tu || !file) return clang_getNullLocation(); + bool Logging = ::getenv("LIBCLANG_LOGGING"); ASTUnit *CXXUnit = static_cast(tu->TUData); + const FileEntry *File = static_cast(file); SourceLocation SLoc - = CXXUnit->getSourceManager().getLocation( - static_cast(file), - line, column); - if (SLoc.isInvalid()) return clang_getNullLocation(); + = CXXUnit->getSourceManager().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); } -- 2.40.0