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') ]