Skip to content

Commit f40c3b1

Browse files
committed
[CodeCompletion] Improve accuracy of unresolved member completion
* Handle generic base types * Suggest '.some' and '.none' for optional types * Don't look through too many parameter lists for function types * Include members with convertible type result rdar://problem/44803439
1 parent e080bab commit f40c3b1

File tree

2 files changed

+157
-16
lines changed

2 files changed

+157
-16
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,7 +2617,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26172617

26182618
void addConstructorCallsForType(Type type, Identifier name,
26192619
DeclVisibilityKind Reason) {
2620-
if (!Ctx.LangOpts.CodeCompleteInitsInPostfixExpr)
2620+
if (!Ctx.LangOpts.CodeCompleteInitsInPostfixExpr && !IsUnresolvedMember)
26212621
return;
26222622

26232623
assert(CurrDeclContext);
@@ -2677,6 +2677,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26772677

26782678
void addNominalTypeRef(const NominalTypeDecl *NTD,
26792679
DeclVisibilityKind Reason) {
2680+
if (IsUnresolvedMember)
2681+
return;
26802682
CommandWordsPairs Pairs;
26812683
CodeCompletionResultBuilder Builder(
26822684
Sink,
@@ -2690,6 +2692,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26902692
}
26912693

26922694
void addTypeAliasRef(const TypeAliasDecl *TAD, DeclVisibilityKind Reason) {
2695+
if (IsUnresolvedMember)
2696+
return;
26932697
CommandWordsPairs Pairs;
26942698
CodeCompletionResultBuilder Builder(
26952699
Sink,
@@ -2908,7 +2912,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29082912
// initializer completions if we do not have a left parenthesis and either
29092913
// the initializer is required, the base type's instance type is not a class,
29102914
// or this is a 'self' or 'super' reference.
2911-
if (IsStaticMetatype || Ty->is<ArchetypeType>())
2915+
if (IsStaticMetatype || IsUnresolvedMember || Ty->is<ArchetypeType>())
29122916
addConstructorCall(CD, Reason, None, Result, /*isOnType*/true);
29132917
else if ((IsSelfRefExpr || IsSuperRefExpr || !Ty->is<ClassType>() ||
29142918
CD->isRequired()) && !HaveLParen)
@@ -3759,17 +3763,59 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
37593763
// We can only say .foo where foo is a static member of the contextual
37603764
// type and has the same type (or if the member is a function, then the
37613765
// same result type) as the contextual type.
3762-
FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD, DeclVisibilityKind reason) {
3766+
FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD,
3767+
DeclVisibilityKind reason) {
37633768
if (!VD->hasInterfaceType()) {
37643769
TypeResolver->resolveDeclSignature(VD);
37653770
if (!VD->hasInterfaceType())
37663771
return false;
37673772
}
37683773

3769-
auto declTy = VD->getInterfaceType();
3770-
while (auto FT = declTy->getAs<AnyFunctionType>())
3774+
// Enum element decls can always be referenced by implicit member
3775+
// expression.
3776+
if (isa<EnumElementDecl>(VD))
3777+
return true;
3778+
3779+
// Only non-failable constructors are implicitly referenceable.
3780+
if (auto CD = dyn_cast<ConstructorDecl>(VD)) {
3781+
switch (CD->getFailability()) {
3782+
case OTK_None:
3783+
case OTK_ImplicitlyUnwrappedOptional:
3784+
return true;
3785+
case OTK_Optional:
3786+
return false;
3787+
}
3788+
}
3789+
3790+
// Otherwise, check the result type matches the contextual type.
3791+
auto declTy = getTypeOfMember(VD, T);
3792+
if (declTy->hasError())
3793+
return false;
3794+
3795+
DeclContext *DC = const_cast<DeclContext *>(CurrDeclContext);
3796+
3797+
// Member types can also be implicitly referenceable as long as it's
3798+
// convertible to the contextual type.
3799+
if (auto CD = dyn_cast<TypeDecl>(VD)) {
3800+
declTy = declTy->getMetatypeInstanceType();
3801+
return swift::isConvertibleTo(declTy, T, *DC);
3802+
}
3803+
3804+
// Only static member can be referenced.
3805+
if (!VD->isStatic())
3806+
return false;
3807+
3808+
if (isa<FuncDecl>(VD)) {
3809+
// Strip '(Self.Type) ->' and parameters.
3810+
declTy = declTy->castTo<AnyFunctionType>()->getResult();
3811+
declTy = declTy->castTo<AnyFunctionType>()->getResult();
3812+
} else if (auto FT = declTy->getAs<AnyFunctionType>()) {
3813+
// The compiler accepts 'static var factory: () -> T' for implicit
3814+
// member expression.
3815+
// FIXME: This emits just 'factory'. We should emit 'factory()' instead.
37713816
declTy = FT->getResult();
3772-
return declTy->isEqual(T);
3817+
}
3818+
return swift::isConvertibleTo(declTy, T, *DC);
37733819
});
37743820

37753821
auto baseType = MetatypeType::get(T);
@@ -3784,14 +3830,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
37843830
void getUnresolvedMemberCompletions(ArrayRef<Type> Types) {
37853831
NeedLeadingDot = !HaveDot;
37863832
for (auto T : Types) {
3787-
if (T) {
3788-
// FIXME: we should also include .some/.none from optional itself but
3789-
// getUnresolvedMemberCompletions doesn't ever return them since the
3790-
// interface type in the FilteredDeclConsumer will not match the bound
3791-
// generic type expected.
3792-
T = T->lookThroughAllOptionalTypes();
3793-
getUnresolvedMemberCompletions(T);
3833+
if (!T)
3834+
continue;
3835+
if (auto objT = T->getOptionalObjectType()) {
3836+
// If this is optional type, perform completion for the object type.
3837+
// i.e. 'let _: Enum??? = .enumMember' is legal.
3838+
getUnresolvedMemberCompletions(objT->lookThroughAllOptionalTypes());
37943839
}
3840+
getUnresolvedMemberCompletions(T);
37953841
}
37963842
}
37973843

test/IDE/complete_unresolved_members.swift

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_8 | %FileCheck %s -check-prefix=UNRESOLVED_3
1313
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_9 | %FileCheck %s -check-prefix=UNRESOLVED_3
14-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3
15-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_2 | %FileCheck %s -check-prefix=UNRESOLVED_3
14+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPT
15+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPT
16+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPTOPTOPT
1617

1718
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_12 | %FileCheck %s -check-prefix=UNRESOLVED_3
1819
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_13 | %FileCheck %s -check-prefix=UNRESOLVED_3
@@ -54,6 +55,16 @@
5455

5556
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERPOLATION_1 | %FileCheck %s -check-prefix=STRING_INTERPOLATION_1
5657
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERPOLATION_INVALID
58+
59+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SUBTYPE_1 | %FileCheck %s -check-prefix=SUBTYPE_1
60+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SUBTYPE_2 | %FileCheck %s -check-prefix=SUBTYPE_2
61+
62+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_1 | %FileCheck %s -check-prefix=GENERIC_1 -check-prefix=GENERIC_1_INT
63+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_2 | %FileCheck %s -check-prefix=GENERIC_1 -check-prefix=GENERIC_1_INT
64+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_3 | %FileCheck %s -check-prefix=GENERIC_1 -check-prefix=GENERIC_1_U
65+
66+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_CLOSURE_1 | %FileCheck %s -check-prefix=STATIC_CLOSURE_1
67+
5768
enum SomeEnum1 {
5869
case South
5970
case North
@@ -188,12 +199,33 @@ class C4 {
188199
func f5() {
189200
optionalEnumTaker1(.#^UNRESOLVED_OPT_2^#)
190201
}
202+
func f6() {
203+
var _: SomeEnum1??? = .#^UNRESOLVED_OPT_3^#
204+
}
191205
}
192206
// UNRESOLVED_3: Begin completions
193207
// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; name=North
194208
// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; name=South
195209
// UNRESOLVED_3-NOT: SomeOptions1
196210
// UNRESOLVED_3-NOT: SomeOptions2
211+
// UNRESOLVED_3-NOT: none
212+
// UNRESOLVED_3-NOT: some(
213+
214+
// UNRESOLVED_3_OPT: Begin completions
215+
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#];
216+
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#];
217+
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional<Wrapped>#]; name=none
218+
// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: some({#Wrapped#})[#(Wrapped) -> Optional<Wrapped>#];
219+
// UNRESOLVED_3_OPT-DAG: Decl[Constructor]/CurrNominal: init({#(some): SomeEnum1#})[#Optional<SomeEnum1>#];
220+
// UNRESOLVED_3_OPT-DAG: Decl[Constructor]/CurrNominal: init({#nilLiteral: ()#})[#Optional<SomeEnum1>#];
221+
222+
// UNRESOLVED_3_OPTOPTOPT: Begin completions
223+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#];
224+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#];
225+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional<Wrapped>#]; name=none
226+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: some({#Wrapped#})[#(Wrapped) -> Optional<Wrapped>#];
227+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[Constructor]/CurrNominal: init({#(some): SomeEnum1??#})[#Optional<SomeEnum1??>#];
228+
// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[Constructor]/CurrNominal: init({#nilLiteral: ()#})[#Optional<SomeEnum1??>#];
197229

198230
class C5 {
199231
func f1() {
@@ -287,7 +319,7 @@ func testAvail1(_ x: EnumAvail1) {
287319
func testAvail2(_ x: OptionsAvail1) {
288320
testAvail2(.#^OPTIONS_AVAIL_1^#)
289321
}
290-
// OPTIONS_AVAIL_1: Begin completions, 3 items
322+
// OPTIONS_AVAIL_1: Begin completions
291323
// ENUM_AVAIL_1-NOT: AAA
292324
// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: aaa[#OptionsAvail1#];
293325
// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal/NotRecommended/TypeRelation[Identical]: BBB[#OptionsAvail1#];
@@ -387,3 +419,66 @@ func testInStringInterpolation() {
387419
// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: foo[#MyEnum#];
388420
// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: bar[#MyEnum#];
389421
// STRING_INTERPOLATION_1: End completions
422+
423+
class BaseClass {
424+
class SubClass : BaseClass { init() {} }
425+
static var subInstance: SubClass = SubClass()
426+
}
427+
protocol MyProtocol {
428+
typealias Concrete1 = BaseClass
429+
typealias Concrete2 = AnotherTy
430+
}
431+
extension BaseClass : MyProtocol {}
432+
struct AnotherTy: MyProtocol {}
433+
func testSubType() {
434+
var _: BaseClass = .#^SUBTYPE_1^#
435+
}
436+
// SUBTYPE_1: Begin completions, 4 items
437+
// SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#BaseClass#];
438+
// SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: SubClass()[#BaseClass.SubClass#];
439+
// SUBTYPE_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: subInstance[#BaseClass.SubClass#];
440+
// SUBTYPE_1-DAG: Decl[Constructor]/Super/TypeRelation[Identical]: Concrete1()[#BaseClass#];
441+
// SUBTYPE_1: End completions
442+
443+
func testMemberTypealias() {
444+
var _: MyProtocol = .#^SUBTYPE_2^#
445+
}
446+
// SUBTYPE_2: Begin completions, 2 items
447+
// SUBTYPE_2-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: Concrete1()[#BaseClass#];
448+
// SUBTYPE_2-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: Concrete2()[#AnotherTy#];
449+
// SUBTYPE_2: End completions
450+
451+
enum Generic<T> {
452+
case contains(content: T)
453+
case empty
454+
static func create(_: T) -> Generic<T> { fatalError() }
455+
}
456+
func takeGenericInt(_: Generic<Int>) { }
457+
func takeGenericU<U>(_: Generic<U>) { }
458+
func testGeneric() {
459+
do {
460+
let _: Generic<Int> = .#^GENERIC_1^#
461+
}
462+
takeGenericInt(.#^GENERIC_2^#)
463+
takeGenericU(.#^GENERIC_3^#)
464+
}
465+
// GENERIC_1: Begin completions
466+
// GENERIC_1: Decl[EnumElement]/ExprSpecific: contains({#content: T#})[#(T) -> Generic<T>#];
467+
// GENERIC_1: Decl[EnumElement]/ExprSpecific: empty[#Generic<T>#];
468+
// GENERIC_1_INT: Decl[StaticMethod]/CurrNominal: create({#Int#})[#Generic<Int>#];
469+
// GENERIC_1_U: Decl[StaticMethod]/CurrNominal: create({#U#})[#Generic<U>#];
470+
// GENERIC_1: End completions
471+
472+
struct HasCreator {
473+
static var create: () -> HasCreator = { fatalError() }
474+
static var create_curried: () -> () -> HasCreator = { fatalError() }
475+
}
476+
func testHasStaticClosure() {
477+
let _: HasCreator = .#^STATIC_CLOSURE_1^#
478+
}
479+
// STATIC_CLOSURE_1: Begin completions, 2 items
480+
// STATIC_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#HasCreator#];
481+
// FIXME: Suggest 'create()[#HasCreateor#]', not 'create'.
482+
// STATIC_CLOSURE_1-DAG: Decl[StaticVar]/CurrNominal: create[#() -> HasCreator#];
483+
// STATIC_CLOSURE_1-NOT: create_curried
484+
// STATIC_CLOSURE_1: End completions

0 commit comments

Comments
 (0)