Skip to content

Commit ff5f1b9

Browse files
authored
Merge pull request #41929 from bnbarham/ast-manager-use-overlays
[SourceKit] Make sure to use overlays when reading files
2 parents 81f49a8 + 15ada41 commit ff5f1b9

File tree

9 files changed

+221
-57
lines changed

9 files changed

+221
-57
lines changed

include/swift/AST/SearchPathOptions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "swift/Basic/ArrayRefView.h"
1717
#include "swift/Basic/PathRemapper.h"
1818
#include "llvm/ADT/Hashing.h"
19+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1920
#include "llvm/ADT/StringMap.h"
21+
#include "llvm/Support/Error.h"
2022
#include "llvm/Support/VirtualFileSystem.h"
2123

2224
#include <string>
@@ -357,6 +359,14 @@ class SearchPathOptions {
357359
return Lookup.searchPathsContainingFile(this, Filenames, FS, IsOSDarwin);
358360
}
359361

362+
/// Creates a filesystem taking into account any overlays specified in
363+
/// \c VFSOverlayFiles. Returns \p BaseFS if there were no overlays and
364+
/// \c FileError(s) if any error occurred while attempting to parse the
365+
/// overlay files.
366+
llvm::Expected<llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
367+
makeOverlayFileSystem(
368+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) const;
369+
360370
private:
361371
static StringRef
362372
pathStringFromFrameworkSearchPath(const FrameworkSearchPath &next) {

include/swift/IDE/CompletionInstance.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class CompletionInstance {
109109
bool performCachedOperationIfPossible(
110110
llvm::hash_code ArgsHash,
111111
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
112+
const SearchPathOptions &SearchPathOpts,
112113
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
113114
DiagnosticConsumer *DiagC,
114115
std::shared_ptr<std::atomic<bool>> CancellationFlag,

lib/AST/SearchPathOptions.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/AST/SearchPathOptions.h"
1414
#include "llvm/ADT/SmallSet.h"
15+
#include "llvm/Support/Errc.h"
1516

1617
using namespace swift;
1718

@@ -114,3 +115,47 @@ ModuleSearchPathLookup::searchPathsContainingFile(
114115
const ModuleSearchPath *Rhs) { return *Lhs < *Rhs; });
115116
return Result;
116117
}
118+
119+
/// Loads a VFS YAML file located at \p File using \p BaseFS and adds it to
120+
/// \p OverlayFS. Returns an error if either loading the \p File failed or it
121+
/// is invalid.
122+
static llvm::Error loadAndValidateVFSOverlay(
123+
const std::string &File,
124+
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &BaseFS,
125+
const llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> &OverlayFS) {
126+
auto Buffer = BaseFS->getBufferForFile(File);
127+
if (!Buffer)
128+
return llvm::createFileError(File, Buffer.getError());
129+
130+
auto VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, File);
131+
if (!VFS)
132+
return llvm::createFileError(File, llvm::errc::invalid_argument);
133+
134+
OverlayFS->pushOverlay(std::move(VFS));
135+
return llvm::Error::success();
136+
}
137+
138+
llvm::Expected<llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
139+
SearchPathOptions::makeOverlayFileSystem(
140+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) const {
141+
// TODO: This implementation is different to how Clang reads overlays in.
142+
// Expose a helper in Clang rather than doing this ourselves.
143+
144+
auto OverlayFS =
145+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
146+
147+
llvm::Error AllErrors = llvm::Error::success();
148+
bool hasOverlays = false;
149+
for (const auto &File : VFSOverlayFiles) {
150+
hasOverlays = true;
151+
if (auto Err = loadAndValidateVFSOverlay(File, BaseFS, OverlayFS))
152+
AllErrors = llvm::joinErrors(std::move(AllErrors), std::move(Err));
153+
}
154+
155+
if (AllErrors)
156+
return std::move(AllErrors);
157+
158+
if (hasOverlays)
159+
return OverlayFS;
160+
return BaseFS;
161+
}

lib/Frontend/Frontend.cpp

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -405,49 +405,28 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok,
405405
return false;
406406
}
407407

408-
static bool loadAndValidateVFSOverlay(
409-
const std::string &File,
410-
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &BaseFS,
411-
const llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> &OverlayFS,
412-
DiagnosticEngine &Diag) {
413-
auto Buffer = BaseFS->getBufferForFile(File);
414-
if (!Buffer) {
415-
Diag.diagnose(SourceLoc(), diag::cannot_open_file, File,
416-
Buffer.getError().message());
408+
bool CompilerInstance::setUpVirtualFileSystemOverlays() {
409+
auto ExpectedOverlay =
410+
Invocation.getSearchPathOptions().makeOverlayFileSystem(
411+
SourceMgr.getFileSystem());
412+
if (!ExpectedOverlay) {
413+
llvm::handleAllErrors(
414+
ExpectedOverlay.takeError(), [&](const llvm::FileError &FE) {
415+
if (FE.convertToErrorCode() == std::errc::no_such_file_or_directory) {
416+
Diagnostics.diagnose(SourceLoc(), diag::cannot_open_file,
417+
FE.getFileName(), FE.messageWithoutFileInfo());
418+
} else {
419+
Diagnostics.diagnose(SourceLoc(), diag::invalid_vfs_overlay_file,
420+
FE.getFileName());
421+
}
422+
});
417423
return true;
418424
}
419425

420-
auto VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()),
421-
nullptr, File);
422-
if (!VFS) {
423-
Diag.diagnose(SourceLoc(), diag::invalid_vfs_overlay_file, File);
424-
return true;
425-
}
426-
OverlayFS->pushOverlay(std::move(VFS));
426+
SourceMgr.setFileSystem(*ExpectedOverlay);
427427
return false;
428428
}
429429

430-
bool CompilerInstance::setUpVirtualFileSystemOverlays() {
431-
auto BaseFS = SourceMgr.getFileSystem();
432-
auto OverlayFS = llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>(
433-
new llvm::vfs::OverlayFileSystem(BaseFS));
434-
bool hadAnyFailure = false;
435-
bool hasOverlays = false;
436-
for (const auto &File : Invocation.getSearchPathOptions().VFSOverlayFiles) {
437-
hasOverlays = true;
438-
hadAnyFailure |=
439-
loadAndValidateVFSOverlay(File, BaseFS, OverlayFS, Diagnostics);
440-
}
441-
442-
// If we successfully loaded all the overlays, let the source manager and
443-
// diagnostic engine take advantage of the overlay file system.
444-
if (!hadAnyFailure && hasOverlays) {
445-
SourceMgr.setFileSystem(OverlayFS);
446-
}
447-
448-
return hadAnyFailure;
449-
}
450-
451430
void CompilerInstance::setUpLLVMArguments() {
452431
// Honor -Xllvm.
453432
if (!Invocation.getFrontendOptions().LLVMArgs.empty()) {

lib/IDE/CompletionInstance.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC,
177177
bool CompletionInstance::performCachedOperationIfPossible(
178178
llvm::hash_code ArgsHash,
179179
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
180+
const SearchPathOptions &SearchPathOpts,
180181
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
181182
DiagnosticConsumer *DiagC,
182183
std::shared_ptr<std::atomic<bool>> CancellationFlag,
@@ -210,6 +211,16 @@ bool CompletionInstance::performCachedOperationIfPossible(
210211
return false;
211212

212213
if (shouldCheckDependencies()) {
214+
// The passed in FileSystem does not have any overlays resolved. Make sure
215+
// to do so before checking dependencies (otherwise we might decide we need
216+
// to run the slow path due to a missing/different file).
217+
auto ExpectedOverlay = SearchPathOpts.makeOverlayFileSystem(FileSystem);
218+
if (ExpectedOverlay) {
219+
FileSystem = std::move(ExpectedOverlay.get());
220+
} else {
221+
llvm::consumeError(ExpectedOverlay.takeError());
222+
}
223+
213224
if (areAnyDependentFilesInvalidated(
214225
CI, *FileSystem, *oldSF->getBufferID(),
215226
DependencyCheckedTimestamp, InMemoryDependencyHash))
@@ -543,9 +554,10 @@ void swift::ide::CompletionInstance::performOperation(
543554
// the cached completion instance.
544555
std::lock_guard<std::mutex> lock(mtx);
545556

546-
if (performCachedOperationIfPossible(ArgsHash, FileSystem, completionBuffer,
547-
Offset, DiagC, CancellationFlag,
548-
Callback)) {
557+
if (performCachedOperationIfPossible(ArgsHash, FileSystem,
558+
Invocation.getSearchPathOptions(),
559+
completionBuffer, Offset, DiagC,
560+
CancellationFlag, Callback)) {
549561
// We were able to reuse a cached AST. Callback has already been invoked
550562
// and we don't need to build a new AST. We are done.
551563
return;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %empty-directory(%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/base.yaml > %t/overlay.yaml
4+
5+
// RUN: %sourcekitd-test \
6+
// RUN: -req=global-config -req-opts=completion_check_dependency_interval=0 == \
7+
// RUN: -req=complete -pos=2:5 %t/A/a.swift -- -vfsoverlay %t/overlay.yaml %t/A/a.swift %t/A/b.swift == \
8+
// RUN: -req=complete -pos=2:5 %t/A/a.swift -- -vfsoverlay %t/overlay.yaml %t/A/a.swift %t/A/b.swift \
9+
// RUN: | %FileCheck %s
10+
11+
// CHECK-LABEL: key.results: [
12+
// CHECK-DAG: key.name: "method()"
13+
// CHECK: ]
14+
// CHECK-NOT: key.reusingastcontext: 1
15+
16+
// CHECK-LABEL: key.results: [
17+
// CHECK-DAG: key.name: "method()"
18+
// CHECK: ]
19+
// CHECK: key.reusingastcontext: 1
20+
21+
22+
//--- A/a.swift
23+
func a(b: B) {
24+
b.
25+
}
26+
27+
//--- B/b.swift
28+
struct B {
29+
func method() {}
30+
}
31+
32+
//--- base.yaml
33+
{
34+
version: 0,
35+
redirecting-with: "fallback",
36+
use-external-names: true,
37+
roots: [
38+
{
39+
type: "directory-remap",
40+
name: "NAME_DIR",
41+
external-contents: "EXTERNAL_DIR"
42+
}
43+
]
44+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file --leading-lines %s %t
3+
// RUN: sed -e "s@NAME_DIR@%{/t:regex_replacement}/A@g" -e "s@EXTERNAL_DIR@%{/t:regex_replacement}/B@g" %t/base.yaml > %t/overlay.yaml
4+
5+
//--- A/a.swift
6+
func a() {
7+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):3 %t/A/a.swift -- -vfsoverlay %t/overlay.yaml %t/A/a.swift %t/A/b.swift | %FileCheck %s
8+
b()
9+
}
10+
11+
//--- B/b.swift
12+
// TODO: This should be B/b.swift, but there's currently a bug with multiple overlays.
13+
// See rdar://90578880 or https://github.com/llvm/llvm-project/issues/53306.
14+
// Replace with CHECK-FIXED when that's fixed.
15+
// CHECK: source.lang.swift.ref.function.free ({{.*}}{{/|\\}}A{{/|\\}}b.swift:*missing file*)
16+
// CHECK-FIXED: source.lang.swift.ref.function.free ({{.*}}{{/|\\}}B{{/|\\}}b.swift:[[@LINE+1]]:6-[[@LINE+1]]:9)
17+
func b() {}
18+
19+
//--- base.yaml
20+
{
21+
version: 0,
22+
redirecting-with: "fallback",
23+
use-external-names: true,
24+
roots: [
25+
{
26+
type: "directory-remap",
27+
name: "NAME_DIR",
28+
external-contents: "EXTERNAL_DIR"
29+
}
30+
]
31+
}

tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,15 +1208,28 @@ void ASTProducer::enqueueConsumer(
12081208
// this synchronously since all results will be delivered async anyway.
12091209
auto This = shared_from_this();
12101210
BuildOperationsQueue.dispatch([Consumer, FileSystem, Mgr, This]() {
1211+
// The passed in filesystem does not have overlays resolved. Make sure to
1212+
// do so before performing any file operations.
1213+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = FileSystem;
1214+
const InvocationOptions &InvocOpts = This->InvokRef->Impl.Opts;
1215+
const CompilerInvocation &ActualInvoc = InvocOpts.Invok;
1216+
auto ExpectedOverlay =
1217+
ActualInvoc.getSearchPathOptions().makeOverlayFileSystem(FileSystem);
1218+
if (ExpectedOverlay) {
1219+
FS = std::move(ExpectedOverlay.get());
1220+
} else {
1221+
llvm::consumeError(ExpectedOverlay.takeError());
1222+
}
1223+
12111224
if (auto BuildOp =
1212-
This->getBuildOperationForConsumer(Consumer, FileSystem, Mgr)) {
1225+
This->getBuildOperationForConsumer(Consumer, FS, Mgr)) {
12131226
bool WasAdded = BuildOp->addConsumer(Consumer);
12141227
if (!WasAdded) {
12151228
// The build operation was cancelled after the call to
12161229
// getBuildOperationForConsumer but before the consumer could be
12171230
// added. This should be an absolute edge case. Let's just try
12181231
// again.
1219-
This->enqueueConsumer(Consumer, FileSystem, Mgr);
1232+
This->enqueueConsumer(Consumer, FS, Mgr);
12201233
}
12211234
} else {
12221235
auto WeakThis = std::weak_ptr<ASTProducer>(This);
@@ -1229,8 +1242,9 @@ void ASTProducer::enqueueConsumer(
12291242
Mgr->Impl.ASTCache.set(This->InvokRef->Impl.Key, This);
12301243
}
12311244
};
1245+
12321246
ASTBuildOperationRef NewBuildOp = std::make_shared<ASTBuildOperation>(
1233-
FileSystem, This->InvokRef, Mgr, DidFinishCallback);
1247+
FS, This->InvokRef, Mgr, DidFinishCallback);
12341248
This->BuildOperations.push_back(NewBuildOp);
12351249
bool WasAdded = NewBuildOp->addConsumer(Consumer);
12361250
assert(WasAdded && "Consumer wasn't added to a new build operation "

0 commit comments

Comments
 (0)