From 58388a2fbcc121a007cfb433667dbf6375c3f584 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Thu, 7 Feb 2019 16:52:30 -0800 Subject: [PATCH 1/2] [ParseableInterface] Fix failing to build a module when the importing file has errors. We were checking the parent invocation's DiagnosticEnginer rather than the subinstance's to determine if there were any errors building the module, which meant we would fail to load the module if there were errors prior to the import statement in the importing file. This also meant code completion would fail to load the module, because it always emits a bogus error in order to mark the AST as erroneous so that different parts of the compiler (e.g. the verifier) have less strict assumptions. rdar://problem/43906499 --- lib/Frontend/ParseableInterfaceSupport.cpp | 2 +- ...odule-cache-errors-in-importing-file.swift | 23 +++++++++++++++ .../Inputs/parseable-interface/MyPoint.swift | 13 +++++++++ .../MyPointExtensions.swift | 7 +++++ .../complete_swiftinterface.swift | 28 +++++++++++++++++++ tools/swift-ide-test/swift-ide-test.cpp | 8 ++++++ 6 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/ParseableInterface/ModuleCache/module-cache-errors-in-importing-file.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPoint.swift create mode 100644 test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift create mode 100644 test/SourceKit/CodeComplete/complete_swiftinterface.swift diff --git a/lib/Frontend/ParseableInterfaceSupport.cpp b/lib/Frontend/ParseableInterfaceSupport.cpp index 9320ae9f61168..abd818f0f3d27 100644 --- a/lib/Frontend/ParseableInterfaceSupport.cpp +++ b/lib/Frontend/ParseableInterfaceSupport.cpp @@ -452,7 +452,7 @@ bool ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface( return; } - SubError = Diags.hadAnyError(); + SubError = SubInstance.getDiags().hadAnyError(); }); return !RunSuccess || SubError; } diff --git a/test/ParseableInterface/ModuleCache/module-cache-errors-in-importing-file.swift b/test/ParseableInterface/ModuleCache/module-cache-errors-in-importing-file.swift new file mode 100644 index 0000000000000..2aa7c3c20ce84 --- /dev/null +++ b/test/ParseableInterface/ModuleCache/module-cache-errors-in-importing-file.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/modulecache) +// +// Setup builds a parseable interface for a module SomeModule (built from some-module.swift). +// This test checks we still build and load its corresponding .swiftmodule when the file that imports it contains an error prior to the import statement. + +// Setup phase 1: Write the input file. +// +// RUN: echo 'public func SomeFunc() -> Int { return 42; }' >>%t/some-module.swift + +// Setup phase 2: build the module. +// +// RUN: %target-swift-frontend -I %t -emit-parseable-module-interface-path %t/SomeModule.swiftinterface -module-name SomeModule %t/some-module.swift -emit-module -o /dev/null + +// Actual test: compile and verify the import succeeds (i.e. we only report the error in this file) +// +// RUN: %target-swift-frontend -typecheck -verify -I %t -module-cache-path %t/modulecache -enable-parseable-module-interface %s + +unresolved // expected-error {{use of unresolved identifier 'unresolved'}} + +import SomeModule + +print(SomeFunc()) diff --git a/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPoint.swift b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPoint.swift new file mode 100644 index 0000000000000..707c85f8aa30d --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPoint.swift @@ -0,0 +1,13 @@ +public struct MyPoint { + public let x: Double + public let y: Double + + public init(x: Double, y: Double) { + self.x = x + self.y = y + } + + public var magnitudeSquared: Double { + return x*x + y*y + } +} diff --git a/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift new file mode 100644 index 0000000000000..37dfbabc2c165 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift @@ -0,0 +1,7 @@ +import MyPoint + +public extension MyPoint { + var magnitude: Double { + return magnitudeSquared.squareRoot() + } +} diff --git a/test/SourceKit/CodeComplete/complete_swiftinterface.swift b/test/SourceKit/CodeComplete/complete_swiftinterface.swift new file mode 100644 index 0000000000000..26d96428076ec --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_swiftinterface.swift @@ -0,0 +1,28 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/modulecache) + +// 1) Build .swiftinterface files for MyPoint and MyExtensions, using a non-default module cache path +// RUN: %target-swift-frontend -emit-parseable-module-interface-path %t/MyPoint.swiftinterface -module-name MyPoint -emit-module -o /dev/null %S/Inputs/parseable-interface/MyPoint.swift +// RUN: %target-swift-frontend -emit-parseable-module-interface-path %t/MyPointExtensions.swiftinterface -module-name MyPointExtensions -emit-module -o /dev/null -enable-parseable-module-interface -module-cache-path %t/modulecache -I %t %S/Inputs/parseable-interface/MyPointExtensions.swift +// RUN: %empty-directory(%t/modulecache) + +// 2) Check completion using the default (cold) module cache +// RUN: %target-swift-ide-test -code-completion -code-completion-token=MEMBER -source-filename %s -I %t | %FileCheck %s + +// 3) Check completion again with a warm module cache +// RUN: %target-swift-ide-test -code-completion -code-completion-token=MEMBER -source-filename %s -I %t | %FileCheck %s + +import MyPoint +import MyPointExtensions + +let x = MyPoint(x: 1, y: 10.5) + +print(x.#^MEMBER^#) + +// CHECK: Begin completions, 5 items +// CHECK: Keyword[self]/CurrNominal: self[#MyPoint#]; name=self +// CHECK: Decl[InstanceVar]/CurrNominal: x[#Double#]; name=x +// CHECK: Decl[InstanceVar]/CurrNominal: y[#Double#]; name=y +// CHECK: Decl[InstanceVar]/CurrNominal: magnitudeSquared[#Double#]; name=magnitudeSquared +// CHECK: Decl[InstanceVar]/CurrNominal: magnitude[#Double#]; name=magnitude +// CHECK: End completions diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index e0f4b37f7313e..94c9d09a6bc52 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -268,6 +268,12 @@ static llvm::cl::opt ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path"), llvm::cl::cat(Category)); +static llvm::cl::opt +EnableParseableModuleInterface("enable-parseable-module-interface", + llvm::cl::desc("Enable loading .swiftinterface files when available"), + llvm::cl::cat(Category), + llvm::cl::init(true)); + static llvm::cl::opt PCHOutputDir("pch-output-dir", llvm::cl::desc("place autogenerated PCH files in this directory"), @@ -3256,6 +3262,8 @@ int main(int argc, char *argv[]) { InitInvok.getLangOptions().EffectiveLanguageVersion = actual.getValue(); } } + InitInvok.getFrontendOptions().EnableParseableModuleInterface = + options::EnableParseableModuleInterface; InitInvok.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; InitInvok.getClangImporterOptions().PrecompiledHeaderOutputDir = From 58a8c9ac8baef327338eec31d0331eb7b9f388d3 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Fri, 8 Feb 2019 17:26:21 -0800 Subject: [PATCH 2/2] [ParseableInterface][sourcekitd] Set the DetailedPreprocessorRecord ClangImporter option on the sub invocation if it's set on the parent In addition to capturing more detailed preprocessor info, the DetailedPreprocessorRecord option sets the clang module format to 'raw' rather than the default 'object'. Sourcekitd doesn't link the code generation libs, which it looks like the default 'object' format requires, so it sets this option to true. The subinvocation generated when loading a module from a .swiftinterface file still used the default prior to this change though, so it would end up crashing sourcekitd. This change sets the DetailedProccessorRecord option if the DetailedRecord option is set on the preprocessor options of parent context's clang module loader. This fixes interface generation crashing for modules that only have a .swiftinterface file. rdar://problem/43906499 --- lib/Frontend/ParseableInterfaceSupport.cpp | 11 ++ .../InterfaceGen/gen_swift_module.swift | 8 ++ ..._module.swift.from_swiftinterface.response | 112 ++++++++++++++++++ .../lib/SwiftLang/SwiftEditorInterfaceGen.cpp | 1 + 4 files changed, 132 insertions(+) create mode 100644 test/SourceKit/InterfaceGen/gen_swift_module.swift.from_swiftinterface.response diff --git a/lib/Frontend/ParseableInterfaceSupport.cpp b/lib/Frontend/ParseableInterfaceSupport.cpp index abd818f0f3d27..800354c1a4b97 100644 --- a/lib/Frontend/ParseableInterfaceSupport.cpp +++ b/lib/Frontend/ParseableInterfaceSupport.cpp @@ -28,6 +28,7 @@ #include "clang/Basic/Module.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Lex/HeaderSearch.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringSet.h" @@ -169,6 +170,16 @@ createInvocationForBuildingFromInterface(ASTContext &Ctx, StringRef ModuleName, SubInvocation.setClangModuleCachePath(CacheDir); SubInvocation.getFrontendOptions().PrebuiltModuleCachePath = PrebuiltCacheDir; + // Respect the detailed-record preprocessor setting of the parent context. + // This, and the "raw" clang module format it implicitly enables, are required + // by sourcekitd. + if (auto *ClangLoader = Ctx.getClangModuleLoader()) { + auto &Opts = ClangLoader->getClangInstance().getPreprocessorOpts(); + if (Opts.DetailedRecord) { + SubInvocation.getClangImporterOptions().DetailedPreprocessingRecord = true; + } + } + // Inhibit warnings from the SubInvocation since we are assuming the user // is not in a position to fix them. SubInvocation.getDiagnosticOptions().SuppressWarnings = true; diff --git a/test/SourceKit/InterfaceGen/gen_swift_module.swift b/test/SourceKit/InterfaceGen/gen_swift_module.swift index ad2ff5e73e8bd..0df2feec4c41c 100644 --- a/test/SourceKit/InterfaceGen/gen_swift_module.swift +++ b/test/SourceKit/InterfaceGen/gen_swift_module.swift @@ -24,3 +24,11 @@ func f(s : inout [Int]) { // RUN: %sourcekitd-test -req=interface-gen-open -module Swift \ // RUN: == -req=find-usr -usr "s:SMsSkRzSL7ElementSTRpzrlE4sortyyF::SYNTHESIZED::s:Sa::SYNTHESIZED::USRDOESNOTEXIST" | %FileCheck -check-prefix=SYNTHESIZED-USR3 %s // SYNTHESIZED-USR3-NOT: USR NOT FOUND + + +// Test we can generate the interface of a module loaded via a .swiftinterface file correctly + +// RUN: %empty-directory(%t.mod) +// RUN: %swift -emit-module -o /dev/null -emit-parseable-module-interface-path %t.mod/swift_mod.swiftinterface %S/Inputs/swift_mod.swift -parse-as-library +// RUN: %sourcekitd-test -req=interface-gen -module swift_mod -- -I %t.mod -enable-parseable-module-interface -module-cache-path %t/mcp > %t.response +// RUN: diff -u %s.from_swiftinterface.response %t.response diff --git a/test/SourceKit/InterfaceGen/gen_swift_module.swift.from_swiftinterface.response b/test/SourceKit/InterfaceGen/gen_swift_module.swift.from_swiftinterface.response new file mode 100644 index 0000000000000..17ecf89b865ff --- /dev/null +++ b/test/SourceKit/InterfaceGen/gen_swift_module.swift.from_swiftinterface.response @@ -0,0 +1,112 @@ + +public class MyClass { + + public func pub_method() +} + +public func pub_function() + + +[ + { + key.kind: source.lang.swift.syntaxtype.attribute.builtin, + key.offset: 1, + key.length: 6 + }, + { + key.kind: source.lang.swift.syntaxtype.keyword, + key.offset: 8, + key.length: 5 + }, + { + key.kind: source.lang.swift.syntaxtype.identifier, + key.offset: 14, + key.length: 7 + }, + { + key.kind: source.lang.swift.syntaxtype.attribute.builtin, + key.offset: 29, + key.length: 6 + }, + { + key.kind: source.lang.swift.syntaxtype.keyword, + key.offset: 36, + key.length: 4 + }, + { + key.kind: source.lang.swift.syntaxtype.identifier, + key.offset: 41, + key.length: 10 + }, + { + key.kind: source.lang.swift.syntaxtype.attribute.builtin, + key.offset: 57, + key.length: 6 + }, + { + key.kind: source.lang.swift.syntaxtype.keyword, + key.offset: 64, + key.length: 4 + }, + { + key.kind: source.lang.swift.syntaxtype.identifier, + key.offset: 69, + key.length: 12 + } +] +<> +[ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "MyClass", + key.offset: 8, + key.length: 47, + key.runtime_name: "_TtC4main7MyClass", + key.nameoffset: 14, + key.namelength: 7, + key.bodyoffset: 23, + key.bodylength: 31, + key.attributes: [ + { + key.offset: 1, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "pub_method()", + key.offset: 36, + key.length: 17, + key.nameoffset: 41, + key.namelength: 12, + key.attributes: [ + { + key.offset: 29, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "pub_function()", + key.offset: 64, + key.length: 19, + key.nameoffset: 69, + key.namelength: 14, + key.attributes: [ + { + key.offset: 57, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ] + } +] diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index 72ee213c4709c..4b7a73521554a 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -708,6 +708,7 @@ void SwiftLangSupport::editorOpenInterface(EditorConsumer &Consumer, } Invocation.getClangImporterOptions().ImportForwardDeclarations = true; + Invocation.getFrontendOptions().EnableParseableModuleInterface = true; std::string ErrMsg; auto IFaceGenRef = SwiftInterfaceGenContext::create(Name,