Skip to content

Commit c972175

Browse files
committed
[VFS] Use original path when falling back to external FS
This is a follow up to 0be9ca7 to make paths in the case of falling back to the external file system use the original format, preserving relative paths, and allow the external filesystem to canonicalize them if needed. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D109128
1 parent c3a3e65 commit c972175

File tree

4 files changed

+215
-28
lines changed

4 files changed

+215
-28
lines changed

clang/test/VFS/relative-path-errors.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: mkdir -p %t
2+
// RUN: cd %t
3+
// RUN: cp %s main.c
4+
// RUN: not %clang_cc1 main.c 2>&1 | FileCheck %s
5+
// RUN: echo '{"roots": [],"version": 0}' > %t.yaml
6+
// RUN: not %clang_cc1 -ivfsoverlay %t.yaml main.c 2>&1 | FileCheck %s
7+
// RUN: echo '{"version": 0,"roots":[{"type":"directory","name":"%/t","contents":[{"type":"file","name":"vfsname", "external-contents":"main.c"}]}]}' > %t.yaml
8+
// RUN: not %clang_cc1 -ivfsoverlay %t.yaml vfsname 2>&1 | FileCheck %s
9+
10+
// CHECK: {{^}}main.c:[[# @LINE + 1]]:1: error:
11+
foobarbaz

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ class File {
121121

122122
/// Closes the file.
123123
virtual std::error_code close() = 0;
124+
125+
// Get the same file with a different path.
126+
static ErrorOr<std::unique_ptr<File>>
127+
getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P);
128+
129+
protected:
130+
// Set the file's underlying path.
131+
virtual void setPath(const Twine &Path) {}
124132
};
125133

126134
/// A member of a directory, yielded by a directory_iterator.
@@ -757,6 +765,12 @@ class RedirectingFileSystem : public vfs::FileSystem {
757765
/// with the given error code on a path associated with the provided Entry.
758766
bool shouldFallBackToExternalFS(std::error_code EC, Entry *E = nullptr) const;
759767

768+
/// Get the File status, or error, from the underlying external file system.
769+
/// This returns the status with the originally requested name, while looking
770+
/// up the entry using the canonical path.
771+
ErrorOr<Status> getExternalStatus(const Twine &CanonicalPath,
772+
const Twine &OriginalPath) const;
773+
760774
// In a RedirectingFileSystem, keys can be specified in Posix or Windows
761775
// style (or even a mixture of both), so this comparison helper allows
762776
// slashes (representing a root) to match backslashes (and vice versa). Note
@@ -814,7 +828,8 @@ class RedirectingFileSystem : public vfs::FileSystem {
814828
Entry *From) const;
815829

816830
/// Get the status for a path with the provided \c LookupResult.
817-
ErrorOr<Status> status(const Twine &Path, const LookupResult &Result);
831+
ErrorOr<Status> status(const Twine &CanonicalPath, const Twine &OriginalPath,
832+
const LookupResult &Result);
818833

819834
public:
820835
/// Looks up \p Path in \c Roots and returns a LookupResult giving the

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class RealFile : public File {
194194
bool RequiresNullTerminator,
195195
bool IsVolatile) override;
196196
std::error_code close() override;
197+
void setPath(const Twine &Path) override;
197198
};
198199

199200
} // namespace
@@ -229,6 +230,12 @@ std::error_code RealFile::close() {
229230
return EC;
230231
}
231232

233+
void RealFile::setPath(const Twine &Path) {
234+
RealName = Path.str();
235+
if (auto Status = status())
236+
S = Status.get().copyWithNewName(Status.get(), Path);
237+
}
238+
232239
namespace {
233240

234241
/// A file system according to your operating system.
@@ -639,6 +646,8 @@ class InMemoryFileAdaptor : public File {
639646
}
640647

641648
std::error_code close() override { return {}; }
649+
650+
void setPath(const Twine &Path) override { RequestedName = Path.str(); }
642651
};
643652
} // namespace
644653

@@ -1244,7 +1253,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
12441253
}
12451254

12461255
// Use status to make sure the path exists and refers to a directory.
1247-
ErrorOr<Status> S = status(Path, *Result);
1256+
ErrorOr<Status> S = status(Path, Dir, *Result);
12481257
if (!S) {
12491258
if (shouldFallBackToExternalFS(S.getError(), Result->E))
12501259
return ExternalFS->dir_begin(Dir, EC);
@@ -1971,47 +1980,68 @@ RedirectingFileSystem::lookupPathImpl(
19711980
return make_error_code(llvm::errc::no_such_file_or_directory);
19721981
}
19731982

1974-
static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1983+
static Status getRedirectedFileStatus(const Twine &OriginalPath,
1984+
bool UseExternalNames,
19751985
Status ExternalStatus) {
19761986
Status S = ExternalStatus;
19771987
if (!UseExternalNames)
1978-
S = Status::copyWithNewName(S, Path);
1988+
S = Status::copyWithNewName(S, OriginalPath);
19791989
S.IsVFSMapped = true;
19801990
return S;
19811991
}
19821992

19831993
ErrorOr<Status> RedirectingFileSystem::status(
1984-
const Twine &Path, const RedirectingFileSystem::LookupResult &Result) {
1994+
const Twine &CanonicalPath, const Twine &OriginalPath,
1995+
const RedirectingFileSystem::LookupResult &Result) {
19851996
if (Optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
1986-
ErrorOr<Status> S = ExternalFS->status(*ExtRedirect);
1997+
SmallString<256> CanonicalRemappedPath((*ExtRedirect).str());
1998+
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
1999+
return EC;
2000+
2001+
ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath);
19872002
if (!S)
19882003
return S;
2004+
S = Status::copyWithNewName(*S, *ExtRedirect);
19892005
auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
1990-
return getRedirectedFileStatus(Path, RE->useExternalName(UseExternalNames),
1991-
*S);
2006+
return getRedirectedFileStatus(OriginalPath,
2007+
RE->useExternalName(UseExternalNames), *S);
19922008
}
19932009

19942010
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
1995-
return Status::copyWithNewName(DE->getStatus(), Path);
2011+
return Status::copyWithNewName(DE->getStatus(), CanonicalPath);
19962012
}
19972013

1998-
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
1999-
SmallString<256> Path;
2000-
Path_.toVector(Path);
2014+
ErrorOr<Status>
2015+
RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
2016+
const Twine &OriginalPath) const {
2017+
if (auto Result = ExternalFS->status(CanonicalPath)) {
2018+
return Result.get().copyWithNewName(Result.get(), OriginalPath);
2019+
} else {
2020+
return Result.getError();
2021+
}
2022+
}
20012023

2002-
if (std::error_code EC = makeCanonical(Path))
2024+
ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2025+
SmallString<256> CanonicalPath;
2026+
OriginalPath.toVector(CanonicalPath);
2027+
2028+
if (std::error_code EC = makeCanonical(CanonicalPath))
20032029
return EC;
20042030

2005-
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2031+
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2032+
lookupPath(CanonicalPath);
20062033
if (!Result) {
2007-
if (shouldFallBackToExternalFS(Result.getError()))
2008-
return ExternalFS->status(Path);
2034+
if (shouldFallBackToExternalFS(Result.getError())) {
2035+
return getExternalStatus(CanonicalPath, OriginalPath);
2036+
}
20092037
return Result.getError();
20102038
}
20112039

2012-
ErrorOr<Status> S = status(Path, *Result);
2013-
if (!S && shouldFallBackToExternalFS(S.getError(), Result->E))
2014-
S = ExternalFS->status(Path);
2040+
ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result);
2041+
if (!S && shouldFallBackToExternalFS(S.getError(), Result->E)) {
2042+
return getExternalStatus(CanonicalPath, OriginalPath);
2043+
}
2044+
20152045
return S;
20162046
}
20172047

@@ -2036,35 +2066,58 @@ class FileWithFixedStatus : public File {
20362066
}
20372067

20382068
std::error_code close() override { return InnerFile->close(); }
2069+
2070+
void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
20392071
};
20402072

20412073
} // namespace
20422074

20432075
ErrorOr<std::unique_ptr<File>>
2044-
RedirectingFileSystem::openFileForRead(const Twine &Path_) {
2045-
SmallString<256> Path;
2046-
Path_.toVector(Path);
2076+
File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2077+
if (!Result)
2078+
return Result;
20472079

2048-
if (std::error_code EC = makeCanonical(Path))
2080+
auto F = std::move(*Result);
2081+
auto Name = F->getName();
2082+
if (Name && Name.get() != P.str())
2083+
F->setPath(P);
2084+
return F;
2085+
}
2086+
2087+
ErrorOr<std::unique_ptr<File>>
2088+
RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2089+
SmallString<256> CanonicalPath;
2090+
OriginalPath.toVector(CanonicalPath);
2091+
2092+
if (std::error_code EC = makeCanonical(CanonicalPath))
20492093
return EC;
20502094

2051-
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2095+
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2096+
lookupPath(CanonicalPath);
20522097
if (!Result) {
20532098
if (shouldFallBackToExternalFS(Result.getError()))
2054-
return ExternalFS->openFileForRead(Path);
2099+
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2100+
OriginalPath);
2101+
20552102
return Result.getError();
20562103
}
20572104

20582105
if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
20592106
return make_error_code(llvm::errc::invalid_argument);
20602107

20612108
StringRef ExtRedirect = *Result->getExternalRedirect();
2109+
SmallString<256> CanonicalRemappedPath(ExtRedirect.str());
2110+
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2111+
return EC;
2112+
20622113
auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
20632114

2064-
auto ExternalFile = ExternalFS->openFileForRead(ExtRedirect);
2115+
auto ExternalFile = File::getWithPath(
2116+
ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect);
20652117
if (!ExternalFile) {
20662118
if (shouldFallBackToExternalFS(ExternalFile.getError(), Result->E))
2067-
return ExternalFS->openFileForRead(Path);
2119+
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2120+
OriginalPath);
20682121
return ExternalFile;
20692122
}
20702123

@@ -2074,7 +2127,7 @@ RedirectingFileSystem::openFileForRead(const Twine &Path_) {
20742127

20752128
// FIXME: Update the status with the name and VFSMapped.
20762129
Status S = getRedirectedFileStatus(
2077-
Path, RE->useExternalName(UseExternalNames), *ExternalStatus);
2130+
OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
20782131
return std::unique_ptr<File>(
20792132
std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
20802133
}

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,114 @@ TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {
16271627
EXPECT_EQ(0, NumDiagnostics);
16281628
}
16291629

1630+
TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {
1631+
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1632+
new vfs::InMemoryFileSystem);
1633+
BaseFS->addFile("//root/foo/a", 0,
1634+
MemoryBuffer::getMemBuffer("contents of a"));
1635+
ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo"));
1636+
auto RemappedFS = vfs::RedirectingFileSystem::create(
1637+
{}, /*UseExternalNames=*/false, *BaseFS);
1638+
1639+
auto OpenedF = RemappedFS->openFileForRead("a");
1640+
ASSERT_FALSE(OpenedF.getError());
1641+
llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1642+
ASSERT_FALSE(Name.getError());
1643+
EXPECT_EQ("a", Name.get());
1644+
1645+
auto OpenedS = (*OpenedF)->status();
1646+
ASSERT_FALSE(OpenedS.getError());
1647+
EXPECT_EQ("a", OpenedS->getName());
1648+
EXPECT_FALSE(OpenedS->IsVFSMapped);
1649+
1650+
auto DirectS = RemappedFS->status("a");
1651+
ASSERT_FALSE(DirectS.getError());
1652+
EXPECT_EQ("a", DirectS->getName());
1653+
EXPECT_FALSE(DirectS->IsVFSMapped);
1654+
1655+
EXPECT_EQ(0, NumDiagnostics);
1656+
}
1657+
1658+
TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {
1659+
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1660+
new vfs::InMemoryFileSystem);
1661+
BaseFS->addFile("//root/foo/realname", 0,
1662+
MemoryBuffer::getMemBuffer("contents of a"));
1663+
auto FS =
1664+
getFromYAMLString("{ 'use-external-names': true,\n"
1665+
" 'roots': [\n"
1666+
"{\n"
1667+
" 'type': 'directory',\n"
1668+
" 'name': '//root/foo',\n"
1669+
" 'contents': [ {\n"
1670+
" 'type': 'file',\n"
1671+
" 'name': 'vfsname',\n"
1672+
" 'external-contents': 'realname'\n"
1673+
" }\n"
1674+
" ]\n"
1675+
"}]}",
1676+
BaseFS);
1677+
ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1678+
1679+
auto OpenedF = FS->openFileForRead("vfsname");
1680+
ASSERT_FALSE(OpenedF.getError());
1681+
llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1682+
ASSERT_FALSE(Name.getError());
1683+
EXPECT_EQ("realname", Name.get());
1684+
1685+
auto OpenedS = (*OpenedF)->status();
1686+
ASSERT_FALSE(OpenedS.getError());
1687+
EXPECT_EQ("realname", OpenedS->getName());
1688+
EXPECT_TRUE(OpenedS->IsVFSMapped);
1689+
1690+
auto DirectS = FS->status("vfsname");
1691+
ASSERT_FALSE(DirectS.getError());
1692+
EXPECT_EQ("realname", DirectS->getName());
1693+
EXPECT_TRUE(DirectS->IsVFSMapped);
1694+
1695+
EXPECT_EQ(0, NumDiagnostics);
1696+
}
1697+
1698+
TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {
1699+
IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1700+
new vfs::InMemoryFileSystem);
1701+
BaseFS->addFile("//root/foo/realname", 0,
1702+
MemoryBuffer::getMemBuffer("contents of a"));
1703+
auto FS =
1704+
getFromYAMLString("{ 'use-external-names': false,\n"
1705+
" 'roots': [\n"
1706+
"{\n"
1707+
" 'type': 'directory',\n"
1708+
" 'name': '//root/foo',\n"
1709+
" 'contents': [ {\n"
1710+
" 'type': 'file',\n"
1711+
" 'name': 'vfsname',\n"
1712+
" 'external-contents': 'realname'\n"
1713+
" }\n"
1714+
" ]\n"
1715+
"}]}",
1716+
BaseFS);
1717+
ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1718+
1719+
auto OpenedF = FS->openFileForRead("vfsname");
1720+
ASSERT_FALSE(OpenedF.getError());
1721+
llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1722+
ASSERT_FALSE(Name.getError());
1723+
EXPECT_EQ("vfsname", Name.get());
1724+
1725+
auto OpenedS = (*OpenedF)->status();
1726+
ASSERT_FALSE(OpenedS.getError());
1727+
EXPECT_EQ("vfsname", OpenedS->getName());
1728+
EXPECT_TRUE(OpenedS->IsVFSMapped);
1729+
1730+
auto DirectS = FS->status("vfsname");
1731+
ASSERT_FALSE(DirectS.getError());
1732+
EXPECT_EQ("vfsname", DirectS->getName());
1733+
EXPECT_TRUE(DirectS->IsVFSMapped);
1734+
1735+
EXPECT_EQ(0, NumDiagnostics);
1736+
}
1737+
16301738
TEST_F(VFSFromYAMLTest, CaseInsensitive) {
16311739
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
16321740
Lower->addRegularFile("//root/foo/bar/a");

0 commit comments

Comments
 (0)