Skip to content

[CodeComplete] Show global completions from modules that are imported as @_spi #60721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions include/swift/IDE/CodeCompletionCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class CodeCompletionCache {
bool ResultsHaveLeadingDot;
bool ForTestableLookup;
bool ForPrivateImportLookup;
/// Must be sorted alphabetically for stable identity.
llvm::SmallVector<std::string, 2> SpiGroups;
bool AddInitsInToplevel;
bool AddCallWithNoDefaultArgs;
bool Annotated;
Expand All @@ -56,6 +58,7 @@ class CodeCompletionCache {
LHS.ResultsHaveLeadingDot == RHS.ResultsHaveLeadingDot &&
LHS.ForTestableLookup == RHS.ForTestableLookup &&
LHS.ForPrivateImportLookup == RHS.ForPrivateImportLookup &&
LHS.SpiGroups == RHS.SpiGroups &&
LHS.AddInitsInToplevel == RHS.AddInitsInToplevel &&
LHS.AddCallWithNoDefaultArgs == RHS.AddCallWithNoDefaultArgs &&
LHS.Annotated == RHS.Annotated;
Expand Down Expand Up @@ -125,16 +128,35 @@ template<>
struct DenseMapInfo<swift::ide::CodeCompletionCache::Key> {
using KeyTy = swift::ide::CodeCompletionCache::Key;
static inline KeyTy getEmptyKey() {
return KeyTy{"", "", {}, false, false, false, false, false, false};
return KeyTy{/*ModuleFilename=*/"",
/*ModuleName=*/"",
/*AccessPath=*/{},
/*ResultsHaveLeadingDot=*/false,
/*ForTestableLookup=*/false,
/*ForPrivateImportLookup=*/false,
/*SpiGroups=*/{},
/*AddInitsInToplevel=*/false,
/*AddCallWithNoDefaultArgs=*/false,
/*Annotated=*/false};
}
static inline KeyTy getTombstoneKey() {
return KeyTy{"", "", {}, true, false, false, false, false, false};
return KeyTy{/*ModuleFilename=*/"",
/*ModuleName=*/"",
/*AccessPath=*/{},
/*ResultsHaveLeadingDot=*/true,
/*ForTestableLookup=*/false,
/*ForPrivateImportLookup=*/false,
/*SpiGroups=*/{},
/*AddInitsInToplevel=*/false,
/*AddCallWithNoDefaultArgs=*/false,
/*Annotated=*/false};
}
static unsigned getHashValue(const KeyTy &Val) {
return llvm::hash_combine(
Val.ModuleFilename, Val.ModuleName,
llvm::hash_combine_range(Val.AccessPath.begin(), Val.AccessPath.end()),
Val.ResultsHaveLeadingDot, Val.ForTestableLookup,
llvm::hash_combine_range(Val.SpiGroups.begin(), Val.SpiGroups.end()),
Val.ForPrivateImportLookup, Val.AddInitsInToplevel,
Val.AddCallWithNoDefaultArgs, Val.Annotated);
}
Expand Down
20 changes: 17 additions & 3 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,16 @@ void swift::ide::deliverCompletionResults(
// ModuleFilename can be empty if something strange happened during
// module loading, for example, the module file is corrupted.
if (!ModuleFilename.empty()) {
llvm::SmallVector<std::string, 2> spiGroups;
for (auto Import : SF.getImports()) {
if (Import.module.importedModule == TheModule) {
for (auto SpiGroup : Import.spiGroups) {
spiGroups.push_back(SpiGroup.str().str());
}
break;
}
}
llvm::sort(spiGroups);
CodeCompletionCache::Key K{
ModuleFilename.str(),
std::string(TheModule->getName()),
Expand All @@ -1312,6 +1322,7 @@ void swift::ide::deliverCompletionResults(
SF.hasTestableOrPrivateImport(
AccessLevel::Internal, TheModule,
SourceFile::ImportQueryKind::PrivateOnly),
spiGroups,
CompletionContext.getAddInitsToTopLevel(),
CompletionContext.addCallWithNoDefaultArgs(),
CompletionContext.getAnnotateResult()};
Expand Down Expand Up @@ -1351,9 +1362,12 @@ void swift::ide::deliverCompletionResults(
// Add results for all imported modules.
SmallVector<ImportedModule, 4> Imports;
SF.getImportedModules(
Imports, {ModuleDecl::ImportFilterKind::Exported,
ModuleDecl::ImportFilterKind::Default,
ModuleDecl::ImportFilterKind::ImplementationOnly});
Imports, {
ModuleDecl::ImportFilterKind::Exported,
ModuleDecl::ImportFilterKind::Default,
ModuleDecl::ImportFilterKind::ImplementationOnly,
ModuleDecl::ImportFilterKind::SPIAccessControl,
});

for (auto Imported : Imports) {
for (auto Import : namelookup::getAllImports(Imported.importedModule))
Expand Down
19 changes: 16 additions & 3 deletions lib/IDE/CodeCompletionCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,12 @@ static std::string getName(StringRef cacheDirectory,
<< (K.AddInitsInToplevel ? "-inits" : "")
<< (K.AddCallWithNoDefaultArgs ? "-nodefaults" : "")
<< (K.Annotated ? "-annotated" : "");
if (K.SpiGroups.size() > 0) {
OSS << "-spi";
for (auto SpiGroup : K.SpiGroups) {
OSS << "-" << SpiGroup;
}
}

// name[-access-path-components]
for (StringRef component : K.AccessPath)
Expand Down Expand Up @@ -548,9 +554,16 @@ OnDiskCodeCompletionCache::getFromFile(StringRef filename) {
return None;

// Make up a key for readCachedModule.
CodeCompletionCache::Key K{filename.str(), "<module-name>", {},
false, false, false,
false, false, false};
CodeCompletionCache::Key K{/*ModuleFilename=*/filename.str(),
/*ModuleName=*/"<module-name>",
/*AccessPath=*/{},
/*ResultsHaveLeadingDot=*/false,
/*ForTestableLookup=*/false,
/*ForPrivateImportLookup=*/false,
/*SpiGroups=*/{},
/*AddInitsInToplevel=*/false,
/*AddCallWithNoDefaultArgs=*/false,
/*Annotated=*/false};

// Read the cached results.
auto V = CodeCompletionCache::createValue();
Expand Down
61 changes: 61 additions & 0 deletions test/IDE/complete_from_swift_module_spi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// RUN: %empty-directory(%t/split)
// RUN: %empty-directory(%t/build)
// RUN: %{python} %utils/split_file.py -o %t/split %s

// RUN: %target-swift-frontend -emit-module -o %t/build %t/split/pck.swift

// First SPI completion then completion in file without SPI import
// RUN: %empty-directory(%t/cc-cache)
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-spi-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITH_SPI
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-different-spi-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITHOUT_SPI
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-normal-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITHOUT_SPI

// First completion in file without SPI import, then with SPI import
// RUN: %empty-directory(%t/cc-cache)
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-normal-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITHOUT_SPI
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-different-spi-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITHOUT_SPI
// RUN: %target-swift-ide-test -code-completion -completion-cache-path %t/cc-cache -source-filename %t/split/with-spi-import.swift -I %t/build -code-completion-token=COMPLETE | %FileCheck %s --check-prefix=WITH_SPI

// WITH_SPI: Begin completions
// WITH_SPI-DAG: Decl[FreeFunction]/OtherModule[pck]: apiFunc()[#Void#]; name=apiFunc()
// WITH_SPI-DAG: Decl[FreeFunction]/OtherModule[pck]: spiFunc()[#Void#]; name=spiFunc()
// WITH_SPI: End completions

// WITHOUT_SPI: Begin completions
// WITHOUT_SPI-NOT: spiFunc
// WITHOUT_SPI-DAG: Decl[FreeFunction]/OtherModule[pck]: apiFunc()[#Void#]; name=apiFunc()
// WITHOUT_SPI-NOT: spiFunc
// WITHOUT_SPI: End completions


// BEGIN pck.swift

public func apiFunc() {}

@_spi(MySPI)
public func spiFunc() {}

// BEGIN with-spi-import.swift

@_spi(MySPI) import pck

func test() {
#^COMPLETE^#
}

// BEGIN with-different-spi-import.swift

@_spi(OtherSPI) import pck

func test() {
#^COMPLETE^#
}


// BEGIN with-normal-import.swift

import pck

func test() {
#^COMPLETE^#
}