aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Basic/FileManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Basic/FileManager.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Basic/FileManager.cpp319
1 files changed, 182 insertions, 137 deletions
diff --git a/contrib/llvm-project/clang/lib/Basic/FileManager.cpp b/contrib/llvm-project/clang/lib/Basic/FileManager.cpp
index 6e9d5d7fb422..974c8c22598f 100644
--- a/contrib/llvm-project/clang/lib/Basic/FileManager.cpp
+++ b/contrib/llvm-project/clang/lib/Basic/FileManager.cpp
@@ -31,6 +31,7 @@
#include <climits>
#include <cstdint>
#include <cstdlib>
+#include <optional>
#include <string>
#include <utility>
@@ -105,10 +106,10 @@ void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
return;
// Add the virtual directory to the cache.
- auto UDE = std::make_unique<DirectoryEntry>();
+ auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
UDE->Name = NamedDirEnt.first();
- NamedDirEnt.second = *UDE.get();
- VirtualDirectoryEntries.push_back(std::move(UDE));
+ NamedDirEnt.second = *UDE;
+ VirtualDirectoryEntries.push_back(UDE);
// Recursively add the other ancestors.
addAncestorsAsVirtualDirs(DirName);
@@ -123,16 +124,16 @@ FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) {
DirName != llvm::sys::path::root_path(DirName) &&
llvm::sys::path::is_separator(DirName.back()))
DirName = DirName.substr(0, DirName.size()-1);
-#ifdef _WIN32
- // Fixing a problem with "clang C:test.c" on Windows.
- // Stat("C:") does not recognize "C:" as a valid directory
- std::string DirNameStr;
- if (DirName.size() > 1 && DirName.back() == ':' &&
- DirName.equals_lower(llvm::sys::path::root_name(DirName))) {
- DirNameStr = DirName.str() + '.';
- DirName = DirNameStr;
+ std::optional<std::string> DirNameStr;
+ if (is_style_windows(llvm::sys::path::Style::native)) {
+ // Fixing a problem with "clang C:test.c" on Windows.
+ // Stat("C:") does not recognize "C:" as a valid directory
+ if (DirName.size() > 1 && DirName.back() == ':' &&
+ DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
+ DirNameStr = DirName.str() + '.';
+ DirName = *DirNameStr;
+ }
}
-#endif
++NumDirLookups;
@@ -172,14 +173,15 @@ FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) {
// same inode (this occurs on Unix-like systems when one dir is
// symlinked to another, for example) or the same path (on
// Windows).
- DirectoryEntry &UDE = UniqueRealDirs[Status.getUniqueID()];
+ DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
- NamedDirEnt.second = UDE;
- if (UDE.getName().empty()) {
+ if (!UDE) {
// We don't have this directory yet, add it. We use the string
// key from the SeenDirEntries map as the string.
- UDE.Name = InterndDirName;
+ UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
+ UDE->Name = InterndDirName;
}
+ NamedDirEnt.second = *UDE;
return DirectoryEntryRef(NamedDirEnt);
}
@@ -211,13 +213,7 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
if (!SeenFileInsertResult.first->second)
return llvm::errorCodeToError(
SeenFileInsertResult.first->second.getError());
- // Construct and return and FileEntryRef, unless it's a redirect to another
- // filename.
- FileEntryRef::MapValue Value = *SeenFileInsertResult.first->second;
- if (LLVM_LIKELY(Value.V.is<FileEntry *>()))
- return FileEntryRef(*SeenFileInsertResult.first);
- return FileEntryRef(*reinterpret_cast<const FileEntryRef::MapEntry *>(
- Value.V.get<const void *>()));
+ return FileEntryRef(*SeenFileInsertResult.first);
}
// We've not seen this before. Fill it in.
@@ -268,42 +264,77 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// It exists. See if we have already opened a file with the same inode.
// This occurs when one dir is symlinked to another, for example.
- FileEntry &UFE = UniqueRealFiles[Status.getUniqueID()];
-
- if (Status.getName() == Filename) {
- // The name matches. Set the FileEntry.
- NamedFileEnt->second = FileEntryRef::MapValue(UFE, DirInfo);
+ FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
+ bool ReusingEntry = UFE != nullptr;
+ if (!UFE)
+ UFE = new (FilesAlloc.Allocate()) FileEntry();
+
+ if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
+ // Use the requested name. Set the FileEntry.
+ NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);
} else {
// Name mismatch. We need a redirect. First grab the actual entry we want
// to return.
+ //
+ // This redirection logic intentionally leaks the external name of a
+ // redirected file that uses 'use-external-name' in \a
+ // vfs::RedirectionFileSystem. This allows clang to report the external
+ // name to users (in diagnostics) and to tools that don't have access to
+ // the VFS (in debug info and dependency '.d' files).
+ //
+ // FIXME: This is pretty complex and has some very complicated interactions
+ // with the rest of clang. It's also inconsistent with how "real"
+ // filesystems behave and confuses parts of clang expect to see the
+ // name-as-accessed on the \a FileEntryRef.
+ //
+ // A potential plan to remove this is as follows -
+ // - Update callers such as `HeaderSearch::findUsableModuleForHeader()`
+ // to explicitly use the `getNameAsRequested()` rather than just using
+ // `getName()`.
+ // - Add a `FileManager::getExternalPath` API for explicitly getting the
+ // remapped external filename when there is one available. Adopt it in
+ // callers like diagnostics/deps reporting instead of calling
+ // `getName()` directly.
+ // - Switch the meaning of `FileEntryRef::getName()` to get the requested
+ // name, not the external name. Once that sticks, revert callers that
+ // want the requested name back to calling `getName()`.
+ // - Update the VFS to always return the requested name. This could also
+ // return the external name, or just have an API to request it
+ // lazily. The latter has the benefit of making accesses of the
+ // external path easily tracked, but may also require extra work than
+ // just returning up front.
+ // - (Optionally) Add an API to VFS to get the external filename lazily
+ // and update `FileManager::getExternalPath()` to use it instead. This
+ // has the benefit of making such accesses easily tracked, though isn't
+ // necessarily required (and could cause extra work than just adding to
+ // eg. `vfs::Status` up front).
auto &Redirection =
*SeenFileEntries
- .insert({Status.getName(), FileEntryRef::MapValue(UFE, DirInfo)})
+ .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})
.first;
assert(Redirection.second->V.is<FileEntry *>() &&
"filename redirected to a non-canonical filename?");
- assert(Redirection.second->V.get<FileEntry *>() == &UFE &&
+ assert(Redirection.second->V.get<FileEntry *>() == UFE &&
"filename from getStatValue() refers to wrong file");
// Cache the redirection in the previously-inserted entry, still available
// in the tentative return value.
- NamedFileEnt->second = FileEntryRef::MapValue(Redirection);
-
- // Fix the tentative return value.
- NamedFileEnt = &Redirection;
+ NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo);
}
FileEntryRef ReturnedRef(*NamedFileEnt);
- if (UFE.isValid()) { // Already have an entry with this inode, return it.
+ if (ReusingEntry) { // Already have an entry with this inode, return it.
- // FIXME: this hack ensures that if we look up a file by a virtual path in
- // the VFS that the getDir() will have the virtual path, even if we found
- // the file by a 'real' path first. This is required in order to find a
- // module's structure when its headers/module map are mapped in the VFS.
- // We should remove this as soon as we can properly support a file having
- // multiple names.
- if (&DirInfo.getDirEntry() != UFE.Dir && Status.IsVFSMapped)
- UFE.Dir = &DirInfo.getDirEntry();
+ // FIXME: This hack ensures that `getDir()` will use the path that was
+ // used to lookup this file, even if we found a file by different path
+ // first. This is required in order to find a module's structure when its
+ // headers/module map are mapped in the VFS.
+ //
+ // See above for how this will eventually be removed. `IsVFSMapped`
+ // *cannot* be narrowed to `ExposesExternalVFSPath` as crash reproducers
+ // also depend on this logic and they have `use-external-paths: false`.
+ if (&DirInfo.getDirEntry() != UFE->Dir && Status.IsVFSMapped)
+ UFE->Dir = &DirInfo.getDirEntry();
// Always update LastRef to the last name by which a file was accessed.
// FIXME: Neither this nor always using the first reference is correct; we
@@ -312,28 +343,27 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// corresponding FileEntry.
// FIXME: LastRef should be removed from FileEntry once all clients adopt
// FileEntryRef.
- UFE.LastRef = ReturnedRef;
+ UFE->LastRef = ReturnedRef;
return ReturnedRef;
}
// Otherwise, we don't have this file yet, add it.
- UFE.LastRef = ReturnedRef;
- UFE.Size = Status.getSize();
- UFE.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
- UFE.Dir = &DirInfo.getDirEntry();
- UFE.UID = NextFileUID++;
- UFE.UniqueID = Status.getUniqueID();
- UFE.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
- UFE.File = std::move(F);
- UFE.IsValid = true;
-
- if (UFE.File) {
- if (auto PathName = UFE.File->getName())
- fillRealPathName(&UFE, *PathName);
+ UFE->LastRef = ReturnedRef;
+ UFE->Size = Status.getSize();
+ UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
+ UFE->Dir = &DirInfo.getDirEntry();
+ UFE->UID = NextFileUID++;
+ UFE->UniqueID = Status.getUniqueID();
+ UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
+ UFE->File = std::move(F);
+
+ if (UFE->File) {
+ if (auto PathName = UFE->File->getName())
+ fillRealPathName(UFE, *PathName);
} else if (!openFile) {
// We should still fill the path even if we aren't opening the file.
- fillRealPathName(&UFE, InterndFileName);
+ fillRealPathName(UFE, InterndFileName);
}
return ReturnedRef;
}
@@ -373,8 +403,7 @@ FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
FileEntryRef::MapValue Value = *NamedFileEnt.second;
if (LLVM_LIKELY(Value.V.is<FileEntry *>()))
return FileEntryRef(NamedFileEnt);
- return FileEntryRef(*reinterpret_cast<const FileEntryRef::MapEntry *>(
- Value.V.get<const void *>()));
+ return FileEntryRef(*Value.V.get<const FileEntryRef::MapEntry *>());
}
// We've not seen this before, or the file is cached as non-existent.
@@ -384,9 +413,12 @@ FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
// Now that all ancestors of Filename are in the cache, the
// following call is guaranteed to find the DirectoryEntry from the
- // cache.
- auto DirInfo = expectedToOptional(
- getDirectoryFromFile(*this, Filename, /*CacheFailure=*/true));
+ // cache. A virtual file can also have an empty filename, that could come
+ // from a source location preprocessor directive with an empty filename as
+ // an example, so we need to pretend it has a name to ensure a valid directory
+ // entry can be returned.
+ auto DirInfo = expectedToOptional(getDirectoryFromFile(
+ *this, Filename.empty() ? "." : Filename, /*CacheFailure=*/true));
assert(DirInfo &&
"The directory of a virtual file should already be in the cache.");
@@ -394,52 +426,55 @@ FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
llvm::vfs::Status Status;
const char *InterndFileName = NamedFileEnt.first().data();
if (!getStatValue(InterndFileName, Status, true, nullptr)) {
- UFE = &UniqueRealFiles[Status.getUniqueID()];
Status = llvm::vfs::Status(
Status.getName(), Status.getUniqueID(),
llvm::sys::toTimePoint(ModificationTime),
Status.getUser(), Status.getGroup(), Size,
Status.getType(), Status.getPermissions());
- NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);
-
- // If we had already opened this file, close it now so we don't
- // leak the descriptor. We're not going to use the file
- // descriptor anyway, since this is a virtual file.
- if (UFE->File)
- UFE->closeFile();
-
- // If we already have an entry with this inode, return it.
- //
- // FIXME: Surely this should add a reference by the new name, and return
- // it instead...
- if (UFE->isValid())
+ auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
+ if (RealFE) {
+ // If we had already opened this file, close it now so we don't
+ // leak the descriptor. We're not going to use the file
+ // descriptor anyway, since this is a virtual file.
+ if (RealFE->File)
+ RealFE->closeFile();
+ // If we already have an entry with this inode, return it.
+ //
+ // FIXME: Surely this should add a reference by the new name, and return
+ // it instead...
+ NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo);
return FileEntryRef(NamedFileEnt);
-
- UFE->UniqueID = Status.getUniqueID();
- UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
- fillRealPathName(UFE, Status.getName());
+ }
+ // File exists, but no entry - create it.
+ RealFE = new (FilesAlloc.Allocate()) FileEntry();
+ RealFE->UniqueID = Status.getUniqueID();
+ RealFE->IsNamedPipe =
+ Status.getType() == llvm::sys::fs::file_type::fifo_file;
+ fillRealPathName(RealFE, Status.getName());
+
+ UFE = RealFE;
} else {
- VirtualFileEntries.push_back(std::make_unique<FileEntry>());
- UFE = VirtualFileEntries.back().get();
- NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);
+ // File does not exist, create a virtual entry.
+ UFE = new (FilesAlloc.Allocate()) FileEntry();
+ VirtualFileEntries.push_back(UFE);
}
+ NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);
UFE->LastRef = FileEntryRef(NamedFileEnt);
UFE->Size = Size;
UFE->ModTime = ModificationTime;
UFE->Dir = &DirInfo->getDirEntry();
UFE->UID = NextFileUID++;
- UFE->IsValid = true;
UFE->File.reset();
return FileEntryRef(NamedFileEnt);
}
-llvm::Optional<FileEntryRef> FileManager::getBypassFile(FileEntryRef VF) {
+OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) {
// Stat of the file and return nullptr if it doesn't exist.
llvm::vfs::Status Status;
if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr))
- return None;
+ return std::nullopt;
if (!SeenBypassFileEntries)
SeenBypassFileEntries = std::make_unique<
@@ -452,16 +487,14 @@ llvm::Optional<FileEntryRef> FileManager::getBypassFile(FileEntryRef VF) {
return FileEntryRef(*Insertion.first);
// Fill in the new entry from the stat.
- BypassFileEntries.push_back(std::make_unique<FileEntry>());
- const FileEntry &VFE = VF.getFileEntry();
- FileEntry &BFE = *BypassFileEntries.back();
- Insertion.first->second = FileEntryRef::MapValue(BFE, VF.getDir());
- BFE.LastRef = FileEntryRef(*Insertion.first);
- BFE.Size = Status.getSize();
- BFE.Dir = VFE.Dir;
- BFE.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
- BFE.UID = NextFileUID++;
- BFE.IsValid = true;
+ FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry();
+ BypassFileEntries.push_back(BFE);
+ Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir());
+ BFE->LastRef = FileEntryRef(*Insertion.first);
+ BFE->Size = Status.getSize();
+ BFE->Dir = VF.getFileEntry().Dir;
+ BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
+ BFE->UID = NextFileUID++;
// Save the entry in the bypass table and return.
return FileEntryRef(*Insertion.first);
@@ -499,12 +532,13 @@ void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
// misleading. We need to clean up the interface here.
makeAbsolutePath(AbsPath);
llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);
- UFE->RealPathName = std::string(AbsPath.str());
+ UFE->RealPathName = std::string(AbsPath);
}
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
-FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile,
+FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,
bool RequiresNullTerminator) {
+ const FileEntry *Entry = &FE.getFileEntry();
// If the content is living on the file entry, return a reference to it.
if (Entry->Content)
return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
@@ -515,7 +549,7 @@ FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile,
if (isVolatile || Entry->isNamedPipe())
FileSize = -1;
- StringRef Filename = Entry->getName();
+ StringRef Filename = FE.getName();
// If the file is already open, use the open file descriptor.
if (Entry->File) {
auto Result = Entry->File->getBuffer(Filename, FileSize,
@@ -578,55 +612,66 @@ FileManager::getNoncachedStatValue(StringRef Path,
}
void FileManager::GetUniqueIDMapping(
- SmallVectorImpl<const FileEntry *> &UIDToFiles) const {
+ SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const {
UIDToFiles.clear();
UIDToFiles.resize(NextFileUID);
- // Map file entries
- for (llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>,
- llvm::BumpPtrAllocator>::const_iterator
- FE = SeenFileEntries.begin(),
- FEEnd = SeenFileEntries.end();
- FE != FEEnd; ++FE)
- if (llvm::ErrorOr<FileEntryRef::MapValue> Entry = FE->getValue()) {
- if (const auto *FE = Entry->V.dyn_cast<FileEntry *>())
- UIDToFiles[FE->getUID()] = FE;
- }
-
- // Map virtual file entries
- for (const auto &VFE : VirtualFileEntries)
- UIDToFiles[VFE->getUID()] = VFE.get();
+ for (const auto &Entry : SeenFileEntries) {
+ // Only return files that exist and are not redirected.
+ if (!Entry.getValue() || !Entry.getValue()->V.is<FileEntry *>())
+ continue;
+ FileEntryRef FE(Entry);
+ // Add this file if it's the first one with the UID, or if its name is
+ // better than the existing one.
+ OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()];
+ if (!ExistingFE || FE.getName() < ExistingFE->getName())
+ ExistingFE = FE;
+ }
}
-StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) {
- llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
- = CanonicalNames.find(Dir);
- if (Known != CanonicalNames.end())
- return Known->second;
-
- StringRef CanonicalName(Dir->getName());
-
- SmallString<4096> CanonicalNameBuf;
- if (!FS->getRealPath(Dir->getName(), CanonicalNameBuf))
- CanonicalName = StringRef(CanonicalNameBuf).copy(CanonicalNameStorage);
+StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) {
+ return getCanonicalName(Dir, Dir.getName());
+}
- CanonicalNames.insert({Dir, CanonicalName});
- return CanonicalName;
+StringRef FileManager::getCanonicalName(FileEntryRef File) {
+ return getCanonicalName(File, File.getName());
}
-StringRef FileManager::getCanonicalName(const FileEntry *File) {
- llvm::DenseMap<const void *, llvm::StringRef>::iterator Known
- = CanonicalNames.find(File);
+StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) {
+ llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
+ CanonicalNames.find(Entry);
if (Known != CanonicalNames.end())
return Known->second;
- StringRef CanonicalName(File->getName());
-
- SmallString<4096> CanonicalNameBuf;
- if (!FS->getRealPath(File->getName(), CanonicalNameBuf))
- CanonicalName = StringRef(CanonicalNameBuf).copy(CanonicalNameStorage);
+ // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to
+ // store it in the DenseMap below.
+ StringRef CanonicalName(Name);
+
+ SmallString<256> AbsPathBuf;
+ SmallString<256> RealPathBuf;
+ if (!FS->getRealPath(Name, RealPathBuf)) {
+ if (is_style_windows(llvm::sys::path::Style::native)) {
+ // For Windows paths, only use the real path if it doesn't resolve
+ // a substitute drive, as those are used to avoid MAX_PATH issues.
+ AbsPathBuf = Name;
+ if (!FS->makeAbsolute(AbsPathBuf)) {
+ if (llvm::sys::path::root_name(RealPathBuf) ==
+ llvm::sys::path::root_name(AbsPathBuf)) {
+ CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
+ } else {
+ // Fallback to using the absolute path.
+ // Simplifying /../ is semantically valid on Windows even in the
+ // presence of symbolic links.
+ llvm::sys::path::remove_dots(AbsPathBuf, /*remove_dot_dot=*/true);
+ CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
+ }
+ }
+ } else {
+ CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
+ }
+ }
- CanonicalNames.insert({File, CanonicalName});
+ CanonicalNames.insert({Entry, CanonicalName});
return CanonicalName;
}