From 05a87e86c4fe2c0e903f5275127f39e8c78e0d56 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 28 Apr 2020 18:34:44 -0700 Subject: [PATCH 1/4] [CodeCompletion] Give up fast-completion if dependent files are modified Check if dependencies are modified since the last checking. Dependencies: - Other source files in the current module - Dependent files collected by the dependency tracker When: - If the last dependency check was over N (defaults to 5) seconds ago Invalidate if: - The dependency file is missing - The modification time of the dependecy is greater than the last check - If the modification time is zero, compare the content using the file system from the previous completion and the current completion rdar://problem/62336432 --- include/swift/IDE/CompletionInstance.h | 11 ++ lib/IDE/CompletionInstance.cpp | 129 ++++++++++++++++-- .../ClangFW.framework/Headers/ClangFW.h | 2 + .../ClangFW.framework/Headers/Funcs.h | 4 + .../Modules/module.modulemap | 4 + .../ClangFW.framework_mod/Headers/Funcs.h | 5 + .../Inputs/checkdeps/MyProject/Bridging.h | 1 + .../Inputs/checkdeps/MyProject/Library.swift | 1 + .../Inputs/checkdeps/MyProject/LocalCFunc.h | 6 + .../checkdeps/MyProject_mod/Library.swift | 1 + .../checkdeps/MyProject_mod/LocalCFunc.h | 7 + .../Inputs/checkdeps/SwiftFW_src/Funcs.swift | 2 + .../checkdeps/SwiftFW_src_mod/Funcs.swift | 1 + .../CodeComplete/Inputs/checkdeps/test.swift | 5 + .../complete_checkdeps_bridged.swift | 72 ++++++++++ .../complete_checkdeps_clangmodule.swift | 72 ++++++++++ .../complete_checkdeps_otherfile.swift | 71 ++++++++++ .../complete_checkdeps_swiftmodule.swift | 71 ++++++++++ .../include/SourceKit/Core/Context.h | 7 +- .../include/SourceKit/Core/LangSupport.h | 3 + tools/SourceKit/lib/Core/Context.cpp | 9 +- .../lib/SwiftLang/SwiftLangSupport.cpp | 15 +- .../lib/SwiftLang/SwiftLangSupport.h | 2 + .../tools/sourcekitd-test/Options.td | 5 + .../tools/sourcekitd-test/TestOptions.cpp | 13 ++ .../tools/sourcekitd-test/TestOptions.h | 1 + .../tools/sourcekitd-test/sourcekitd-test.cpp | 9 +- .../tools/sourcekitd/lib/API/Requests.cpp | 14 +- utils/gyb_sourcekit_support/UIDs.py | 2 + 29 files changed, 527 insertions(+), 18 deletions(-) create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index b928fa997e79c..1684d0e2d6cce 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -18,6 +18,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/VirtualFileSystem.h" @@ -38,13 +39,20 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, /// Manages \c CompilerInstance for completion like operations. class CompletionInstance { unsigned MaxASTReuseCount = 100; + unsigned DependencyCheckIntervalSecond = 5; std::mutex mtx; std::unique_ptr CachedCI; llvm::hash_code CachedArgHash; + llvm::sys::TimePoint<> DependencyCheckedTimestamp; unsigned CachedReuseCount = 0; + void cacheCompilerInstance(std::unique_ptr CI, + llvm::hash_code ArgsHash); + + bool shouldCheckDependencies() const; + /// Calls \p Callback with cached \c CompilerInstance if it's usable for the /// specified completion request. /// Returns \c if the callback was called. Returns \c false if the compiler @@ -52,6 +60,7 @@ class CompletionInstance { /// in function bodies, or the interface hash of the file has changed. bool performCachedOperationIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC, llvm::function_ref Callback); @@ -69,6 +78,8 @@ class CompletionInstance { llvm::function_ref Callback); public: + void setDependencyCheckIntervalSecond(unsigned Value); + /// Calls \p Callback with a \c CompilerInstance which is prepared for the /// second pass. \p Callback is resposible to perform the second pass on it. /// The \c CompilerInstance may be reused from the previous completions, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index cb3f8c965b577..0bbf166bd014c 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -18,6 +18,8 @@ #include "swift/AST/Module.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SourceFile.h" +#include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/ClangImporter/ClangModule.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/SourceManager.h" @@ -28,6 +30,7 @@ #include "swift/Subsystems.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/MemoryBuffer.h" +#include "clang/AST/ASTContext.h" using namespace swift; using namespace ide; @@ -162,10 +165,77 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, return newDC; } +/// Check if any dependent files are modified since \p timestamp. +bool areAnyDependentFilesInvalidated(CompilerInstance &CI, + llvm::vfs::FileSystem &FS, + StringRef currentFileName, + llvm::sys::TimePoint<> timestamp) { + + auto isInvalidated = [&](StringRef filePath) -> bool { + auto stat = FS.status(filePath); + if (!stat) + // Missing. + return true; + + auto lastModTime = stat->getLastModificationTime(); + if (lastModTime > timestamp) + // Modified. + return true; + + // If the last modification time is zero, this file is probably from a + // virtual file system. We need to check the content. + if (lastModTime == llvm::sys::TimePoint<>()) { + if (&CI.getFileSystem() == &FS) + return false; + + auto oldContent = CI.getFileSystem().getBufferForFile(filePath); + auto newContent = FS.getBufferForFile(filePath); + if (!oldContent || !newContent) + // (unreachable?) + return true; + + if (oldContent.get()->getBuffer() != newContent.get()->getBuffer()) + // Different content. + return true; + } + + return false; + }; + + // Check files in the current module. + for (FileUnit *file : CI.getMainModule()->getFiles()) { + StringRef filename; + if (auto SF = dyn_cast(file)) + filename = SF->getFilename(); + else if (auto LF = dyn_cast(file)) + filename = LF->getFilename(); + else + continue; + + // Ignore the current file and synthesized files. + if (filename.empty() || filename.front() == '<' || + filename.equals(currentFileName)) + continue; + + if (isInvalidated(filename)) + return true; + } + + // Check other non-system depenencies (e.g. modules, headers). + for (auto &dep : CI.getDependencyTracker()->getDependencies()) { + if (isInvalidated(dep)) + return true; + } + + // All loaded module files are not modified since the timestamp. + return false; +} + } // namespace bool CompletionInstance::performCachedOperationIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { @@ -187,10 +257,17 @@ bool CompletionInstance::performCachedOperationIfPossible( auto &oldInfo = oldState->getCodeCompletionDelayedDeclState(); auto &SM = CI.getSourceMgr(); - if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != - completionBuffer->getBufferIdentifier()) + auto bufferName = completionBuffer->getBufferIdentifier(); + if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != bufferName) return false; + if (shouldCheckDependencies()) { + if (areAnyDependentFilesInvalidated(CI, *FileSystem, bufferName, + DependencyCheckedTimestamp)) + return false; + DependencyCheckedTimestamp = std::chrono::system_clock::now(); + } + // Parse the new buffer into temporary SourceFile. SourceManager tmpSM; auto tmpBufferID = tmpSM.addMemBufferCopy(completionBuffer); @@ -263,8 +340,7 @@ bool CompletionInstance::performCachedOperationIfPossible( completionBuffer->getBuffer().slice(startOffset, endOffset); auto newOffset = Offset - startOffset; - newBufferID = SM.addMemBufferCopy(sourceText, - completionBuffer->getBufferIdentifier()); + newBufferID = SM.addMemBufferCopy(sourceText, bufferName); SM.openVirtualFile(SM.getLocForBufferStart(newBufferID), tmpSM.getDisplayNameForLoc(startLoc), tmpSM.getLineAndColumn(startLoc).first - 1); @@ -310,8 +386,7 @@ bool CompletionInstance::performCachedOperationIfPossible( endOffset = tmpSM.getLocOffsetInBuffer(endLoc, tmpBufferID); sourceText = sourceText.slice(0, endOffset); } - newBufferID = SM.addMemBufferCopy(sourceText, - completionBuffer->getBufferIdentifier()); + newBufferID = SM.addMemBufferCopy(sourceText, bufferName); SM.setCodeCompletionPoint(newBufferID, Offset); // Create a new module and a source file using the current AST context. @@ -369,7 +444,15 @@ bool CompletionInstance::performNewOperation( llvm::function_ref Callback) { llvm::PrettyStackTraceString trace("While performing new completion"); + auto isCachedCompletionRequested = ArgsHash.hasValue(); + auto TheInstance = std::make_unique(); + + // Track dependencies in fast-completion mode to invalidate the compiler + // instance if any dependent files are modified. + if (isCachedCompletionRequested) + TheInstance->createDependencyTracker(false); + { auto &CI = *TheInstance; if (DiagC) @@ -407,15 +490,34 @@ bool CompletionInstance::performNewOperation( Callback(CI, /*reusingASTContext=*/false); } - if (ArgsHash.hasValue()) { - CachedCI = std::move(TheInstance); - CachedArgHash = *ArgsHash; - CachedReuseCount = 0; - } + // Cache the compiler instance if fast completion is enabled. + if (isCachedCompletionRequested) + cacheCompilerInstance(std::move(TheInstance), *ArgsHash); return true; } +void CompletionInstance::cacheCompilerInstance( + std::unique_ptr CI, llvm::hash_code ArgsHash) { + CachedCI = std::move(CI); + CachedArgHash = ArgsHash; + auto now = std::chrono::system_clock::now(); + DependencyCheckedTimestamp = now; + CachedReuseCount = 0; +} + +bool CompletionInstance::shouldCheckDependencies() const { + assert(CachedCI); + using namespace std::chrono; + auto now = system_clock::now(); + return DependencyCheckedTimestamp + seconds(DependencyCheckIntervalSecond) < now; +} + +void CompletionInstance::setDependencyCheckIntervalSecond(unsigned Value) { + std::lock_guard lock(mtx); + DependencyCheckIntervalSecond = Value; +} + bool swift::ide::CompletionInstance::performOperation( swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, @@ -451,8 +553,9 @@ bool swift::ide::CompletionInstance::performOperation( // the cached completion instance. std::lock_guard lock(mtx); - if (performCachedOperationIfPossible(Invocation, ArgsHash, completionBuffer, - Offset, DiagC, Callback)) + if (performCachedOperationIfPossible(Invocation, ArgsHash, FileSystem, + completionBuffer, Offset, DiagC, + Callback)) return true; if (performNewOperation(ArgsHash, Invocation, FileSystem, completionBuffer, diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h new file mode 100644 index 0000000000000..3eab67881eee9 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h @@ -0,0 +1,2 @@ + +#import diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h new file mode 100644 index 0000000000000..d2a3d166d5c15 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h @@ -0,0 +1,4 @@ +#ifndef CLANGFW_FUNCS_H +#define CLANGFW_FUNCS_H +int clangFWFunc(); +#endif diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..c1720d160f034 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap @@ -0,0 +1,4 @@ +framework module ClangFW { + umbrella header "ClangFW.h" + export * +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h new file mode 100644 index 0000000000000..09c4450451ecd --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h @@ -0,0 +1,5 @@ +#ifndef CLANGFW_FUNCS_H +#define CLANGFW_FUNCS_H +int clangFWFunc_mod(); +#endif + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h new file mode 100644 index 0000000000000..aebbbe5cf5564 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h @@ -0,0 +1 @@ +#import "LocalCFunc.h" diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift new file mode 100644 index 0000000000000..c19b0db61d800 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift @@ -0,0 +1 @@ +func localSwiftFunc() -> Int {} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h new file mode 100644 index 0000000000000..12daa10870eaf --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h @@ -0,0 +1,6 @@ +#ifndef MYPROJECT_LOCALCFUNC_H +#define MYPROJECT_LOCALCFUNC_H + +int localClangFunc(); + +#endif diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift new file mode 100644 index 0000000000000..cb6af344cf1ee --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift @@ -0,0 +1 @@ +func localSwiftFunc_mod() -> Int {} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h new file mode 100644 index 0000000000000..65ee9fd3abb10 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h @@ -0,0 +1,7 @@ +#ifndef MYPROJECT_LOCALCFUNC_H +#define MYPROJECT_LOCALCFUNC_H + +int localClangFunc_mod(); + +#endif + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift new file mode 100644 index 0000000000000..6ff2c2e73ee0f --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift @@ -0,0 +1,2 @@ +public func swiftFWFunc() -> Int { return 1 } + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift new file mode 100644 index 0000000000000..98800199b2574 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift @@ -0,0 +1 @@ +public func swiftFWFunc_mod() -> Int { return 1 } diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift new file mode 100644 index 0000000000000..54e03f90fdef0 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift @@ -0,0 +1,5 @@ +import Foundation +import ClangFW // < 500 headers framework +func foo() { + /* HERE */ +} diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift new file mode 100644 index 0000000000000..47d6b365682f2 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift @@ -0,0 +1,72 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify bridging header library file' == \ +// RUN: -shell -- cp -R $INPUT_DIR/MyProject_mod/LocalCFunc.h %t/MyProject/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify bridging header library file +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc_mod()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc_mod()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift new file mode 100644 index 0000000000000..2c691f2408532 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift @@ -0,0 +1,72 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify framework (c)' == \ +// RUN: -shell -- cp -R $INPUT_DIR/ClangFW.framework_mod/* %t/Frameworks/ClangFW.framework/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify framework (c) +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc_mod()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc_mod()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift new file mode 100644 index 0000000000000..b87dcc0b8e561 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift @@ -0,0 +1,71 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo "### Modify local library file" == \ +// RUN: -shell -- cp -R $INPUT_DIR/MyProject_mod/Library.swift %t/MyProject/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify local library file +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc_mod()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc_mod()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift new file mode 100644 index 0000000000000..ce8ae5367b754 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift @@ -0,0 +1,71 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify framework (s)' == \ +// RUN: -shell -- %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src_mod/Funcs.swift == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify framework (s) +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc_mod()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc_mod()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/tools/SourceKit/include/SourceKit/Core/Context.h b/tools/SourceKit/include/SourceKit/Core/Context.h index 056e727fe7e2e..cbfc287320924 100644 --- a/tools/SourceKit/include/SourceKit/Core/Context.h +++ b/tools/SourceKit/include/SourceKit/Core/Context.h @@ -36,6 +36,9 @@ class GlobalConfig { /// /// At the time of writing this just means ignoring .swiftsourceinfo files. bool OptimizeForIDE = false; + + /// Interval second for checking dependencies in fast code completion. + unsigned CompletionCheckDependencyInterval = 5; }; private: @@ -43,8 +46,10 @@ class GlobalConfig { mutable llvm::sys::Mutex Mtx; public: - Settings update(Optional OptimizeForIDE); + Settings update(Optional OptimizeForIDE, + Optional CompletionCheckDependencyInterval); bool shouldOptimizeForIDE() const; + unsigned getCompletionCheckDependencyInterval() const; }; class Context { diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index e7c5707da4305..9415cd5ef9896 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -37,6 +37,7 @@ class SourceFileSyntax; } // namespace swift namespace SourceKit { +class GlobalConfig; struct EntityInfo { UIdent Kind; @@ -651,6 +652,8 @@ class LangSupport { virtual ~LangSupport() { } + virtual void globalConfigurationUpdated(std::shared_ptr Config) {}; + virtual void indexSource(StringRef Filename, IndexingConsumer &Consumer, ArrayRef Args) = 0; diff --git a/tools/SourceKit/lib/Core/Context.cpp b/tools/SourceKit/lib/Core/Context.cpp index 65a245283bd46..d93c510a85f7a 100644 --- a/tools/SourceKit/lib/Core/Context.cpp +++ b/tools/SourceKit/lib/Core/Context.cpp @@ -17,10 +17,13 @@ using namespace SourceKit; GlobalConfig::Settings -GlobalConfig::update(Optional OptimizeForIDE) { +GlobalConfig::update(Optional OptimizeForIDE, + Optional CompletionCheckDependencyInterval) { llvm::sys::ScopedLock L(Mtx); if (OptimizeForIDE.hasValue()) State.OptimizeForIDE = *OptimizeForIDE; + if (CompletionCheckDependencyInterval.hasValue()) + State.CompletionCheckDependencyInterval = *CompletionCheckDependencyInterval; return State; }; @@ -28,6 +31,10 @@ bool GlobalConfig::shouldOptimizeForIDE() const { llvm::sys::ScopedLock L(Mtx); return State.OptimizeForIDE; } +unsigned GlobalConfig::getCompletionCheckDependencyInterval() const { + llvm::sys::ScopedLock L(Mtx); + return State.CompletionCheckDependencyInterval; +} SourceKit::Context::Context(StringRef RuntimeLibPath, llvm::function_ref(Context &)> diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index fcb4b4c5eadea..373306076dbaf 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -257,6 +257,13 @@ class InMemoryFileSystemProvider: public SourceKit::FileSystemProvider { }; } +static void +configureCompletionInstance(std::shared_ptr CompletionInst, + std::shared_ptr GlobalConfig) { + CompletionInst->setDependencyCheckIntervalSecond( + GlobalConfig->getCompletionCheckDependencyInterval()); +} + SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) : NotificationCtr(SKCtx.getNotificationCenter()), CCCache(new SwiftCompletionCache) { @@ -270,7 +277,8 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SKCtx.getGlobalConfiguration(), Stats, RuntimeResourcePath); - CompletionInst = std::make_unique(); + CompletionInst = std::make_shared(); + configureCompletionInstance(CompletionInst, SKCtx.getGlobalConfiguration()); // By default, just use the in-memory cache. CCCache->inMemory = std::make_unique(); @@ -282,6 +290,11 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SwiftLangSupport::~SwiftLangSupport() { } +void SwiftLangSupport::globalConfigurationUpdated( + std::shared_ptr Config) { + configureCompletionInstance(CompletionInst, Config); +} + UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) { return UIdentVisitor(IsRef).visit(const_cast(D)); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 06cba4f8b89d9..b29e59a6b665c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -450,6 +450,8 @@ class SwiftLangSupport : public LangSupport { // LangSupport Interface //==========================================================================// + void globalConfigurationUpdated(std::shared_ptr Config) override; + void indexSource(StringRef Filename, IndexingConsumer &Consumer, ArrayRef Args) override; diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index 48b9d2101ecfa..5119a2e169470 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -143,6 +143,11 @@ def vfs_name : Separate<["-"], "vfs-name">, def optimize_for_ide : Joined<["-"], "for-ide=">, HelpText<"Value for the OptimizeForIde global configuration setting">; +def completion_check_dependency_interval : Separate<["-"], "completion-check-dependency-interval">, + HelpText<"Inteval seconds for checking dependencies in fast completion">; +def completion_check_dependency_interval_EQ : Joined<["-"], "completion-check-dependency-interval=">, + Alias; + def suppress_config_request : Flag<["-"], "suppress-config-request">, HelpText<"Suppress the default global configuration request, that is otherwise sent before any other request (except for the global-config request itself)">; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index 48330f4ac9080..ee577379698a8 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -381,6 +381,19 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { break; } + case OPT_completion_check_dependency_interval: { + int64_t Value; + if (StringRef(InputArg->getValue()).getAsInteger(10, Value)) { + llvm::errs() << "error: expected number for inteval\n"; + return true; + } else if (Value < 0) { + llvm::errs() << "error: completion-check-dependency-interval must be > 0\n"; + return true; + } + CompletionCheckDependencyInterval = Value; + break; + } + case OPT_suppress_config_request: SuppressDefaultConfigRequest = true; break; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index 7551ab48afee5..a0c6769bca1c0 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -114,6 +114,7 @@ struct TestOptions { bool timeRequest = false; llvm::Optional OptimizeForIde; bool SuppressDefaultConfigRequest = false; + llvm::Optional CompletionCheckDependencyInterval; unsigned repeatRequest = 1; struct VFSFile { std::string path; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 748a4d4e072b8..5bbc258a859b4 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -548,7 +548,14 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::GlobalConfiguration: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); if (Opts.OptimizeForIde.hasValue()) - sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast(Opts.OptimizeForIde.getValue())); + sourcekitd_request_dictionary_set_int64( + Req, KeyOptimizeForIDE, + static_cast(Opts.OptimizeForIde.getValue())); + if (Opts.CompletionCheckDependencyInterval.hasValue()) + sourcekitd_request_dictionary_set_int64( + Req, KeyCompletionCheckDependencyInterval, + static_cast( + Opts.CompletionCheckDependencyInterval.getValue())); break; case SourceKitRequest::ProtocolVersion: diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 66d465e0ffbef..8825adb0e351a 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -438,9 +438,21 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { if (!Req.getInt64(KeyOptimizeForIDE, EditorMode, true)) { OptimizeForIDE = EditorMode; } + Optional CompletionCheckDependencyInterval; + int64_t IntervalValue = 0; + if (!Req.getInt64(KeyCompletionCheckDependencyInterval, + IntervalValue, /*isOptional=*/true)) + CompletionCheckDependencyInterval = IntervalValue; + + GlobalConfig::Settings UpdatedConfig = Config->update( + OptimizeForIDE, CompletionCheckDependencyInterval); + + getGlobalContext().getSwiftLangSupport().globalConfigurationUpdated(Config); - GlobalConfig::Settings UpdatedConfig = Config->update(OptimizeForIDE); dict.set(KeyOptimizeForIDE, UpdatedConfig.OptimizeForIDE); + dict.set(KeyCompletionCheckDependencyInterval, + UpdatedConfig.CompletionCheckDependencyInterval); + return Rec(RB.createResponse()); } if (ReqUID == RequestProtocolVersion) { diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 9bd7cb711ac86..d69e00ff7c62c 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -179,6 +179,8 @@ def __init__(self, internal_name, external_name): KEY('OptimizeForIDE', 'key.optimize_for_ide'), KEY('RequiredBystanders', 'key.required_bystanders'), KEY('ReusingASTContext', 'key.reusingastcontext'), + KEY('CompletionCheckDependencyInterval', + 'key.completion_check_dependency_interval'), ] From df8fa09d8e0a10f872e6499d3de3a697e496399c Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 1 May 2020 09:16:11 -0700 Subject: [PATCH 2/4] [CodeCompletion] Added check dependency test for complete.open --- .../Inputs/checkdeps/MyProject/Library.swift | 4 ++ .../checkdeps/MyProject_mod/Library.swift | 4 ++ .../CodeComplete/complete_checkdeps_vfs.swift | 45 ++++++++++++++++++ .../complete_checkdeps_vfs_open.swift | 46 +++++++++++++++++++ .../include/SourceKit/Core/LangSupport.h | 2 +- .../lib/SwiftLang/SwiftCompletion.cpp | 1 + .../tools/sourcekitd/lib/API/Requests.cpp | 5 ++ 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift create mode 100644 test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift index c19b0db61d800..7e27f53052063 100644 --- a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift @@ -1 +1,5 @@ func localSwiftFunc() -> Int {} + +struct MyStruct { + func myStructMethod() {} +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift index cb6af344cf1ee..1e071aa1cc52b 100644 --- a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift @@ -1 +1,5 @@ func localSwiftFunc_mod() -> Int {} + +struct MyStruct { + func myStructMethod_mod() {} +} diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift new file mode 100644 index 0000000000000..5c9bf69fcd31d --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift @@ -0,0 +1,45 @@ +func foo(value: MyStruct) { + value./*HERE*/ +} + +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: %empty-directory(%t) + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### Modify" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### Keep" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Keep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift new file mode 100644 index 0000000000000..3869ed89f7923 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift @@ -0,0 +1,46 @@ +func foo(value: MyStruct) { + value./*HERE*/ +} + +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: %empty-directory(%t) +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ + +// RUN: -shell -- echo "### Modify" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ + +// RUN: -shell -- echo "### Keep" == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Keep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 9415cd5ef9896..cb3b13d3fabd5 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -146,7 +146,7 @@ class CodeCompletionConsumer { virtual void failed(StringRef ErrDescription) = 0; virtual void setCompletionKind(UIdent kind) {}; - virtual void setReusingASTContext(bool) {}; + virtual void setReusingASTContext(bool) = 0; virtual bool handleResult(const CodeCompletionInfo &Info) = 0; }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 1c8c8ccdb6edc..73a1e7b7aecc6 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -1221,6 +1221,7 @@ void SwiftLangSupport::codeCompleteOpen( completionKind = completionCtx.CodeCompletionKind; typeContextKind = completionCtx.typeContextKind; mayUseImplicitMemberExpr = completionCtx.MayUseImplicitMemberExpr; + consumer.setReusingASTContext(completionCtx.ReusingASTContext); completions = extendCompletions(results, sink, info, nameToPopularity, CCOpts); }); diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 8825adb0e351a..739fb0b436f26 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -2034,6 +2034,7 @@ class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer { void startGroup(UIdent kind, StringRef name) override; void endGroup() override; void setNextRequestStart(unsigned offset) override; + void setReusingASTContext(bool flag) override; }; } // end anonymous namespace @@ -2231,6 +2232,10 @@ void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { assert(!Response.isNull()); Response.set(KeyNextRequestStart, offset); } +void SKGroupedCodeCompletionConsumer::setReusingASTContext(bool flag) { + if (flag) + RespBuilder.getDictionary().setBool(KeyReusingASTContext, flag); +} //===----------------------------------------------------------------------===// // Type Context Info From e32de789ff410a3a182118389fd526224152292c Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 1 May 2020 09:48:16 -0700 Subject: [PATCH 3/4] [Tests] Mark check dependency tests cases 'REQUIRES: shell' These test cases use shell variables etc. --- test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift | 2 ++ .../SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift | 2 ++ test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift | 2 ++ .../SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift | 2 ++ test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift | 2 ++ test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift | 2 ++ 6 files changed, 12 insertions(+) diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift index 47d6b365682f2..2675887fc0dbf 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift @@ -5,6 +5,8 @@ func foo() { /*HERE*/ } +// REQUIRES: shell + // RUN: %empty-directory(%t/Frameworks) // RUN: %empty-directory(%t/MyProject) diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift index 2c691f2408532..68965b5e9a97d 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift @@ -5,6 +5,8 @@ func foo() { /*HERE*/ } +// REQUIRES: shell + // RUN: %empty-directory(%t/Frameworks) // RUN: %empty-directory(%t/MyProject) diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift index b87dcc0b8e561..d9d6f90daf3d6 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift @@ -5,6 +5,8 @@ func foo() { /*HERE*/ } +// REQUIRES: shell + // RUN: %empty-directory(%t/Frameworks) // RUN: %empty-directory(%t/MyProject) diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift index ce8ae5367b754..30cbd7b97a37f 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift @@ -5,6 +5,8 @@ func foo() { /*HERE*/ } +// REQUIRES: shell + // RUN: %empty-directory(%t/Frameworks) // RUN: %empty-directory(%t/MyProject) diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift index 5c9bf69fcd31d..5c123a91db284 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift @@ -2,6 +2,8 @@ func foo(value: MyStruct) { value./*HERE*/ } +// REQUIRES: shell + // RUN: DEPCHECK_INTERVAL=1 // RUN: SLEEP_TIME=2 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift index 3869ed89f7923..12980de80ce45 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift @@ -2,6 +2,8 @@ func foo(value: MyStruct) { value./*HERE*/ } +// REQUIRES: shell + // RUN: DEPCHECK_INTERVAL=1 // RUN: SLEEP_TIME=2 From af5daed144a1015f2fc0014203d246988bf70a60 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 1 May 2020 14:15:21 -0700 Subject: [PATCH 4/4] [CodeCompletion] Tweak fast completion dependency checking - Detect same file with bufferID instead of the file name - Compare virtual in-memory filesystem content with hash value --- include/swift/IDE/CompletionInstance.h | 2 + lib/IDE/CompletionInstance.cpp | 156 ++++++++++++------ .../checkdeps/MyProject/LibraryExt.swift | 3 + .../CodeComplete/complete_checkdeps_vfs.swift | 11 +- .../complete_checkdeps_vfs_open.swift | 14 +- 5 files changed, 127 insertions(+), 59 deletions(-) create mode 100644 test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index 1684d0e2d6cce..8eb345fee94da 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -44,8 +44,10 @@ class CompletionInstance { std::mutex mtx; std::unique_ptr CachedCI; + ModuleDecl *CurrentModule = nullptr; llvm::hash_code CachedArgHash; llvm::sys::TimePoint<> DependencyCheckedTimestamp; + llvm::StringMap InMemoryDependencyHash; unsigned CachedReuseCount = 0; void cacheCompilerInstance(std::unique_ptr CI, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 0bbf166bd014c..67955f93407dc 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -18,19 +18,19 @@ #include "swift/AST/Module.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SourceFile.h" -#include "swift/Serialization/SerializedModuleLoader.h" -#include "swift/ClangImporter/ClangModule.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/SourceManager.h" +#include "swift/ClangImporter/ClangModule.h" #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/PersistentParserState.h" +#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Subsystems.h" +#include "clang/AST/ASTContext.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/MemoryBuffer.h" -#include "clang/AST/ASTContext.h" using namespace swift; using namespace ide; @@ -165,72 +165,113 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, return newDC; } -/// Check if any dependent files are modified since \p timestamp. -bool areAnyDependentFilesInvalidated(CompilerInstance &CI, - llvm::vfs::FileSystem &FS, - StringRef currentFileName, - llvm::sys::TimePoint<> timestamp) { - - auto isInvalidated = [&](StringRef filePath) -> bool { - auto stat = FS.status(filePath); - if (!stat) - // Missing. - return true; - - auto lastModTime = stat->getLastModificationTime(); - if (lastModTime > timestamp) - // Modified. - return true; - - // If the last modification time is zero, this file is probably from a - // virtual file system. We need to check the content. - if (lastModTime == llvm::sys::TimePoint<>()) { - if (&CI.getFileSystem() == &FS) - return false; - - auto oldContent = CI.getFileSystem().getBufferForFile(filePath); - auto newContent = FS.getBufferForFile(filePath); - if (!oldContent || !newContent) - // (unreachable?) - return true; - - if (oldContent.get()->getBuffer() != newContent.get()->getBuffer()) - // Different content. - return true; - } - - return false; - }; - +/// For each dependency file in \p CI, run \p callback until the callback +/// returns \c true. Returns \c true if any callback call returns \c true, \c +/// false otherwise. +static bool +forEachDependencyUntilTrue(CompilerInstance &CI, ModuleDecl *CurrentModule, + unsigned excludeBufferID, + llvm::function_ref callback) { // Check files in the current module. - for (FileUnit *file : CI.getMainModule()->getFiles()) { + for (FileUnit *file : CurrentModule->getFiles()) { StringRef filename; - if (auto SF = dyn_cast(file)) + if (auto SF = dyn_cast(file)) { + if (SF->getBufferID() == excludeBufferID) + continue; filename = SF->getFilename(); - else if (auto LF = dyn_cast(file)) + } else if (auto LF = dyn_cast(file)) filename = LF->getFilename(); else continue; - // Ignore the current file and synthesized files. - if (filename.empty() || filename.front() == '<' || - filename.equals(currentFileName)) + // Ignore synthesized files. + if (filename.empty() || filename.front() == '<') continue; - if (isInvalidated(filename)) + if (callback(filename)) return true; } // Check other non-system depenencies (e.g. modules, headers). for (auto &dep : CI.getDependencyTracker()->getDependencies()) { - if (isInvalidated(dep)) + if (callback(dep)) return true; } - // All loaded module files are not modified since the timestamp. return false; } +/// Collect hash codes of the dependencies into \c Map. +static void cacheDependencyHashIfNeeded(CompilerInstance &CI, + ModuleDecl *CurrentModule, + unsigned excludeBufferID, + llvm::StringMap &Map) { + auto &FS = CI.getFileSystem(); + forEachDependencyUntilTrue( + CI, CurrentModule, excludeBufferID, [&](StringRef filename) { + if (Map.count(filename)) + return false; + + auto stat = FS.status(filename); + if (!stat) + return false; + + // We will check the hash only if the modification time of the dependecy + // is zero. See 'areAnyDependentFilesInvalidated() below'. + if (stat->getLastModificationTime() != llvm::sys::TimePoint<>()) + return false; + + auto buf = FS.getBufferForFile(filename); + Map[filename] = llvm::hash_value(buf.get()->getBuffer()); + return false; + }); +} + +/// Check if any dependent files are modified since \p timestamp. +static bool areAnyDependentFilesInvalidated( + CompilerInstance &CI, ModuleDecl *CurrentModule, llvm::vfs::FileSystem &FS, + unsigned excludeBufferID, llvm::sys::TimePoint<> timestamp, + llvm::StringMap &Map) { + + return forEachDependencyUntilTrue( + CI, CurrentModule, excludeBufferID, [&](StringRef filePath) { + auto stat = FS.status(filePath); + if (!stat) + // Missing. + return true; + + auto lastModTime = stat->getLastModificationTime(); + if (lastModTime > timestamp) + // Modified. + return true; + + // If the last modification time is zero, this file is probably from a + // virtual file system. We need to check the content. + if (lastModTime == llvm::sys::TimePoint<>()) { + // Get the hash code of the last content. + auto oldHashEntry = Map.find(filePath); + if (oldHashEntry == Map.end()) + // Unreachable? Not virtual in old filesystem, but virtual in new + // one. + return true; + auto oldHash = oldHashEntry->second; + + // Calculate the hash code of the current content. + auto newContent = FS.getBufferForFile(filePath); + if (!newContent) + // Unreachable? stat succeeded, but coundn't get the content. + return true; + + auto newHash = llvm::hash_value(newContent.get()->getBuffer()); + + if (oldHash != newHash) + return true; + } + + return false; + }); +} + } // namespace bool CompletionInstance::performCachedOperationIfPossible( @@ -262,8 +303,9 @@ bool CompletionInstance::performCachedOperationIfPossible( return false; if (shouldCheckDependencies()) { - if (areAnyDependentFilesInvalidated(CI, *FileSystem, bufferName, - DependencyCheckedTimestamp)) + if (areAnyDependentFilesInvalidated( + CI, CurrentModule, *FileSystem, SM.getCodeCompletionBufferID(), + DependencyCheckedTimestamp, InMemoryDependencyHash)) return false; DependencyCheckedTimestamp = std::chrono::system_clock::now(); } @@ -406,6 +448,7 @@ bool CompletionInstance::performCachedOperationIfPossible( performImportResolution(*newSF); bindExtensions(*newSF); + CurrentModule = newM; traceDC = newM; #ifndef NDEBUG const auto *reparsedState = newSF->getDelayedParserState(); @@ -432,6 +475,8 @@ bool CompletionInstance::performCachedOperationIfPossible( } CachedReuseCount += 1; + cacheDependencyHashIfNeeded(CI, CurrentModule, SM.getCodeCompletionBufferID(), + InMemoryDependencyHash); return true; } @@ -500,17 +545,24 @@ bool CompletionInstance::performNewOperation( void CompletionInstance::cacheCompilerInstance( std::unique_ptr CI, llvm::hash_code ArgsHash) { CachedCI = std::move(CI); + CurrentModule = CachedCI->getMainModule(); CachedArgHash = ArgsHash; auto now = std::chrono::system_clock::now(); DependencyCheckedTimestamp = now; CachedReuseCount = 0; + InMemoryDependencyHash.clear(); + cacheDependencyHashIfNeeded( + *CachedCI, CurrentModule, + CachedCI->getASTContext().SourceMgr.getCodeCompletionBufferID(), + InMemoryDependencyHash); } bool CompletionInstance::shouldCheckDependencies() const { assert(CachedCI); using namespace std::chrono; auto now = system_clock::now(); - return DependencyCheckedTimestamp + seconds(DependencyCheckIntervalSecond) < now; + return DependencyCheckedTimestamp + seconds(DependencyCheckIntervalSecond) < + now; } void CompletionInstance::setDependencyCheckIntervalSecond(unsigned Value) { diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift new file mode 100644 index 0000000000000..67919b0f97b56 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift @@ -0,0 +1,3 @@ +extension MyStruct { + func extensionMethod() {} +} diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift index 5c123a91db284..388c09e57af33 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift @@ -8,26 +8,29 @@ func foo(value: MyStruct) { // RUN: SLEEP_TIME=2 // RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/VFS) +// RUN: cp %S/Inputs/checkdeps/MyProject/LibraryExt.swift %t/VFS/ // RUN: %sourcekitd-test \ // RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ // RUN: -shell -- echo "### Initial" == \ -// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ // RUN: -shell -- echo "### Modify" == \ // RUN: -shell -- sleep ${SLEEP_TIME} == \ -// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ // RUN: -shell -- echo "### Keep" == \ // RUN: -shell -- sleep ${SLEEP_TIME} == \ -// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift \ // RUN: | %FileCheck %s // CHECK-LABEL: ### Initial // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK-NOT: key.reusingastcontext: 1 @@ -35,6 +38,7 @@ func foo(value: MyStruct) { // CHECK-LABEL: ### Modify // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK-NOT: key.reusingastcontext: 1 @@ -42,6 +46,7 @@ func foo(value: MyStruct) { // CHECK-LABEL: ### Keep // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift index 12980de80ce45..1a8916dc0aceb 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift @@ -8,27 +8,31 @@ func foo(value: MyStruct) { // RUN: SLEEP_TIME=2 // RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/VFS) +// RUN: cp %S/Inputs/checkdeps/MyProject/LibraryExt.swift %t/VFS/ + // RUN: %sourcekitd-test \ // RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ // RUN: -shell -- echo "### Initial" == \ -// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ // RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ // RUN: -shell -- echo "### Modify" == \ // RUN: -shell -- sleep ${SLEEP_TIME} == \ -// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ // RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ // RUN: -shell -- echo "### Keep" == \ -// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ // RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s \ -// RUN: | %FileCheck %s +// RUN: | tee %t/trace | %FileCheck %s // CHECK-LABEL: ### Initial // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK-NOT: key.reusingastcontext: 1 @@ -36,6 +40,7 @@ func foo(value: MyStruct) { // CHECK-LABEL: ### Modify // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK-NOT: key.reusingastcontext: 1 @@ -43,6 +48,7 @@ func foo(value: MyStruct) { // CHECK-LABEL: ### Keep // CHECK: key.results: [ // CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK: key.reusingastcontext: 1