Skip to content

Commit 468e6d9

Browse files
committed
[CodeCompletion] Complete unresolved members that are not enums or OptionSets
Replace the enum and OptionSet-specific code with static member lookup that checks the type against the contextual type. This lets us complete static variables, static functions, and initializers. In particular, this fixes completion of NS_EXTENSIBLE_STRING_ENUM types from Objective-C. rdar://problem/26628652
1 parent 136c8d4 commit 468e6d9

File tree

2 files changed

+72
-32
lines changed

2 files changed

+72
-32
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
16551655
DeducedAssociatedTypeCache;
16561656

16571657
Optional<SemanticContextKind> ForcedSemanticContext = None;
1658+
bool IsUnresolvedMember = false;
16581659

16591660
std::unique_ptr<ArchetypeTransformer> TransformerPt = nullptr;
16601661

@@ -1885,6 +1886,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18851886
if (ForcedSemanticContext)
18861887
return *ForcedSemanticContext;
18871888

1889+
if (IsUnresolvedMember) {
1890+
if (isa<EnumElementDecl>(D)) {
1891+
return SemanticContextKind::ExpressionSpecific;
1892+
}
1893+
}
1894+
18881895
switch (Reason) {
18891896
case DeclVisibilityKind::LocalVariable:
18901897
case DeclVisibilityKind::FunctionParameter:
@@ -3002,20 +3009,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30023009
return false;
30033010
}
30043011

3005-
void handleOptionSet(Decl *D, DeclVisibilityKind Reason) {
3006-
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
3007-
if (isOptionSetDecl(NTD)) {
3008-
for (auto M : NTD->getMembers()) {
3009-
if (auto *VD = dyn_cast<VarDecl>(M)) {
3010-
if (isOptionSet(VD->getType()) && VD->isStatic()) {
3011-
addVarDeclRef(VD, Reason);
3012-
}
3013-
}
3014-
}
3015-
}
3016-
}
3017-
}
3018-
30193012
bool isOptionSetDecl(NominalTypeDecl *D) {
30203013
auto optionSetType = dyn_cast<ProtocolDecl>(Ctx.getOptionSetDecl());
30213014
if (!optionSetType)
@@ -3659,13 +3652,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
36593652
unboxType(FT->getInput());
36603653
unboxType(FT->getResult());
36613654
} else if (auto NTD = T->getNominalOrBoundGenericNominal()){
3662-
if (HandledDecls.count(NTD) == 0) {
3663-
auto Reason = DeclVisibilityKind::MemberOfCurrentNominal;
3664-
if (!Lookup.handleEnumElement(NTD, Reason)) {
3665-
Lookup.handleOptionSet(NTD, Reason);
3666-
}
3667-
HandledDecls.insert(NTD);
3668-
}
3655+
if (HandledDecls.insert(NTD).second)
3656+
Lookup.getUnresolvedMemberCompletions(T);
36693657
}
36703658
}
36713659

@@ -3699,20 +3687,39 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
36993687
}
37003688
};
37013689

3702-
void getUnresolvedMemberCompletions(SourceLoc Loc, SmallVectorImpl<Type> &Types) {
3690+
void getUnresolvedMemberCompletions(ArrayRef<Type> Types) {
37033691
NeedLeadingDot = !HaveDot;
37043692
for (auto T : Types) {
37053693
if (T && T->getNominalOrBoundGenericNominal()) {
3706-
auto Reason = DeclVisibilityKind::MemberOfCurrentNominal;
3707-
if (!handleEnumElement(T->getNominalOrBoundGenericNominal(), Reason)) {
3708-
handleOptionSet(T->getNominalOrBoundGenericNominal(), Reason);
3709-
}
3694+
// We can only say .foo where foo is a static member of the contextual
3695+
// type and has the same type (or if the member is a function, then the
3696+
// same result type) as the contextual type.
3697+
auto contextCanT = T->getCanonicalType();
3698+
FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD, DeclVisibilityKind reason) {
3699+
if (!VD->hasType()) {
3700+
TypeResolver->resolveDeclSignature(VD);
3701+
if (!VD->hasType())
3702+
return false;
3703+
}
3704+
3705+
auto T = VD->getType();
3706+
while (auto FT = T->getAs<AnyFunctionType>())
3707+
T = FT->getResult();
3708+
return T->getCanonicalType() == contextCanT;
3709+
});
3710+
3711+
auto baseType = MetatypeType::get(T);
3712+
llvm::SaveAndRestore<LookupKind> SaveLook(Kind, LookupKind::ValueExpr);
3713+
llvm::SaveAndRestore<Type> SaveType(ExprType, baseType);
3714+
llvm::SaveAndRestore<bool> SaveUnresolved(IsUnresolvedMember, true);
3715+
lookupVisibleMemberDecls(consumer, baseType, CurrDeclContext,
3716+
TypeResolver.get(),
3717+
/*includeInstanceMembers=*/false);
37103718
}
37113719
}
37123720
}
37133721

3714-
void getUnresolvedMemberCompletions(SourceLoc Loc,
3715-
std::vector<std::string> &FuncNames,
3722+
void getUnresolvedMemberCompletions(std::vector<std::string> &FuncNames,
37163723
bool HasReturn) {
37173724
NeedLeadingDot = !HaveDot;
37183725
LookupByName Lookup(*this, FuncNames);
@@ -5186,12 +5193,10 @@ void CodeCompletionCallbacksImpl::doneParsing() {
51865193
eraseErrorTypes(PE);
51875194
Success = typeCheckUnresolvedExpr(*CurDeclContext, UnresolvedExpr, PE,
51885195
PossibleTypes);
5189-
Lookup.getUnresolvedMemberCompletions(
5190-
P.Context.SourceMgr.getCodeCompletionLoc(), PossibleTypes);
5196+
Lookup.getUnresolvedMemberCompletions(PossibleTypes);
51915197
}
51925198
if (!Success) {
51935199
Lookup.getUnresolvedMemberCompletions(
5194-
P.Context.SourceMgr.getCodeCompletionLoc(),
51955200
TokensBeforeUnresolvedExpr,
51965201
UnresolvedExprInReturn);
51975202
}

test/IDE/complete_unresolved_members.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646

4747
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OTHER_FILE_1 %S/Inputs/EnumFromOtherFile.swift | FileCheck %s -check-prefix=OTHER_FILE_1
4848

49+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NON_OPT_SET_1 | FileCheck %s -check-prefix=NON_OPT_SET_1
50+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NON_OPT_SET_2 | FileCheck %s -check-prefix=NON_OPT_SET_1
51+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NON_OPT_SET_3 | FileCheck %s -check-prefix=NON_OPT_SET_1
52+
4953
enum SomeEnum1 {
5054
case South
5155
case North
@@ -271,10 +275,11 @@ func testAvail1(x: EnumAvail1) {
271275
func testAvail2(x: OptionsAvail1) {
272276
testAvail2(.#^OPTIONS_AVAIL_1^#)
273277
}
274-
// OPTIONS_AVAIL_1: Begin completions, 2 items
278+
// OPTIONS_AVAIL_1: Begin completions, 3 items
275279
// ENUM_AVAIL_1-NOT: AAA
276280
// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal: aaa[#OptionsAvail1#];
277281
// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal/NotRecommended: BBB[#OptionsAvail1#];
282+
// OPTIONS_AVAIL_1-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#OptionsAvail1#]
278283
// ENUM_AVAIL_1-NOT: AAA
279284
// OPTIONS_AVAIL_1: End completions
280285

@@ -329,3 +334,33 @@ func enumFromOtherFile() -> EnumFromOtherFile {
329334
// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific: a({#Int#})[#(Int) -> EnumFromOtherFile#];
330335
// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific: c[#EnumFromOtherFile#];
331336
// OTHER_FILE_1: End completions
337+
338+
struct NonOptSet {
339+
static let a = NonOptSet()
340+
static let wrongType = 1
341+
let notStatic = NonOptSet()
342+
init(x: Int, y: Int) {}
343+
init() {}
344+
static func b() -> NonOptSet { return NonOptSet() }
345+
static func wrongType() -> Int { return 0 }
346+
func notStatic() -> NonOptSet { return NonOptSet() }
347+
}
348+
349+
func testNonOptSet() {
350+
let x: NonOptSet
351+
x = .#^NON_OPT_SET_1^#
352+
}
353+
// NON_OPT_SET_1: Begin completions, 4 items
354+
// NON_OPT_SET_1-DAG: Decl[StaticVar]/CurrNominal: a[#NonOptSet#]
355+
// NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#}, {#y: Int#})[#NonOptSet#]
356+
// NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal: init()[#NonOptSet#]
357+
// NON_OPT_SET_1-DAG: Decl[StaticMethod]/CurrNominal: b()[#NonOptSet#]
358+
// NON_OPT_SET_1: End completions
359+
360+
func testNonOptSet() {
361+
let x: NonOptSet = .#^NON_OPT_SET_2^#
362+
}
363+
364+
func testNonOptSet() -> NonOptSet {
365+
return .#^NON_OPT_SET_3^#
366+
}

0 commit comments

Comments
 (0)