diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Basic/FileManager.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Basic/FileManager.cpp | 319 |
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; } |