Skip to content

Commit 3fda0ed

Browse files
committed
[VFS] RedirectingFileSystem only replace path if not already mapped
If the `ExternalFS` has already remapped a path then the `RedirectingFileSystem` should not change it to the originally provided path. This fixes the original path always being used if multiple VFS overlays were provided and the path wasn't found in the highest (ie. first in the chain). This also renames `IsVFSMapped` to `ExposesExternalVFSPath` and only sets it if `UseExternalName` is true. This flag then represents that the `Status` has an external path that's different from its virtual path. Right now the contained path is still the external path, but further PRs will change this to *always* be the virtual path. Clients that need the external can then request it specifically. Note that even though `ExposesExternalVFSPath` isn't set for all VFS-mapped paths, `IsVFSMapped` was only being used by a hack in `FileManager` that was specific to module searching. In that case `UseExternalNames` is always `true` and so that hack still applies. Resolves rdar://90578880 and llvm-project#53306. Differential Revision: https://reviews.llvm.org/D122549
1 parent 4477500 commit 3fda0ed

File tree

5 files changed

+104
-38
lines changed

5 files changed

+104
-38
lines changed

clang/lib/Basic/FileManager.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,15 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
270270
// This occurs when one dir is symlinked to another, for example.
271271
FileEntry &UFE = UniqueRealFiles[Status.getUniqueID()];
272272

273+
// FIXME: This should just check `!Status.ExposesExternalVFSPath`, but the
274+
// else branch also ends up fixing up relative paths to be the actually
275+
// looked up absolute path. This isn't necessarily desired, but does seem to
276+
// be relied on in some clients.
273277
if (Status.getName() == Filename) {
274278
// The name matches. Set the FileEntry.
275279
NamedFileEnt->second = FileEntryRef::MapValue(UFE, DirInfo);
276280
} else {
277-
// Name mismatch. We need a redirect. First grab the actual entry we want
278-
// to return.
281+
// We need a redirect. First grab the actual entry we want to return.
279282
//
280283
// This redirection logic intentionally leaks the external name of a
281284
// redirected file that uses 'use-external-name' in \a
@@ -285,9 +288,11 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
285288
//
286289
// FIXME: This is pretty complicated. It's also inconsistent with how
287290
// "real" filesystems behave and confuses parts of clang expect to see the
288-
// name-as-accessed on the \a FileEntryRef. Maybe the returned \a
289-
// FileEntryRef::getName() could return the accessed name unmodified, but
290-
// make the external name available via a separate API.
291+
// name-as-accessed on the \a FileEntryRef. To remove this we should
292+
// implement the FIXME on `ExposesExternalVFSPath`, ie. update the
293+
// `FileEntryRef::getName()` path to *always* be the virtual path and have
294+
// clients request the external path only when required through a separate
295+
// API.
291296
auto &Redirection =
292297
*SeenFileEntries
293298
.insert({Status.getName(), FileEntryRef::MapValue(UFE, DirInfo)})
@@ -308,13 +313,16 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
308313
FileEntryRef ReturnedRef(*NamedFileEnt);
309314
if (UFE.isValid()) { // Already have an entry with this inode, return it.
310315

311-
// FIXME: this hack ensures that if we look up a file by a virtual path in
312-
// the VFS that the getDir() will have the virtual path, even if we found
313-
// the file by a 'real' path first. This is required in order to find a
314-
// module's structure when its headers/module map are mapped in the VFS.
315-
// We should remove this as soon as we can properly support a file having
316-
// multiple names.
317-
if (&DirInfo.getDirEntry() != UFE.Dir && Status.IsVFSMapped)
316+
// FIXME: This hack ensures that `getDir()` will use the path that was
317+
// used to lookup this file, even if we found a file by different path
318+
// first. This is required in order to find a module's structure when its
319+
// headers/module map are mapped in the VFS.
320+
//
321+
// This should be removed once `HeaderSearch` is updated to use `*Ref`s
322+
// *and* the redirection hack above is removed. The removal of the latter
323+
// is required since otherwise the ref will have the exposed external VFS
324+
// path still.
325+
if (&DirInfo.getDirEntry() != UFE.Dir && Status.ExposesExternalVFSPath)
318326
UFE.Dir = &DirInfo.getDirEntry();
319327

320328
// Always update LastRef to the last name by which a file was accessed.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: sed -e "s@NAME_DIR@%{/t:regex_replacement}/A@g" -e "s@EXTERNAL_DIR@%{/t:regex_replacement}/B@g" %t/vfs/base.yaml > %t/vfs/a-b.yaml
4+
// RUN: sed -e "s@NAME_DIR@%{/t:regex_replacement}/C@g" -e "s@EXTERNAL_DIR@%{/t:regex_replacement}/D@g" %t/vfs/base.yaml > %t/vfs/c-d.yaml
5+
6+
// Check that the external name is given when multiple overlays are provided
7+
8+
// RUN: %clang_cc1 -Werror -I %t/A -ivfsoverlay %t/vfs/c-d.yaml -ivfsoverlay %t/vfs/a-b.yaml -fsyntax-only -E -C %t/main.c 2>&1 | FileCheck --check-prefix=FROM_B %s
9+
// FROM_B: # 1 "{{.*(/|\\\\)B(/|\\\\)}}Header.h"
10+
// FROM_B: // Header.h in B
11+
12+
// RUN: %clang_cc1 -Werror -I %t/B -ivfsoverlay %t/vfs/c-d.yaml -ivfsoverlay %t/vfs/a-b.yaml -fsyntax-only -E -C %t/main.c 2>&1 | FileCheck --check-prefix=FROM_B %s
13+
14+
// RUN: %clang_cc1 -Werror -I %t/C -ivfsoverlay %t/vfs/c-d.yaml -ivfsoverlay %t/vfs/a-b.yaml -fsyntax-only -E -C %t/main.c 2>&1 | FileCheck --check-prefix=FROM_D %s
15+
// FROM_D: # 1 "{{.*(/|\\\\)D(/|\\\\)}}Header.h"
16+
// FROM_D: // Header.h in D
17+
18+
// RUN: %clang_cc1 -Werror -I %t/C -ivfsoverlay %t/vfs/c-d.yaml -ivfsoverlay %t/vfs/a-b.yaml -fsyntax-only -E -C %t/main.c 2>&1 | FileCheck --check-prefix=FROM_D %s
19+
20+
//--- main.c
21+
#include "Header.h"
22+
23+
//--- B/Header.h
24+
// Header.h in B
25+
26+
//--- D/Header.h
27+
// Header.h in D
28+
29+
//--- vfs/base.yaml
30+
{
31+
'version': 0,
32+
'redirecting-with': 'fallthrough',
33+
'roots': [
34+
{ 'name': 'NAME_DIR',
35+
'type': 'directory-remap',
36+
'external-contents': 'EXTERNAL_DIR'
37+
}
38+
]
39+
}

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,19 @@ class Status {
5555
llvm::sys::fs::perms Perms;
5656

5757
public:
58-
// FIXME: remove when files support multiple names
59-
bool IsVFSMapped = false;
58+
/// Whether this entity has an external path different from the virtual path,
59+
/// and the external path is exposed by leaking it through the abstraction.
60+
/// For example, a RedirectingFileSystem will set this for paths where
61+
/// UseExternalName is true.
62+
///
63+
/// FIXME: Currently the external path is exposed by replacing the virtual
64+
/// path in this Status object. Instead, we should leave the path in the
65+
/// Status intact (matching the requested virtual path), and just use this
66+
/// flag as hint that a new API, FileSystem::getExternalVFSPath(), will not
67+
/// return `None`. Clients can then request the external path only where
68+
/// needed (e.g. for diagnostics, or to report discovered dependencies to
69+
/// external tools).
70+
bool ExposesExternalVFSPath = false;
6071

6172
Status() = default;
6273
Status(const llvm::sys::fs::file_status &Status);

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,10 +2163,16 @@ RedirectingFileSystem::lookupPathImpl(
21632163
static Status getRedirectedFileStatus(const Twine &OriginalPath,
21642164
bool UseExternalNames,
21652165
Status ExternalStatus) {
2166+
// The path has been mapped by some nested VFS, don't override it with the
2167+
// original path.
2168+
if (ExternalStatus.ExposesExternalVFSPath)
2169+
return ExternalStatus;
2170+
21662171
Status S = ExternalStatus;
21672172
if (!UseExternalNames)
21682173
S = Status::copyWithNewName(S, OriginalPath);
2169-
S.IsVFSMapped = true;
2174+
else
2175+
S.ExposesExternalVFSPath = true;
21702176
return S;
21712177
}
21722178

@@ -2268,7 +2274,9 @@ class FileWithFixedStatus : public File {
22682274

22692275
ErrorOr<std::unique_ptr<File>>
22702276
File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2271-
if (!Result)
2277+
// See \c getRedirectedFileStatus - don't update path if it's already been
2278+
// mapped.
2279+
if (!Result || (*Result)->status()->ExposesExternalVFSPath)
22722280
return Result;
22732281

22742282
ErrorOr<std::unique_ptr<File>> F = std::move(*Result);

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,20 +1442,20 @@ TEST_F(VFSFromYAMLTest, MappedFiles) {
14421442
ErrorOr<vfs::Status> S = O->status("//root/file1");
14431443
ASSERT_FALSE(S.getError());
14441444
EXPECT_EQ("//root/foo/bar/a", S->getName());
1445-
EXPECT_TRUE(S->IsVFSMapped);
1445+
EXPECT_TRUE(S->ExposesExternalVFSPath);
14461446

14471447
ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
14481448
EXPECT_EQ("//root/foo/bar/a", SLower->getName());
14491449
EXPECT_TRUE(S->equivalent(*SLower));
1450-
EXPECT_FALSE(SLower->IsVFSMapped);
1450+
EXPECT_FALSE(SLower->ExposesExternalVFSPath);
14511451

14521452
// file after opening
14531453
auto OpenedF = O->openFileForRead("//root/file1");
14541454
ASSERT_FALSE(OpenedF.getError());
14551455
auto OpenedS = (*OpenedF)->status();
14561456
ASSERT_FALSE(OpenedS.getError());
14571457
EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1458-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1458+
EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
14591459

14601460
// directory
14611461
S = O->status("//root/");
@@ -1467,43 +1467,43 @@ TEST_F(VFSFromYAMLTest, MappedFiles) {
14671467
S = O->status("//root/mappeddir");
14681468
ASSERT_FALSE(S.getError());
14691469
EXPECT_TRUE(S->isDirectory());
1470-
EXPECT_TRUE(S->IsVFSMapped);
1470+
EXPECT_TRUE(S->ExposesExternalVFSPath);
14711471
EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));
14721472

14731473
SLower = O->status("//root/foo/bar");
14741474
EXPECT_EQ("//root/foo/bar", SLower->getName());
14751475
EXPECT_TRUE(S->equivalent(*SLower));
1476-
EXPECT_FALSE(SLower->IsVFSMapped);
1476+
EXPECT_FALSE(SLower->ExposesExternalVFSPath);
14771477

14781478
// file in remapped directory
14791479
S = O->status("//root/mappeddir/a");
14801480
ASSERT_FALSE(S.getError());
1481-
ASSERT_FALSE(S->isDirectory());
1482-
ASSERT_TRUE(S->IsVFSMapped);
1483-
ASSERT_EQ("//root/foo/bar/a", S->getName());
1481+
EXPECT_FALSE(S->isDirectory());
1482+
EXPECT_TRUE(S->ExposesExternalVFSPath);
1483+
EXPECT_EQ("//root/foo/bar/a", S->getName());
14841484

14851485
// file in remapped directory, with use-external-name=false
14861486
S = O->status("//root/mappeddir2/a");
14871487
ASSERT_FALSE(S.getError());
1488-
ASSERT_FALSE(S->isDirectory());
1489-
ASSERT_TRUE(S->IsVFSMapped);
1490-
ASSERT_EQ("//root/mappeddir2/a", S->getName());
1488+
EXPECT_FALSE(S->isDirectory());
1489+
EXPECT_FALSE(S->ExposesExternalVFSPath);
1490+
EXPECT_EQ("//root/mappeddir2/a", S->getName());
14911491

14921492
// file contents in remapped directory
14931493
OpenedF = O->openFileForRead("//root/mappeddir/a");
14941494
ASSERT_FALSE(OpenedF.getError());
14951495
OpenedS = (*OpenedF)->status();
14961496
ASSERT_FALSE(OpenedS.getError());
14971497
EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1498-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1498+
EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
14991499

15001500
// file contents in remapped directory, with use-external-name=false
15011501
OpenedF = O->openFileForRead("//root/mappeddir2/a");
15021502
ASSERT_FALSE(OpenedF.getError());
15031503
OpenedS = (*OpenedF)->status();
15041504
ASSERT_FALSE(OpenedS.getError());
15051505
EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());
1506-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1506+
EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
15071507

15081508
// broken mapping
15091509
EXPECT_EQ(O->status("//root/file2").getError(),
@@ -1535,20 +1535,20 @@ TEST_F(VFSFromYAMLTest, MappedRoot) {
15351535
ErrorOr<vfs::Status> S = O->status("//mappedroot/a");
15361536
ASSERT_FALSE(S.getError());
15371537
EXPECT_EQ("//root/foo/bar/a", S->getName());
1538-
EXPECT_TRUE(S->IsVFSMapped);
1538+
EXPECT_TRUE(S->ExposesExternalVFSPath);
15391539

15401540
ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
15411541
EXPECT_EQ("//root/foo/bar/a", SLower->getName());
15421542
EXPECT_TRUE(S->equivalent(*SLower));
1543-
EXPECT_FALSE(SLower->IsVFSMapped);
1543+
EXPECT_FALSE(SLower->ExposesExternalVFSPath);
15441544

15451545
// file after opening
15461546
auto OpenedF = O->openFileForRead("//mappedroot/a");
15471547
ASSERT_FALSE(OpenedF.getError());
15481548
auto OpenedS = (*OpenedF)->status();
15491549
ASSERT_FALSE(OpenedS.getError());
15501550
EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1551-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1551+
EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
15521552

15531553
EXPECT_EQ(0, NumDiagnostics);
15541554
}
@@ -1696,12 +1696,12 @@ TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {
16961696
auto OpenedS = (*OpenedF)->status();
16971697
ASSERT_FALSE(OpenedS.getError());
16981698
EXPECT_EQ("a", OpenedS->getName());
1699-
EXPECT_FALSE(OpenedS->IsVFSMapped);
1699+
EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
17001700

17011701
auto DirectS = RemappedFS->status("a");
17021702
ASSERT_FALSE(DirectS.getError());
17031703
EXPECT_EQ("a", DirectS->getName());
1704-
EXPECT_FALSE(DirectS->IsVFSMapped);
1704+
EXPECT_FALSE(DirectS->ExposesExternalVFSPath);
17051705

17061706
EXPECT_EQ(0, NumDiagnostics);
17071707
}
@@ -1736,12 +1736,12 @@ TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {
17361736
auto OpenedS = (*OpenedF)->status();
17371737
ASSERT_FALSE(OpenedS.getError());
17381738
EXPECT_EQ("realname", OpenedS->getName());
1739-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1739+
EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
17401740

17411741
auto DirectS = FS->status("vfsname");
17421742
ASSERT_FALSE(DirectS.getError());
17431743
EXPECT_EQ("realname", DirectS->getName());
1744-
EXPECT_TRUE(DirectS->IsVFSMapped);
1744+
EXPECT_TRUE(DirectS->ExposesExternalVFSPath);
17451745

17461746
EXPECT_EQ(0, NumDiagnostics);
17471747
}
@@ -1776,12 +1776,12 @@ TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {
17761776
auto OpenedS = (*OpenedF)->status();
17771777
ASSERT_FALSE(OpenedS.getError());
17781778
EXPECT_EQ("vfsname", OpenedS->getName());
1779-
EXPECT_TRUE(OpenedS->IsVFSMapped);
1779+
EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
17801780

17811781
auto DirectS = FS->status("vfsname");
17821782
ASSERT_FALSE(DirectS.getError());
17831783
EXPECT_EQ("vfsname", DirectS->getName());
1784-
EXPECT_TRUE(DirectS->IsVFSMapped);
1784+
EXPECT_FALSE(DirectS->ExposesExternalVFSPath);
17851785

17861786
EXPECT_EQ(0, NumDiagnostics);
17871787
}

0 commit comments

Comments
 (0)