From b9d5672ca1e4e159765265348bf9fe6cd21a8dd2 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Tue, 19 Nov 2019 17:48:19 -0800 Subject: [PATCH] [SourceKit] Add a global-configuration request to control SourceKit's behavior around .swiftsourceinfo files SwiftSourceInfo files provide source location information for decls coming from loaded modules. For most IDE use cases it either has an undesirable impact on performance with no benefit (code completion), results in stale locations being used instead of more up-to-date indexer locations (cursor info), or has no observable effect (live diagnostics, which are filtered to just those with a location in the primary file). For non-IDE clients of SourceKit though, cursor info providing declaration locations for symbols from other modules is useful, so add a global configuration option (and a new request to set it) to control whether .swiftsourceinfo files are loaded or not based on use case (they are loaded by default). --- lib/IDE/SwiftSourceDocInfo.cpp | 23 ++++--- test/Serialization/comments-batch-mode.swift | 4 +- test/Serialization/comments-framework.swift | 4 +- test/Serialization/comments-hidden.swift | 2 +- test/Serialization/comments.swift | 4 +- .../CompileNotifications/arg-parsing.swift | 4 +- .../CursorInfo/use-swift-source-info.swift | 29 +++++++++ test/SourceKit/Misc/stats.swift | 12 ++-- .../include/SourceKit/Core/Context.h | 23 +++++++ tools/SourceKit/lib/Core/Context.cpp | 16 ++++- .../lib/SwiftLang/SwiftASTManager.cpp | 17 ++++- .../SourceKit/lib/SwiftLang/SwiftASTManager.h | 2 + .../lib/SwiftLang/SwiftCompletion.cpp | 4 +- .../SwiftLang/SwiftConformingMethodList.cpp | 4 +- .../lib/SwiftLang/SwiftLangSupport.cpp | 5 +- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 4 +- .../tools/sourcekitd-test/Options.td | 6 ++ .../tools/sourcekitd-test/TestOptions.cpp | 15 +++++ .../tools/sourcekitd-test/TestOptions.h | 3 + .../tools/sourcekitd-test/sourcekitd-test.cpp | 65 ++++++++++++++----- .../tools/sourcekitd/lib/API/Requests.cpp | 18 ++++- tools/swift-ide-test/swift-ide-test.cpp | 13 ++++ utils/gyb_sourcekit_support/UIDs.py | 2 + 23 files changed, 225 insertions(+), 54 deletions(-) create mode 100644 test/SourceKit/CursorInfo/use-swift-source-info.swift diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index e86f3782bb091..bb645722236da 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -764,18 +764,21 @@ void swift::ide::getLocationInfo(const ValueDecl *VD, auto ClangNode = VD->getClangNode(); if (VD->getLoc().isValid()) { + auto getSignatureRange = [&](const ValueDecl *VD) -> Optional { + if (auto FD = dyn_cast(VD)) { + SourceRange R = FD->getSignatureSourceRange(); + if (R.isValid()) + return getCharLength(SM, R); + } + return None; + }; unsigned NameLen; - if (auto FD = dyn_cast(VD)) { - SourceRange R = FD->getSignatureSourceRange(); - if (R.isInvalid()) - return; - NameLen = getCharLength(SM, R); + if (auto SigLen = getSignatureRange(VD)) { + NameLen = SigLen.getValue(); + } else if (VD->hasName()) { + NameLen = VD->getBaseName().userFacingName().size(); } else { - if (VD->hasName()) { - NameLen = VD->getBaseName().userFacingName().size(); - } else { - NameLen = getCharLength(SM, VD->getLoc()); - } + NameLen = getCharLength(SM, VD->getLoc()); } unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc()); diff --git a/test/Serialization/comments-batch-mode.swift b/test/Serialization/comments-batch-mode.swift index 2929590252958..4336546ae4e75 100644 --- a/test/Serialization/comments-batch-mode.swift +++ b/test/Serialization/comments-batch-mode.swift @@ -1,10 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -enable-batch-mode -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %S/Inputs/comments-batch/File1.swift %S/Inputs/comments-batch/File2.swift %S/Inputs/comments-batch/File3.swift %S/Inputs/comments-batch/File4.swift %S/Inputs/comments-batch/File5.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -wmo -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %S/Inputs/comments-batch/File1.swift %S/Inputs/comments-batch/File2.swift %S/Inputs/comments-batch/File3.swift %S/Inputs/comments-batch/File4.swift %S/Inputs/comments-batch/File5.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s // CHECK: Inputs/comments-batch/File1.swift:2:13: Func/FuncFromFile1 RawComment=[/// Comment in File1\n] // CHECK: Inputs/comments-batch/File2.swift:2:13: Func/FuncFromFile2 RawComment=[/// Comment in File2\n] diff --git a/test/Serialization/comments-framework.swift b/test/Serialization/comments-framework.swift index dbce0ec9dab66..84dd55f1e283f 100644 --- a/test/Serialization/comments-framework.swift +++ b/test/Serialization/comments-framework.swift @@ -3,10 +3,10 @@ // RUN: %empty-directory(%t/comments.framework/Modules/comments.swiftmodule/Project) // RUN: %target-swift-frontend -module-name comments -emit-module -emit-module-path %t/comments.framework/Modules/comments.swiftmodule/%target-swiftmodule-name -emit-module-doc-path %t/comments.framework/Modules/comments.swiftmodule/%target-swiftdoc-name -emit-module-source-info-path %t/comments.framework/Modules/comments.swiftmodule/Project/%target-swiftsourceinfo-name %s -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -F %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -F %t | %FileCheck %s // RUN: cp -r %t/comments.framework/Modules/comments.swiftmodule %t/comments.swiftmodule -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s /// first_decl_class_1 Aaa. public class first_decl_class_1 { diff --git a/test/Serialization/comments-hidden.swift b/test/Serialization/comments-hidden.swift index bb8bfa5507b37..9ad0e52fd170c 100644 --- a/test/Serialization/comments-hidden.swift +++ b/test/Serialization/comments-hidden.swift @@ -18,7 +18,7 @@ // // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -enable-testing -module-name comments -emit-module -emit-module-path %t/comments.swiftmodule -emit-module-doc -emit-module-doc-path %t/comments.swiftdoc -emit-module-source-info-path %t/comments.swiftsourceinfo %s -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t > %t.testing.txt +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t > %t.testing.txt // RUN: %FileCheck %s -check-prefix=SOURCE-LOC < %t.testing.txt /// PublicClass Documentation diff --git a/test/Serialization/comments.swift b/test/Serialization/comments.swift index 9e060026d3d40..efdd36b5e68bd 100644 --- a/test/Serialization/comments.swift +++ b/test/Serialization/comments.swift @@ -5,7 +5,7 @@ // RUN: llvm-bcanalyzer %t/comments.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftdoc | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftsourceinfo | %FileCheck %s -check-prefix=BCANALYZER -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t | %FileCheck %s -check-prefix=FIRST +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s -check-prefix=FIRST // Test the case when we have a multiple files in a module. // @@ -16,7 +16,7 @@ // RUN: llvm-bcanalyzer %t/comments.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftdoc | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftsourceinfo | %FileCheck %s -check-prefix=BCANALYZER -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t > %t.printed.txt +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t > %t.printed.txt // RUN: %FileCheck %s -check-prefix=FIRST < %t.printed.txt // RUN: %FileCheck %s -check-prefix=SECOND < %t.printed.txt diff --git a/test/SourceKit/CompileNotifications/arg-parsing.swift b/test/SourceKit/CompileNotifications/arg-parsing.swift index de831a4585dd3..67bfc0cd20928 100644 --- a/test/SourceKit/CompileNotifications/arg-parsing.swift +++ b/test/SourceKit/CompileNotifications/arg-parsing.swift @@ -3,7 +3,7 @@ // ARG_PARSE_0: { // ARG_PARSE_0: key.notification: source.notification.compile-will-start // ARG_PARSE_0: key.compileid: [[CID1:".*"]] -// ARG_PARSE_0: key.compilerargs-string: "{{.*}}.swift -no-such-arg -Xfrontend -ignore-module-source-info" +// ARG_PARSE_0: key.compilerargs-string: "{{.*}}.swift -no-such-arg" // ARG_PARSE_0: } // ARG_PARSE_0: { // ARG_PARSE_0: key.notification: source.notification.compile-did-finish @@ -24,7 +24,7 @@ // ARG_PARSE_1: { // ARG_PARSE_1: key.notification: source.notification.compile-will-start // ARG_PARSE_1: key.compileid: [[CID1:".*"]] -// ARG_PARSE_1: key.compilerargs-string: "{{.*}}.swift -no-such-arg -Xfrontend -ignore-module-source-info" +// ARG_PARSE_1: key.compilerargs-string: "{{.*}}.swift -no-such-arg" // ARG_PARSE_1: } // ARG_PARSE_1: { // ARG_PARSE_1: key.notification: source.notification.compile-did-finish diff --git a/test/SourceKit/CursorInfo/use-swift-source-info.swift b/test/SourceKit/CursorInfo/use-swift-source-info.swift new file mode 100644 index 0000000000000..8b50f38ccb200 --- /dev/null +++ b/test/SourceKit/CursorInfo/use-swift-source-info.swift @@ -0,0 +1,29 @@ +import Foo +func bar() { + foo() +} + +// RUN: %empty-directory(%t) +// RUN: echo "/// Some doc" >> %t/Foo.swift +// RUN: echo "public func foo() { }" >> %t/Foo.swift +// RUN: %target-swift-frontend -enable-batch-mode -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %t/Foo.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc +// +// Test setting optimize for ide to false +// RUN: %sourcekitd-test -req=global-config -for-ide=0 == -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITH %s +// +// Test setting optimize for ide to true +// RUN: %sourcekitd-test -req=global-config -for-ide=1 == -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITHOUT %s +// +// Test sourcekitd-test's default global configuration request (optimize for ide is true) +// RUN: %sourcekitd-test -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITHOUT %s +// +// Test without sending any global configuration request to check the sevice's default settings (optimize for ide is false) +// RUN: %sourcekitd-test -suppress-config-request -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITH %s + +// WITH: source.lang.swift.ref.function.free ({{.*}}/Foo.swift:2:13-2:16) +// WITHOUT: source.lang.swift.ref.function.free () +// BOTH: foo() +// BOTH: s:3Foo3fooyyF +// BOTH: () -> () +// BOTH: $syycD +// BOTH: Foo diff --git a/test/SourceKit/Misc/stats.swift b/test/SourceKit/Misc/stats.swift index ebde8bf39b021..7e6a4c890ac48 100644 --- a/test/SourceKit/Misc/stats.swift +++ b/test/SourceKit/Misc/stats.swift @@ -2,7 +2,7 @@ func foo() {} // RUN: %sourcekitd-test -req=syntax-map %s == -req=stats | %FileCheck %s -check-prefix=SYNTAX_1 -// SYNTAX_1: 2 {{.*}} source.statistic.num-requests +// SYNTAX_1: 3 {{.*}} source.statistic.num-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-semantic-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-ast-builds // SYNTAX_1: 1 {{.*}} source.statistic.num-open-documents @@ -10,7 +10,7 @@ func foo() {} // RUN: %sourcekitd-test -req=syntax-map %s == -req=close %s == -req=stats | %FileCheck %s -check-prefix=SYNTAX_2 -// SYNTAX_2: 3 {{.*}} source.statistic.num-requests +// SYNTAX_2: 4 {{.*}} source.statistic.num-requests // SYNTAX_2: 0 {{.*}} source.statistic.num-semantic-requests // SYNTAX_2: 0 {{.*}} source.statistic.num-ast-builds // SYNTAX_2: 0 {{.*}} source.statistic.num-open-documents @@ -18,7 +18,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_1 -// SEMA_1: 3 {{.*}} source.statistic.num-requests +// SEMA_1: 4 {{.*}} source.statistic.num-requests // SEMA_1: 0 {{.*}} source.statistic.num-semantic-requests // SEMA_1: 1 {{.*}} source.statistic.num-ast-builds // SEMA_1: 1 {{.*}} source.statistic.num-asts-in-memory @@ -28,7 +28,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=edit -pos=1:1 -replace=" " %s == -req=stats | %FileCheck %s -check-prefix=SEMA_2 -// SEMA_2: 5 {{.*}} source.statistic.num-requests +// SEMA_2: 6 {{.*}} source.statistic.num-requests // SEMA_2: 0 {{.*}} source.statistic.num-semantic-requests // SEMA_2: 2 {{.*}} source.statistic.num-ast-builds // NOTE: we cannot match num-asts-in-memory, or num-ast-cache-hits reliably when @@ -40,7 +40,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=cursor -pos=1:6 %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_3 -// SEMA_3: 4 {{.*}} source.statistic.num-requests +// SEMA_3: 5 {{.*}} source.statistic.num-requests // SEMA_3: 1 {{.*}} source.statistic.num-semantic-requests // SEMA_3: 1 {{.*}} source.statistic.num-ast-builds // SEMA_3: 1 {{.*}} source.statistic.num-asts-in-memory @@ -50,7 +50,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=related-idents -pos=1:6 %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_4 -// SEMA_4: 4 {{.*}} source.statistic.num-requests +// SEMA_4: 5 {{.*}} source.statistic.num-requests // SEMA_4: 1 {{.*}} source.statistic.num-semantic-requests // SEMA_4: 1 {{.*}} source.statistic.num-ast-builds // SEMA_4: 1 {{.*}} source.statistic.num-asts-in-memory diff --git a/tools/SourceKit/include/SourceKit/Core/Context.h b/tools/SourceKit/include/SourceKit/Core/Context.h index 063516e723fa4..056e727fe7e2e 100644 --- a/tools/SourceKit/include/SourceKit/Core/Context.h +++ b/tools/SourceKit/include/SourceKit/Core/Context.h @@ -16,6 +16,7 @@ #include "SourceKit/Core/LLVM.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Mutex.h" #include #include @@ -27,10 +28,30 @@ namespace SourceKit { class LangSupport; class NotificationCenter; +class GlobalConfig { +public: + struct Settings { + /// When true, the default compiler options and other configuration flags will be chosen to optimize for + /// usage from an IDE. + /// + /// At the time of writing this just means ignoring .swiftsourceinfo files. + bool OptimizeForIDE = false; + }; + +private: + Settings State; + mutable llvm::sys::Mutex Mtx; + +public: + Settings update(Optional OptimizeForIDE); + bool shouldOptimizeForIDE() const; +}; + class Context { std::string RuntimeLibPath; std::unique_ptr SwiftLang; std::shared_ptr NotificationCtr; + std::shared_ptr Config; public: Context(StringRef RuntimeLibPath, @@ -44,6 +65,8 @@ class Context { LangSupport &getSwiftLangSupport() { return *SwiftLang; } std::shared_ptr getNotificationCenter() { return NotificationCtr; } + + std::shared_ptr getGlobalConfiguration() { return Config; } }; } // namespace SourceKit diff --git a/tools/SourceKit/lib/Core/Context.cpp b/tools/SourceKit/lib/Core/Context.cpp index 066b5bbfdade3..65a245283bd46 100644 --- a/tools/SourceKit/lib/Core/Context.cpp +++ b/tools/SourceKit/lib/Core/Context.cpp @@ -16,11 +16,25 @@ using namespace SourceKit; +GlobalConfig::Settings +GlobalConfig::update(Optional OptimizeForIDE) { + llvm::sys::ScopedLock L(Mtx); + if (OptimizeForIDE.hasValue()) + State.OptimizeForIDE = *OptimizeForIDE; + return State; +}; + +bool GlobalConfig::shouldOptimizeForIDE() const { + llvm::sys::ScopedLock L(Mtx); + return State.OptimizeForIDE; +} + SourceKit::Context::Context(StringRef RuntimeLibPath, llvm::function_ref(Context &)> LangSupportFactoryFn, bool shouldDispatchNotificationsOnMain) : RuntimeLibPath(RuntimeLibPath), - NotificationCtr(new NotificationCenter(shouldDispatchNotificationsOnMain)) { + NotificationCtr(new NotificationCenter(shouldDispatchNotificationsOnMain)), + Config(new GlobalConfig()) { // Should be called last after everything is initialized. SwiftLang = LangSupportFactoryFn(*this); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 58663bd93b6a1..1e2b1fd10a211 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -371,11 +371,13 @@ struct CacheKeyHashInfo { struct SwiftASTManager::Implementation { explicit Implementation( std::shared_ptr EditorDocs, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath) - : EditorDocs(EditorDocs), Stats(Stats), + : EditorDocs(EditorDocs), Config(Config), Stats(Stats), RuntimeResourcePath(RuntimeResourcePath) {} std::shared_ptr EditorDocs; + std::shared_ptr Config; std::shared_ptr Stats; std::string RuntimeResourcePath; SourceManager SourceMgr; @@ -401,8 +403,10 @@ struct SwiftASTManager::Implementation { SwiftASTManager::SwiftASTManager( std::shared_ptr EditorDocs, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath) - : Impl(*new Implementation(EditorDocs, Stats, RuntimeResourcePath)) {} + : Impl(*new Implementation(EditorDocs, Config, Stats, + RuntimeResourcePath)) {} SwiftASTManager::~SwiftASTManager() { delete &Impl; @@ -535,6 +539,15 @@ bool SwiftASTManager::initCompilerInvocation( // We don't care about LLVMArgs FrontendOpts.LLVMArgs.clear(); + // SwiftSourceInfo files provide source location information for decls coming + // from loaded modules. For most IDE use cases it either has an undesirable + // impact on performance with no benefit (code completion), results in stale + // locations being used instead of more up-to-date indexer locations (cursor + // info), or has no observable effect (diagnostics, which are filtered to just + // those with a location in the primary file, and everything else). + if (Impl.Config->shouldOptimizeForIDE()) + FrontendOpts.IgnoreSwiftSourceInfo = true; + // Disable expensive SIL options to reduce time spent in SILGen. disableExpensiveSILOptions(Invocation.getSILOptions()); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h index b5d64e1e5c494..266b2e4636640 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h @@ -43,6 +43,7 @@ namespace SourceKit { class SwiftLangSupport; class SwiftInvocation; struct SwiftStatistics; + class GlobalConfig; typedef RefPtr SwiftInvocationRef; class EditorDiagConsumer; @@ -89,6 +90,7 @@ typedef std::shared_ptr SwiftASTConsumerRef; class SwiftASTManager : public std::enable_shared_from_this { public: explicit SwiftASTManager(std::shared_ptr, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath); ~SwiftASTManager(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 6a9ee7b491a1b..eb4b1fa48b761 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -172,8 +172,8 @@ static bool swiftCodeCompleteImpl( return false; } - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; const char *Position = InputFile->getBufferStart() + CodeCompletionOffset; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 76c308d534136..375cd44d639e5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -67,8 +67,8 @@ static bool swiftConformingMethodListImpl( return false; } - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index a0993222b1938..f1da55be856e8 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -263,8 +263,9 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) Stats = std::make_shared(); EditorDocuments = std::make_shared(); - ASTMgr = std::make_shared(EditorDocuments, Stats, - RuntimeResourcePath); + ASTMgr = std::make_shared(EditorDocuments, + SKCtx.getGlobalConfiguration(), + Stats, RuntimeResourcePath); // By default, just use the in-memory cache. CCCache->inMemory = llvm::make_unique(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 285052d3a2895..40ababa58c06c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -67,8 +67,8 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, return false; } - // Disable source location resolutions from .swiftsourceinfo file because - // they are somewhat heavy operations and are not needed for completions. + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index 53b5c9573bef6..213ef1780a5a4 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -140,6 +140,12 @@ def vfs_files : CommaJoined<["-"], "vfs-files=">, def vfs_name : Separate<["-"], "vfs-name">, HelpText<"Specify a virtual filesystem name">; +def optimize_for_ide : Joined<["-"], "for-ide=">, + HelpText<"Value for the OptimizeForIde global configuration setting">; + +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)">; + def help : Flag<["-", "--"], "help">, HelpText<"Display available options">; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index 1ecae811b9f96..cebe6dd8942ec 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -154,6 +154,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("stats", SourceKitRequest::Statistics) .Case("track-compiles", SourceKitRequest::EnableCompileNotifications) .Case("collect-type", SourceKitRequest::CollectExpresstionType) + .Case("global-config", SourceKitRequest::GlobalConfiguration) .Default(SourceKitRequest::None); if (Request == SourceKitRequest::None) { @@ -370,6 +371,20 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { VFSName = InputArg->getValue(); break; + case OPT_optimize_for_ide: { + bool Value; + if (StringRef(InputArg->getValue()).getAsInteger(10, Value)) { + llvm::errs() << "error: expected 0 or 1 for 'for-ide'\n"; + return true; + } + OptimizeForIde = Value; + break; + } + + case OPT_suppress_config_request: + SuppressDefaultConfigRequest = true; + break; + case OPT_UNKNOWN: llvm::errs() << "error: unknown argument: " << InputArg->getAsString(ParsedArgs) << '\n' diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index f854a241836b3..8f86211df58df 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -64,6 +64,7 @@ enum class SourceKitRequest { SyntaxTree, EnableCompileNotifications, CollectExpresstionType, + GlobalConfiguration, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, #include "swift/IDE/RefactoringKinds.def" }; @@ -110,6 +111,8 @@ struct TestOptions { bool CollectActionables = false; bool isAsyncRequest = false; bool timeRequest = false; + llvm::Optional OptimizeForIde; + bool SuppressDefaultConfigRequest = false; 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 23ce67ffce814..6a397dff7ff99 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -53,7 +53,9 @@ int STDOUT_FILENO = _fileno(stdout); } #endif -static int handleTestInvocation(ArrayRef Args, TestOptions &InitOpts); +static bool sendGlobalConfigRequest(); +static int handleTestInvocation(ArrayRef Args, TestOptions &InitOpts, + bool IsFirstInvocation); static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, const std::string &SourceFile, std::unique_ptr SourceBuf, @@ -238,6 +240,7 @@ static void skt_main(skt_args *args) { // invocations. TestOptions InitOpts; auto Args = llvm::makeArrayRef(argv+1, argc-1); + bool firstInvocation = true; while (1) { unsigned i = 0; for (auto Arg: Args) { @@ -247,15 +250,17 @@ static void skt_main(skt_args *args) { } if (i == Args.size()) break; - if (int ret = handleTestInvocation(Args.slice(0, i), InitOpts)) { + if (int ret = handleTestInvocation(Args.slice(0, i), InitOpts, + firstInvocation)) { sourcekitd_shutdown(); args->ret = ret; return; } Args = Args.slice(i + 1); + firstInvocation = false; } - if (int ret = handleTestInvocation(Args, InitOpts)) { + if (int ret = handleTestInvocation(Args, InitOpts, firstInvocation)) { sourcekitd_shutdown(); args->ret = ret; return; @@ -385,7 +390,7 @@ static int handleJsonRequestPath(StringRef QueryPath, const TestOptions &Opts) { static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts); static int handleTestInvocation(ArrayRef Args, - TestOptions &InitOpts) { + TestOptions &InitOpts, bool firstInvocation) { unsigned Optargc = 0; for (auto Arg: Args) { @@ -401,6 +406,16 @@ static int handleTestInvocation(ArrayRef Args, if (Optargc < Args.size()) Opts.CompilerArgs = Args.slice(Optargc+1); + if (firstInvocation && Opts.Request != SourceKitRequest::GlobalConfiguration && + !Opts.SuppressDefaultConfigRequest) { + // We don't fail if this request fails for now so that sourcekitd-test is + // still usable with older versions of sourcekitd that don't have the + // global-configuration request. + if (sendGlobalConfigRequest()) { + llvm::outs() << "warning: global configuration request failed\n"; + } + } + assert(Opts.repeatRequest >= 1); for (unsigned i = 0; i < Opts.repeatRequest; ++i) { if (int ret = handleTestInvocation(Opts, InitOpts)) { @@ -435,6 +450,28 @@ static int setExpectedTypes(const sourcekitd_test::TestOptions &Opts, return 0; } +static bool sendGlobalConfigRequest() { + TestOptions Opts; + sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr, + nullptr, 0); + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); + + // For test invocations we default to setting OptimizeForIDE to true. This + // matches the use case of the most popular clients of sourcekitd (editors) + // and also disables loading locations from .swiftsourceinfo files. This is + // desirable for testing because the .swiftsourceinfo for the stdlib is + // available when sourcekitd is tested, and can make some stdlib-dependent + // sourcekitd tests unstable due to changing source locations from the stdlib + // module. + sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast(true)); + sourcekitd_response_t Resp = sendRequestSync(Req, Opts); + bool IsError = sourcekitd_response_is_error(Resp); + if (IsError) + sourcekitd_response_description_dump(Resp); + sourcekitd_request_release(Req); + return IsError; +} + static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (!Opts.JsonRequestPath.empty()) return handleJsonRequestPath(Opts.JsonRequestPath, Opts); @@ -481,7 +518,6 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0); ActiveRequest = Opts.Request; - bool ShouldIgnoreSourceInfo = true; switch (Opts.Request) { case SourceKitRequest::None: llvm::errs() << "request is not set\n"; @@ -490,6 +526,12 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { // with a zero (successful) exit code. return 1; + 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())); + break; + case SourceKitRequest::ProtocolVersion: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestProtocolVersion); break; @@ -841,7 +883,6 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_dictionary_set_int64(Req, KeyUsingSwiftArgs, true); sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpenHeaderInterface); - ShouldIgnoreSourceInfo = false; } sourcekitd_request_dictionary_set_string(Req, KeyName, getInterfaceGenDocumentName().c_str()); @@ -935,17 +976,6 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_object_t Args = sourcekitd_request_array_create(nullptr, 0); for (auto Arg : Opts.CompilerArgs) sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Arg); - if (ShouldIgnoreSourceInfo) { - // Ignore .swiftsourceinfo file when testing sourcekitd. - // .swiftsourceinfo for stdlib will be available when sourcekitd is tested, - // which may make some stdlib-depending sourcekitd tests volatile. - // We cannot append the flags when the compiler arguments are for clang - // invocation. - sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, - "-Xfrontend"); - sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, - "-ignore-module-source-info"); - } sourcekitd_request_dictionary_set_value(Req, KeyCompilerArgs, Args); sourcekitd_request_release(Args); } @@ -1092,6 +1122,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, printMangleResults(sourcekitd_response_get_value(Resp), outs()); break; + case SourceKitRequest::GlobalConfiguration: case SourceKitRequest::ProtocolVersion: case SourceKitRequest::CompilerVersion: case SourceKitRequest::Close: diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 3a3483e96f599..66d184e0cf544 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -425,6 +425,21 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { if (!ReqUID) return Rec(createErrorRequestInvalid("missing 'key.request' with UID value")); + if (ReqUID == RequestGlobalConfiguration) { + auto Config = getGlobalContext().getGlobalConfiguration(); + ResponseBuilder RB; + auto dict = RB.getDictionary(); + + Optional OptimizeForIDE; + int64_t EditorMode = true; + if (!Req.getInt64(KeyOptimizeForIDE, EditorMode, true)) { + OptimizeForIDE = EditorMode; + } + + GlobalConfig::Settings UpdatedConfig = Config->update(OptimizeForIDE); + dict.set(KeyOptimizeForIDE, UpdatedConfig.OptimizeForIDE); + return Rec(RB.createResponse()); + } if (ReqUID == RequestProtocolVersion) { ResponseBuilder RB; auto dict = RB.getDictionary(); @@ -1002,7 +1017,8 @@ static void handleSemanticRequest( Req.getInt64(KeyRetrieveRefactorActions, Actionables, /*isOptional=*/true); return Lang.getCursorInfo( *SourceFile, Offset, Length, Actionables, CancelOnSubsequentRequest, - Args, std::move(vfsOptions), [Rec](const RequestResult &Result) { + Args, std::move(vfsOptions), + [Rec](const RequestResult &Result) { reportCursorInfo(Result, Rec); }); } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 3961d14f5ca03..c8c47f56dfd63 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -694,6 +694,13 @@ GraphVisPath("output-request-graphviz", static llvm::cl::opt CanonicalizeType("canonicalize-type", llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false)); + +static llvm::cl::opt +EnableSwiftSourceInfo("enable-swiftsourceinfo", + llvm::cl::desc("Whether to consume .swiftsourceinfo files"), + llvm::cl::cat(Category), + llvm::cl::init(false)); + } // namespace options static std::unique_ptr @@ -3325,6 +3332,12 @@ int main(int argc, char *argv[]) { InitInvok.getLangOptions().EnableObjCInterop = llvm::Triple(options::Triple).isOSDarwin(); } + + // We disable source location resolutions from .swiftsourceinfo files by + // default to match sourcekitd-test's and ide clients' expected behavior + // (passing optimize-for-ide in the global configuration request). + if (!options::EnableSwiftSourceInfo) + InitInvok.getFrontendOptions().IgnoreSwiftSourceInfo = true; if (!options::Triple.empty()) InitInvok.setTargetTriple(options::Triple); if (!options::SwiftVersion.empty()) { diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 655a5a20b479e..ad0c1d54018c9 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -174,6 +174,7 @@ def __init__(self, internal_name, external_name): KEY('VFSName', 'key.vfs.name'), KEY('VFSOptions', 'key.vfs.options'), KEY('Files', 'key.files'), + KEY('OptimizeForIDE', 'key.optimize_for_ide'), ] @@ -231,6 +232,7 @@ def __init__(self, internal_name, external_name): 'source.request.enable-compile-notifications'), REQUEST('TestNotification', 'source.request.test_notification'), REQUEST('CollectExpressionType', 'source.request.expression.type'), + REQUEST('GlobalConfiguration', 'source.request.configuration.global') ]