Skip to content

[CodeCompletion] Unresolved member completion at type parameter position #21249

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
Jan 9, 2019
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
34 changes: 24 additions & 10 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1523,10 +1523,10 @@ static Type getReturnTypeFromContext(const DeclContext *DC) {
if (FD->getDeclContext()->isTypeContext())
Ty = FD->getMethodInterfaceType();
if (auto FT = Ty->getAs<AnyFunctionType>())
return FT->getResult();
return DC->mapTypeIntoContext(FT->getResult());
}
} else if (auto ACE = dyn_cast<AbstractClosureExpr>(DC)) {
if (ACE->getType())
if (ACE->getType() && !ACE->getType()->hasError())
return ACE->getResultType();
if (auto CE = dyn_cast<ClosureExpr>(ACE)) {
if (CE->hasExplicitResultType())
Expand Down Expand Up @@ -3670,9 +3670,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
}

void getUnresolvedMemberCompletions(Type T) {
if (!T->getNominalOrBoundGenericNominal())
if (!T->mayHaveMembers())
return;

ModuleDecl *CurrModule = CurrDeclContext->getParentModule();

// We can only say .foo where foo is a static member of the contextual
// type and has the same type (or if the member is a function, then the
// same result type) as the contextual type.
Expand Down Expand Up @@ -3704,8 +3706,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
}

// Otherwise, check the result type matches the contextual type.
auto declTy = getTypeOfMember(VD, T);
if (declTy->hasError())
auto declTy = T->getTypeOfMember(CurrModule, VD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what effect this change has. getTypeOfMember in code-completion seems to do a bunch of things that the one on TypeBase doesn't, including eraseArchetypes, and they also use different substitution flags. We currently are trying hard to avoid having archetypes leak into results where they show up badly when printed, per the comment on eraseArchetypes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the changes below to mapTypeIntoContext supposed to rectify this? If so, how does that compare to eraseArchetypes?

Copy link
Member Author

@rintaro rintaro Dec 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to keep ArchetypeType here because T might be an ArchetypeType and we want to check if result of declTy is convertible to T.

Let's say T is archetype U which conforms to HasStatic protocol and VD is static var instance: Self { get }, getTypeOfMember(VD, T) returns HasStatic protocol, but that is not convertible to archetype U. But T->getTypeOfMember(CurrModule, VD) correctly returns U.

This is just a filtering logic that checks VD can be referenced by "implicit member expression" syntax in T type context. The result of declTy isn't used anywhere else. The type name for completion result is re-constructed by result builder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, thanks for explaining!

if (declTy->is<ErrorType>())
return false;

DeclContext *DC = const_cast<DeclContext *>(CurrDeclContext);
Expand Down Expand Up @@ -5174,6 +5176,7 @@ class CodeCompletionTypeContextAnalyzer {
void recordPossibleType(Type ty) {
if (!ty || ty->is<ErrorType>())
return;

PossibleTypes.push_back(ty->getRValueType());
}

Expand Down Expand Up @@ -5212,6 +5215,10 @@ class CodeCompletionTypeContextAnalyzer {
SmallPtrSet<TypeBase *, 4> seenTypes;
SmallPtrSet<Identifier, 4> seenNames;
for (auto &typeAndDecl : Candidates) {
DeclContext *memberDC = nullptr;
if (typeAndDecl.second)
memberDC = typeAndDecl.second->getInnermostDeclContext();

auto Params = typeAndDecl.first->getParams();
if (Position >= Params.size())
continue;
Expand All @@ -5220,8 +5227,11 @@ class CodeCompletionTypeContextAnalyzer {
if (seenNames.insert(Param.getLabel()).second)
recordPossibleName(Param.getLabel().str());
} else {
if (seenTypes.insert(Param.getOldType().getPointer()).second)
recordPossibleType(Param.getOldType());
Type ty = Param.getOldType();
if (memberDC && ty->hasTypeParameter())
ty = memberDC->mapTypeIntoContext(ty);
if (seenTypes.insert(ty.getPointer()).second)
recordPossibleType(ty);
}
}
}
Expand Down Expand Up @@ -5302,8 +5312,11 @@ class CodeCompletionTypeContextAnalyzer {
if (auto type = destExpr->getType()) {
recordPossibleType(type);
} else if (auto *DRE = dyn_cast<DeclRefExpr>(destExpr)) {
if (auto *decl = DRE->getDecl())
recordPossibleType(decl->getInterfaceType());
if (auto *decl = DRE->getDecl()) {
if (decl->hasInterfaceType())
recordPossibleType(decl->getDeclContext()->mapTypeIntoContext(
decl->getInterfaceType()));
}
}
}
break;
Expand Down Expand Up @@ -5398,7 +5411,8 @@ class CodeCompletionTypeContextAnalyzer {
auto ExprPat = cast<ExprPattern>(P);
if (auto D = ExprPat->getMatchVar()) {
if (D->hasInterfaceType())
recordPossibleType(D->getInterfaceType());
recordPossibleType(
D->getDeclContext()->mapTypeIntoContext(D->getInterfaceType()));
}
break;
}
Expand Down
110 changes: 110 additions & 0 deletions test/IDE/complete_unresolved_members.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADED_INIT_1 | %FileCheck %s -check-prefix=OVERLOADED_METHOD_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OVERLOADED_INIT_2 | %FileCheck %s -check-prefix=OVERLOADED_METHOD_1

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_1 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_2 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_3 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_4 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_5 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_6 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_7 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_8 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_9 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_10 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_11 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_12 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_13 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_14 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_15 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_16 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_17 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_18 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_19 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_20 | %FileCheck %s -check-prefix=GENERICPARAM_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_21 | %FileCheck %s -check-prefix=GENERICPARAM_1

enum SomeEnum1 {
case South
case North
Expand Down Expand Up @@ -535,3 +557,91 @@ func testOverload(val: HasOverloaded) {
let _ = HasOverloaded(e: .#^OVERLOADED_INIT_2^#)
// Same as OVERLOADED_METHOD_1.
}

protocol HasStatic {
static var instance: Self { get }
}
func receiveHasStatic<T: HasStatic>(x: T) {}
func testingGenericParam1<T: HasStatic>(x: inout T, fn: (T) -> Void) -> T {
x = .#^GENERICPARAM_1^#
// GENERICPARAM_1: Begin completions, 1 items
// GENERICPARAM_1: Decl[StaticVar]/CurrNominal: instance[#HasStatic#]; name=instance
// GENERICPARAM_1: End completions

/* Parser sync. */;

let _: (Int, T) = (1, .#^GENERICPARAM_2^#)
// Same as GENERICPARAM_1.

(_, x) = (1, .#^GENERICPARAM_3^#)
// Same as GENERICPARAM_1.

let _ = fn(.#^GENERICPARAM_4^#)
// Same as GENERICPARAM_1.

let _ = receiveHasStatic(x: .#^GENERICPARAM_5^#)
// Same as GENERICPARAM_1.

let _ = { () -> T in
return .#^GENERICPARAM_6^#
// Same as GENERICPARAM_1.
}
let _: () -> T = {
return .#^GENERICPARAM_7^#
// Same as GENERICPARAM_1.
}
let _ = { (_: InvalidTy) -> T in
return .#^GENERICPARAM_8^#
// Same as GENERICPARAM_1.
}

if case .#^GENERICPARAM_9^# = x {}
// Same as GENERICPARAM_1.

return .#^GENERICPARAM_10^#
// Same as GENERICPARAM_1.
}

class C<T: HasStatic> {

var t: T = .instance

func foo(x: T) -> T {
return .#^GENERICPARAM_11^#
// Same as GENERICPARAM_1.
}
func bar<U: HasStatic>(x: U) -> U {
return .#^GENERICPARAM_12^#
// Same as GENERICPARAM_1.
}

func testing() {
let _ = foo(x: .#^GENERICPARAM_13^#)
// Same as GENERICPARAM_1.
let _ = bar(x: .#^GENERICPARAM_14^#)
// Same as GENERICPARAM_1.

t = .#^GENERICPARAM_15^#
// Same as GENERICPARAM_1.

/* Parser sync. */; func sync1() {}
self.t = .#^GENERICPARAM_16^#
// Same as GENERICPARAM_1.

/* Parser sync. */; func sync2() {}
(_, t) = (1, .#^GENERICPARAM_17^#)
// Same as GENERICPARAM_1.

(_, self.t) = (1, .#^GENERICPARAM_18^#)
// Same as GENERICPARAM_1.
}
}

func testingGenericParam2<X>(obj: C<X>) {
let _ = obj.foo(x: .#^GENERICPARAM_19^#)
// Same as GENERICPARAM_1.
let _ = obj.bar(x: .#^GENERICPARAM_20^#)
// Same as GENERICPARAM_1.
obj.t = .#^GENERICPARAM_21^#
// Same as GENERICPARAM_1.
}