Index: include/clang/Basic/FileManager.h =================================================================== --- include/clang/Basic/FileManager.h +++ include/clang/Basic/FileManager.h @@ -104,6 +104,10 @@ void closeFile() const { File.reset(); // rely on destructor to close File } + + // Only for use in tests to see if deferred opens are happening, rather than + // relying on RealPathName being empty. + bool isOpenForTests() const { return File != nullptr; } }; struct FileData; @@ -173,6 +177,9 @@ /// or a directory) as virtual directories. void addAncestorsAsVirtualDirs(StringRef Path); + /// Fills the RealPathName in file entry. + void fillRealPathName(FileEntry *UFE, llvm::StringRef FileName); + public: FileManager(const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr FS = nullptr); Index: lib/Basic/FileManager.cpp =================================================================== --- lib/Basic/FileManager.cpp +++ lib/Basic/FileManager.cpp @@ -293,16 +293,8 @@ // If we opened the file for the first time, record the resulting info. // Do this even if the cache entry was valid, maybe we didn't previously open. if (F && !UFE.File) { - if (auto PathName = F->getName()) { - llvm::SmallString<128> AbsPath(*PathName); - // This is not the same as `VFS::getRealPath()`, which resolves symlinks - // but can be very expensive on real file systems. - // FIXME: the semantic of RealPathName is unclear, and the name might be - // misleading. We need to clean up the interface here. - makeAbsolutePath(AbsPath); - llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true); - UFE.RealPathName = AbsPath.str(); - } + if (auto PathName = F->getName()) + fillRealPathName(&UFE, *PathName); UFE.File = std::move(F); assert(!UFE.DeferredOpen && "we just opened it!"); } @@ -395,6 +387,7 @@ UFE->UniqueID = Data.UniqueID; UFE->IsNamedPipe = Data.IsNamedPipe; UFE->InPCH = Data.InPCH; + fillRealPathName(UFE, Data.Name); } if (!UFE) { @@ -438,6 +431,17 @@ return Changed; } +void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { + llvm::SmallString<128> AbsPath(FileName); + // This is not the same as `VFS::getRealPath()`, which resolves symlinks + // but can be very expensive on real file systems. + // FIXME: the semantic of RealPathName is unclear, and the name might be + // misleading. We need to clean up the interface here. + makeAbsolutePath(AbsPath); + llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true); + UFE->RealPathName = AbsPath.str(); +} + llvm::ErrorOr> FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile, bool ShouldCloseOpenFile) { Index: unittests/Basic/FileManagerTest.cpp =================================================================== --- unittests/Basic/FileManagerTest.cpp +++ unittests/Basic/FileManagerTest.cpp @@ -235,22 +235,18 @@ ASSERT_TRUE(file != nullptr); ASSERT_TRUE(file->isValid()); // "real path name" reveals whether the file was actually opened. - EXPECT_EQ("", file->tryGetRealPathName()); + EXPECT_FALSE(file->isOpenForTests()); file = manager.getFile("/tmp/test", /*OpenFile=*/true); ASSERT_TRUE(file != nullptr); ASSERT_TRUE(file->isValid()); -#ifdef _WIN32 - EXPECT_EQ("/tmp\\test", file->tryGetRealPathName()); -#else - EXPECT_EQ("/tmp/test", file->tryGetRealPathName()); -#endif + EXPECT_TRUE(file->isOpenForTests()); // However we should never try to open a file previously opened as virtual. ASSERT_TRUE(manager.getVirtualFile("/tmp/testv", 5, 0)); ASSERT_TRUE(manager.getFile("/tmp/testv", /*OpenFile=*/false)); file = manager.getFile("/tmp/testv", /*OpenFile=*/true); - EXPECT_EQ("", file->tryGetRealPathName()); + EXPECT_FALSE(file->isOpenForTests()); } // The following tests apply to Unix-like system only. @@ -353,4 +349,19 @@ EXPECT_EQ(Path, ExpectedResult); } +// getVirtualFile should always fill the real path. +TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) { + // Inject fake files into the file system. + auto statCache = llvm::make_unique(); + statCache->InjectDirectory("/tmp", 42); + statCache->InjectFile("/tmp/test", 43); + manager.addStatCache(std::move(statCache)); + + // Check for real path. + const FileEntry *file = manager.getVirtualFile("/tmp/test", 123, 1); + ASSERT_TRUE(file != nullptr); + ASSERT_TRUE(file->isValid()); + EXPECT_EQ(file->tryGetRealPathName(), "/tmp/test"); +} + } // anonymous namespace