From: Douglas Gregor Date: Wed, 2 Dec 2009 18:12:28 +0000 (+0000) Subject: Extend -remap-file=from;to to permit mapping from a non-existent X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=057e567f1b375190779e5341f42861896cdee442;p=clang Extend -remap-file=from;to to permit mapping from a non-existent file. This is accomplished by introducing the notion of a "virtual" file into the file manager, which provides a FileEntry* for a named file whose size and modification time are known but which may not exist on disk. Added a cute little test that remaps both a .c file and a .h file it includes to alternative files. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90329 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h index 5e7ac4f3c8..d0e0118d99 100644 --- a/include/clang/Basic/FileManager.h +++ b/include/clang/Basic/FileManager.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_FILEMANAGER_H #define LLVM_CLANG_FILEMANAGER_H +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/OwningPtr.h" @@ -151,6 +152,9 @@ class FileManager { /// unsigned NextFileUID; + /// \brief The virtual files that we have allocated. + llvm::SmallVector VirtualFileEntries; + // Statistics. unsigned NumDirLookups, NumFileLookups; unsigned NumDirCacheMisses, NumFileCacheMisses; @@ -199,6 +203,11 @@ public: const FileEntry *getFile(const char *FilenameStart, const char *FilenameEnd); + /// \brief Retrieve a file entry for a "virtual" file that acts as + /// if there were a file with the given name on disk. The file + /// itself is not accessed. + const FileEntry *getVirtualFile(const llvm::StringRef &Filename, + off_t Size, time_t ModificationTime); void PrintStats() const; }; diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp index ee4309de93..9c5f1d59c1 100644 --- a/lib/Basic/FileManager.cpp +++ b/lib/Basic/FileManager.cpp @@ -147,6 +147,12 @@ FileManager::FileManager() FileManager::~FileManager() { delete &UniqueDirs; delete &UniqueFiles; + for (llvm::SmallVectorImpl::iterator + V = VirtualFileEntries.begin(), + VEnd = VirtualFileEntries.end(); + V != VEnd; + ++V) + delete *V; } void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) { @@ -184,6 +190,30 @@ void FileManager::removeStatCache(StatSysCallCache *statCache) { assert(false && "Stat cache not found for removal"); } +/// \brief Retrieve the directory that the given file name resides in. +static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr, + const char *NameStart, + const char *NameEnd) { + // Figure out what directory it is in. If the string contains a / in it, + // strip off everything after it. + // FIXME: this logic should be in sys::Path. + const char *SlashPos = NameEnd-1; + while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0])) + --SlashPos; + // Ignore duplicate //'s. + while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1])) + --SlashPos; + + if (SlashPos < NameStart) { + // Use the current directory if file has no path component. + const char *Name = "."; + return FileMgr.getDirectory(Name, Name+1); + } else if (SlashPos == NameEnd-1) + return 0; // If filename ends with a /, it's a directory. + else + return FileMgr.getDirectory(NameStart, SlashPos); +} + /// getDirectory - Lookup, cache, and verify the specified directory. This /// returns null if the directory doesn't exist. /// @@ -252,33 +282,16 @@ const FileEntry *FileManager::getFile(const char *NameStart, // By default, initialize it to invalid. NamedFileEnt.setValue(NON_EXISTENT_FILE); - // Figure out what directory it is in. If the string contains a / in it, - // strip off everything after it. - // FIXME: this logic should be in sys::Path. - const char *SlashPos = NameEnd-1; - while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0])) - --SlashPos; - // Ignore duplicate //'s. - while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1])) - --SlashPos; - - const DirectoryEntry *DirInfo; - if (SlashPos < NameStart) { - // Use the current directory if file has no path component. - const char *Name = "."; - DirInfo = getDirectory(Name, Name+1); - } else if (SlashPos == NameEnd-1) - return 0; // If filename ends with a /, it's a directory. - else - DirInfo = getDirectory(NameStart, SlashPos); - - if (DirInfo == 0) // Directory doesn't exist, file can't exist. - return 0; // Get the null-terminated file name as stored as the key of the // FileEntries map. const char *InterndFileName = NamedFileEnt.getKeyData(); + const DirectoryEntry *DirInfo + = getDirectoryFromFile(*this, NameStart, NameEnd); + if (DirInfo == 0) // Directory doesn't exist, file can't exist. + return 0; + // FIXME: Use the directory info to prune this, before doing the stat syscall. // FIXME: This will reduce the # syscalls. @@ -312,6 +325,44 @@ const FileEntry *FileManager::getFile(const char *NameStart, return &UFE; } +const FileEntry * +FileManager::getVirtualFile(const llvm::StringRef &Filename, + off_t Size, time_t ModificationTime) { + const char *NameStart = Filename.begin(), *NameEnd = Filename.end(); + + ++NumFileLookups; + + // See if there is already an entry in the map. + llvm::StringMapEntry &NamedFileEnt = + FileEntries.GetOrCreateValue(NameStart, NameEnd); + + // See if there is already an entry in the map. + if (NamedFileEnt.getValue()) + return NamedFileEnt.getValue() == NON_EXISTENT_FILE + ? 0 : NamedFileEnt.getValue(); + + ++NumFileCacheMisses; + + // By default, initialize it to invalid. + NamedFileEnt.setValue(NON_EXISTENT_FILE); + + const DirectoryEntry *DirInfo + = getDirectoryFromFile(*this, NameStart, NameEnd); + if (DirInfo == 0) // Directory doesn't exist, file can't exist. + return 0; + + FileEntry *UFE = new FileEntry(); + VirtualFileEntries.push_back(UFE); + NamedFileEnt.setValue(UFE); + + UFE->Name = NamedFileEnt.getKeyData(); + UFE->Size = Size; + UFE->ModTime = ModificationTime; + UFE->Dir = DirInfo; + UFE->UID = NextFileUID++; + return UFE; +} + void FileManager::PrintStats() const { llvm::errs() << "\n*** File Manager Stats:\n"; llvm::errs() << UniqueFiles.size() << " files found, " diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp index 9c42993cf0..aa05b34ef5 100644 --- a/lib/Frontend/InitPreprocessor.cpp +++ b/lib/Frontend/InitPreprocessor.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/System/Path.h" @@ -502,11 +503,11 @@ static void InitializeFileRemapping(Diagnostic &Diags, continue; } - // Find the file that we're mapping from. - const FileEntry *FromFile = FileMgr.getFile(Remap->first); + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = FileMgr.getVirtualFile(Remap->first, + ToFile->getSize(), + 0); if (!FromFile) { - // FIXME: We could actually recover from this, by faking a - // FileEntry based on the "ToFile". Diags.Report(diag::err_fe_remap_missing_from_file) << Remap->first; continue; diff --git a/test/Misc/Inputs/remapped-file-2 b/test/Misc/Inputs/remapped-file-2 new file mode 100644 index 0000000000..9ac034a3b9 --- /dev/null +++ b/test/Misc/Inputs/remapped-file-2 @@ -0,0 +1,3 @@ +#include "nonexistent.h" + +int *f() { return fp; } diff --git a/test/Misc/Inputs/remapped-file-3 b/test/Misc/Inputs/remapped-file-3 new file mode 100644 index 0000000000..b7ab613367 --- /dev/null +++ b/test/Misc/Inputs/remapped-file-3 @@ -0,0 +1,2 @@ +extern float *fp; + diff --git a/test/Misc/remap-file.c b/test/Misc/remap-file.c index e8aa3e44cc..da10590ccd 100644 --- a/test/Misc/remap-file.c +++ b/test/Misc/remap-file.c @@ -1,5 +1,8 @@ -// RUN: clang-cc -remap-file="%s;%S/Inputs/remapped-file" -fsyntax-only %s 2>&1 | FileCheck %s - -// CHECK: remap-file.c:1:28: warning: incompatible pointer types +// RUN: clang-cc -remap-file="%s;%S/Inputs/remapped-file" -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-EXIST %s +// RUN: clang-cc -remap-file="%S/nonexistent.c;%S/Inputs/remapped-file" -fsyntax-only %S/nonexistent.c 2>&1 | FileCheck -check-prefix=CHECK-NONEXIST %s +// RUN: clang-cc -remap-file="%S/nonexistent.c;%S/Inputs/remapped-file-2" -remap-file="%S/nonexistent.h;%S/Inputs/remapped-file-3" -fsyntax-only %S/nonexistent.c 2>&1 | FileCheck -check-prefix=CHECK-HEADER %s +// CHECK-EXIST: remap-file.c:1:28: warning: incompatible pointer types +// CHECK-NONEXIST: nonexistent.c:1:28: warning: incompatible pointer types +// CHECK-HEADER: nonexistent.c:3:19: warning: incompatible pointer types int