Skip to content

[code-completion] Complete as function reference when the type matches #5136

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
Oct 5, 2016
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
48 changes: 33 additions & 15 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -921,22 +921,30 @@ static CodeCompletionResult::ExpectedTypeRelation calculateTypeRelation(
if (auto FT = Ty->getAs<AnyFunctionType>()) {
if (FT->getResult()->isVoid())
return CodeCompletionResult::ExpectedTypeRelation::Invalid;
return std::max(calculateTypeRelation(FT->getResult(), ExpectedTy, DC),
CodeCompletionResult::ExpectedTypeRelation::Unrelated);
}
return CodeCompletionResult::ExpectedTypeRelation::Unrelated;
}

static CodeCompletionResult::ExpectedTypeRelation calculateTypeRelationForDecl (
const Decl *D,
Type ExpectedType) {
static CodeCompletionResult::ExpectedTypeRelation
calculateTypeRelationForDecl(const Decl *D, Type ExpectedType,
bool UseFuncResultType = true) {
auto VD = dyn_cast<ValueDecl>(D);
auto DC = D->getDeclContext();
if (!VD)
return CodeCompletionResult::ExpectedTypeRelation::Unrelated;
if (auto FD = dyn_cast<FuncDecl>(VD)) {
return std::max(calculateTypeRelation(FD->getType(), ExpectedType, DC),
calculateTypeRelation(FD->getResultType(), ExpectedType, DC));

if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
auto funcType = FD->getType()->getAs<AnyFunctionType>();
if (DC->isTypeContext() && funcType && funcType->is<AnyFunctionType>())
funcType = funcType->getResult()->getAs<AnyFunctionType>();
if (funcType) {
auto relation = calculateTypeRelation(funcType, ExpectedType, DC);
if (UseFuncResultType)
relation =
std::max(relation, calculateTypeRelation(funcType->getResult(),
ExpectedType, DC));
return relation;
}
}
if (auto NTD = dyn_cast<NominalTypeDecl>(VD)) {
return std::max(calculateTypeRelation(NTD->getType(), ExpectedType, DC),
Expand Down Expand Up @@ -1634,6 +1642,19 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
Builder.addDeclDocCommentWords(llvm::makeArrayRef(Pairs));
}

bool shouldUseFunctionReference(AbstractFunctionDecl *D) {
if (PreferFunctionReferencesToCalls)
return true;
for (auto expectedType : ExpectedTypes) {
if (expectedType && expectedType->is<AnyFunctionType>() &&
calculateTypeRelationForDecl(D, expectedType, false) >=
CodeCompletionResult::ExpectedTypeRelation::Convertible) {
return true;
}
}
return false;
}

public:
struct RequestedResultsTy {
const Module *TheModule;
Expand Down Expand Up @@ -2731,10 +2752,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
Builder.addTextChunk(":");
}

if (!HaveRParen)
Builder.addRightParen();
else
Builder.addAnnotatedRightParen();
Builder.addRightParen();
}
}

Expand All @@ -2757,7 +2775,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
case LookupKind::ValueExpr:
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
// Do we want compound function names here?
if (PreferFunctionReferencesToCalls) {
if (shouldUseFunctionReference(CD)) {
addCompoundFunctionName(CD, Reason);
return;
}
Expand Down Expand Up @@ -2821,7 +2839,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
return;

// Do we want compound function names here?
if (PreferFunctionReferencesToCalls) {
if (shouldUseFunctionReference(FD)) {
addCompoundFunctionName(FD, Reason);
return;
}
Expand Down Expand Up @@ -2889,7 +2907,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
return;

// Do we want compound function names here?
if (PreferFunctionReferencesToCalls) {
if (shouldUseFunctionReference(FD)) {
addCompoundFunctionName(FD, Reason);
return;
}
Expand Down
170 changes: 170 additions & 0 deletions test/IDE/complete_func_reference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_VOID_0 | %FileCheck %s -check-prefix=VOID_VOID
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_VOID_1 | %FileCheck %s -check-prefix=VOID_VOID
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_VOID_2 | %FileCheck %s -check-prefix=VOID_VOID
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_VOID_3 | %FileCheck %s -check-prefix=VOID_VOID
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_VOID_4 | %FileCheck %s -check-prefix=VOID_VOID
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ANY_INT_0 | %FileCheck %s -check-prefix=ANY_INT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ANY_INT_1 | %FileCheck %s -check-prefix=ANY_INT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ANY_INT_2 | %FileCheck %s -check-prefix=ANY_INT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INT_ANY_0 | %FileCheck %s -check-prefix=INT_ANY
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INT_ANY_1 | %FileCheck %s -check-prefix=INT_ANY
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INT_ANY_2 | %FileCheck %s -check-prefix=INT_ANY
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_INT_INT_0 | %FileCheck %s -check-prefix=VOID_INT_INT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_INT_INT_1 | %FileCheck %s -check-prefix=VOID_INT_INT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VOID_INT_INT_2 | %FileCheck %s -check-prefix=VOID_INT_INT

func voidToVoid() {}
func voidToInt() -> Int {}
func intToInt(a: Int) -> Int {}
func intToVoid(a: Int) {}

func voidToAny() -> Any {}
func anyToAny(a: Any) -> Any {}
func anyToVoid(a: Any) {}

func intToAny(a: Int) -> Any {}
func anyToInt(a: Any) -> Int {}

func returnsIntToInt() -> (Int) -> Int {}

struct S0 {
func voidToVoid() {}
func voidToInt() -> Int {}
func intToInt(a: Int) -> Int {}
func intToVoid(a: Int) {}

func voidToAny() -> Any {}
func anyToAny(a: Any) -> Any {}
func anyToVoid(a: Any) {}

func intToAny(a: Int) -> Any {}
func anyToInt(a: Any) -> Int {}

func returnsIntToInt() -> (Int) -> Int {}

static func voidToVoid() {}
static func voidToInt() -> Int {}
static func intToInt(a: Int) -> Int {}
static func intToVoid(a: Int) {}

static func voidToAny() -> Any {}
static func anyToAny(a: Any) -> Any {}
static func anyToVoid(a: Any) {}

static func intToAny(a: Int) -> Any {}
static func anyToInt(a: Any) -> Int {}

static func returnsIntToInt() -> (Int) -> Int {}
}

do {
func take(_: @escaping ()->()) {}
take(#^VOID_VOID_0^#)
}
// VOID_VOID: Begin completions
// VOID_VOID-DAG: Decl{{.*}}/TypeRelation[Identical]: voidToVoid;
// VOID_VOID-DAG: Decl{{.*}}/TypeRelation[Convertible]: anyToVoid(a:);
// VOID_VOID-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: intToVoid({#a: Int#})[#Void#];
// VOID_VOID-DAT: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: anyToVoid({#a: Any#})[#Void#];
// VOID_VOID-DAG: Decl{{.*}}: anyToAny({#a: Any#})[#Any#];
// VOID_VOID-DAG: Decl{{.*}}: intToAny({#a: Int#})[#Any#];
// VOID_VOID-DAG: Decl{{.*}}: voidToInt()[#Int#];
// VOID_VOID-DAG: Decl{{.*}}: anyToInt({#a: Any#})[#Int#];
// VOID_VOID-DAG: Decl{{.*}}: intToInt({#a: Int#})[#Int#];
// VOID_VOID-DAG: Decl{{.*}}: voidToAny()[#Any#];
// VOID_VOID-DAG: Decl{{.*}}: returnsIntToInt()[#(Int) -> Int#];
// VOID_VOID: End completions

do {
func take(_: Int, _: Int, c: @escaping ()->()) {}
take(1, 2, c: #^VOID_VOID_1^#)
}

do {
let take: ()->()
take = #^VOID_VOID_2^#
}
do {
let take: ()->()
take = S0().#^VOID_VOID_3^#
}
do {
let take: ()->()
take = S0.#^VOID_VOID_4^#
}

do {
func take(_: @escaping (Any)->Int) {}
take(#^ANY_INT_0^#)
}
do {
func take(_: @escaping (Any)->Int) {}
take(S0().#^ANY_INT_1^#)
}
do {
func take(_: @escaping (Any)->Int) {}
take(S0.#^ANY_INT_2^#)
}

// ANY_INT: Begin completions
// ANY_INT-DAG: Decl{{.*}}/TypeRelation[Convertible]: anyToInt(a:);
// ANY_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: intToVoid({#a: Int#})[#Void#];
// ANY_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: anyToVoid({#a: Any#})[#Void#];
// ANY_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: voidToVoid()[#Void#];
// ANY_INT-DAG: Decl{{.*}}: voidToAny()[#Any#];
// ANY_INT-DAG: Decl{{.*}}: intToInt({#a: Int#})[#Int#];
// ANY_INT-DAG: Decl{{.*}}: intToAny({#a: Int#})[#Any#];
// ANY_INT-DAG: Decl{{.*}}: anyToAny({#a: Any#})[#Any#];
// ANY_INT-DAG: Decl{{.*}}: voidToInt()[#Int#];
// ANY_INT-DAG: Decl{{.*}}: returnsIntToInt()[#(Int) -> Int#];
// ANY_INT: End completions

do {
func take(_: @escaping (Int)->Any) {}
take(#^INT_ANY_0^#)
}

// INT_ANY: Begin completions
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: intToAny(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: intToInt(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: intToVoid(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: anyToAny(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: anyToInt(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: anyToVoid(a:);
// INT_ANY-DAG: Decl{{.*}}/TypeRelation[Convertible]: returnsIntToInt()[#(Int) -> Int#];
// INT_ANY-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: voidToVoid()[#Void#];
// INT_ANY-DAG: Decl{{.*}}: voidToInt()[#Int#];
// INT_ANY-DAG: Decl{{.*}}: voidToAny()[#Any#];
// INT_ANY: End completions

do {
func take(_: @escaping (Int)->Any) {}
take(S0().#^INT_ANY_1^#)
}
do {
func take(_: @escaping (Int)->Any) {}
take(S0.#^INT_ANY_2^#)
}

do {
func take(_: @escaping ()->(Int)->Int) {}
take(#^VOID_INT_INT_0^#)
}
do {
func take(_: @escaping ()->(Int)->Int) {}
take(S0().#^VOID_INT_INT_1^#)
}
do {
func take(_: @escaping ()->(Int)->Int) {}
take(S0.#^VOID_INT_INT_2^#)
}
// VOID_INT_INT-DAG: Decl{{.*}}/TypeRelation[Identical]: returnsIntToInt;
// VOID_INT_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: intToVoid({#a: Int#})[#Void#];
// VOID_INT_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: anyToVoid({#a: Any#})[#Void#];
// VOID_INT_INT-DAG: Decl{{.*}}/NotRecommended/TypeRelation[Invalid]: voidToVoid()[#Void#];
// VOID_INT_INT-DAG: Decl{{.*}}: voidToAny()[#Any#];
// VOID_INT_INT-DAG: Decl{{.*}}: intToAny({#a: Int#})[#Any#];
// VOID_INT_INT-DAG: Decl{{.*}}: anyToAny({#a: Any#})[#Any#];
// VOID_INT_INT-DAG: Decl{{.*}}: voidToInt()[#Int#];
// VOID_INT_INT-DAG: Decl{{.*}}: anyToInt({#a: Any#})[#Int#];
// VOID_INT_INT-DAG: Decl{{.*}}: intToInt({#a: Int#})[#Int#];