Skip to content

Commit f5f4fa6

Browse files
committed
[CodeCompletion] Handle TupleShuffleExpr in completion context analyzer
(and TupleExpr at non-call argument position). Now, unresolved member completion in array literal should work. Also, Don't calculate convertibility to 'Any' type. That would be a noise to type relation because anything is convertible to 'Any'. rdar://problem/43302814
1 parent 1dfb979 commit f5f4fa6

File tree

4 files changed

+132
-17
lines changed

4 files changed

+132
-17
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ static CodeCompletionResult::ExpectedTypeRelation calculateTypeRelation(
948948
if (!Ty->hasTypeParameter() && !ExpectedTy->hasTypeParameter()) {
949949
if (Ty->isEqual(ExpectedTy))
950950
return CodeCompletionResult::ExpectedTypeRelation::Identical;
951-
if (isConvertibleTo(Ty, ExpectedTy, *DC))
951+
if (!ExpectedTy->isAny() && isConvertibleTo(Ty, ExpectedTy, *DC))
952952
return CodeCompletionResult::ExpectedTypeRelation::Convertible;
953953
}
954954
if (auto FT = Ty->getAs<AnyFunctionType>()) {
@@ -3892,6 +3892,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
38923892

38933893
static bool getPositionInArgs(DeclContext &DC, Expr *Args, Expr *CCExpr,
38943894
unsigned &Position, bool &HasName) {
3895+
if (auto TSE = dyn_cast<TupleShuffleExpr>(Args))
3896+
Args = TSE->getSubExpr();
3897+
38953898
if (isa<ParenExpr>(Args)) {
38963899
HasName = false;
38973900
Position = 0;
@@ -3914,6 +3917,38 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39143917
return false;
39153918
}
39163919

3920+
/// Translate argument index in \p Args to parameter index.
3921+
/// Does nothing unless \p Args is \c TupleShuffleExpr.
3922+
static bool translateArgIndexToParamIndex(Expr *Args,
3923+
unsigned &Position, bool &HasName) {
3924+
auto TSE = dyn_cast<TupleShuffleExpr>(Args);
3925+
if (!TSE)
3926+
return true;
3927+
3928+
auto mapping = TSE->getElementMapping();
3929+
for (unsigned destIdx = 0, e = mapping.size(); destIdx != e; ++destIdx) {
3930+
auto srcIdx = mapping[destIdx];
3931+
if (srcIdx == (signed)Position) {
3932+
Position = destIdx;
3933+
return true;
3934+
}
3935+
if (srcIdx == TupleShuffleExpr::Variadic &&
3936+
llvm::is_contained(TSE->getVariadicArgs(), Position)) {
3937+
// The arg is a part of variadic args.
3938+
Position = destIdx;
3939+
HasName = false;
3940+
if (auto Args = dyn_cast<TupleExpr>(TSE->getSubExpr())) {
3941+
// Check if the first variadiac argument has the label.
3942+
auto firstVarArgIdx = TSE->getVariadicArgs().front();
3943+
HasName = Args->getElementNameLoc(firstVarArgIdx).isValid();
3944+
}
3945+
return true;
3946+
}
3947+
}
3948+
3949+
return false;
3950+
}
3951+
39173952
static bool
39183953
collectArgumentExpectation(DeclContext &DC, ApplyExpr *CallE, Expr *CCExpr,
39193954
std::vector<Type> &ExpectedTypes,
@@ -3928,6 +3963,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39283963
bool HasName;
39293964
if (!getPositionInArgs(DC, CallE->getArg(), CCExpr, Position, HasName))
39303965
return false;
3966+
if (!translateArgIndexToParamIndex(CallE->getArg(), Position, HasName))
3967+
return false;
39313968

39323969
// Collect possible types (or labels) at the position.
39333970
{
@@ -4989,7 +5026,7 @@ namespace {
49895026
class ExprParentFinder : public ASTWalker {
49905027
friend class CodeCompletionTypeContextAnalyzer;
49915028
Expr *ChildExpr;
4992-
llvm::function_ref<bool(ParentTy)> Predicate;
5029+
llvm::function_ref<bool(ParentTy, ParentTy)> Predicate;
49935030

49945031
bool arePositionsSame(Expr *E1, Expr *E2) {
49955032
return E1->getSourceRange().Start == E2->getSourceRange().Start &&
@@ -5001,60 +5038,62 @@ namespace {
50015038
ParentTy ParentClosest;
50025039
ParentTy ParentFarthest;
50035040
ExprParentFinder(Expr* ChildExpr,
5004-
llvm::function_ref<bool(ParentTy)> Predicate) :
5041+
llvm::function_ref<bool(ParentTy, ParentTy)> Predicate) :
50055042
ChildExpr(ChildExpr), Predicate(Predicate) {}
50065043

50075044
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
5045+
if (E != ChildExpr && Predicate(E, Parent)) {
5046+
Ancestors.push_back(E);
5047+
return { true, E };
5048+
}
50085049
if (E == ChildExpr || arePositionsSame(E, ChildExpr)) {
50095050
if (!Ancestors.empty()) {
50105051
ParentClosest = Ancestors.back();
50115052
ParentFarthest = Ancestors.front();
50125053
}
50135054
return {false, nullptr};
50145055
}
5015-
if (Predicate(E))
5016-
Ancestors.push_back(E);
50175056
return { true, E };
50185057
}
50195058

50205059
Expr *walkToExprPost(Expr *E) override {
5021-
if (Predicate(E))
5060+
if (Predicate(E, Parent))
50225061
Ancestors.pop_back();
50235062
return E;
50245063
}
50255064

50265065
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
5027-
if (Predicate(S))
5066+
if (Predicate(S, Parent))
50285067
Ancestors.push_back(S);
50295068
return { true, S };
50305069
}
50315070

50325071
Stmt *walkToStmtPost(Stmt *S) override {
5033-
if (Predicate(S))
5072+
if (Predicate(S, Parent))
50345073
Ancestors.pop_back();
50355074
return S;
50365075
}
50375076

50385077
bool walkToDeclPre(Decl *D) override {
5039-
if (Predicate(D))
5078+
if (Predicate(D, Parent))
50405079
Ancestors.push_back(D);
50415080
return true;
50425081
}
50435082

50445083
bool walkToDeclPost(Decl *D) override {
5045-
if (Predicate(D))
5084+
if (Predicate(D, Parent))
50465085
Ancestors.pop_back();
50475086
return true;
50485087
}
50495088

50505089
std::pair<bool, Pattern *> walkToPatternPre(Pattern *P) override {
5051-
if (Predicate(P))
5090+
if (Predicate(P, Parent))
50525091
Ancestors.push_back(P);
50535092
return { true, P };
50545093
}
50555094

50565095
Pattern *walkToPatternPost(Pattern *P) override {
5057-
if (Predicate(P))
5096+
if (Predicate(P, Parent))
50585097
Ancestors.pop_back();
50595098
return P;
50605099
}
@@ -5074,14 +5113,20 @@ class CodeCompletionTypeContextAnalyzer {
50745113
CodeCompletionTypeContextAnalyzer(DeclContext *DC, Expr *ParsedExpr) : DC(DC),
50755114
ParsedExpr(ParsedExpr), SM(DC->getASTContext().SourceMgr),
50765115
Context(DC->getASTContext()),
5077-
Finder(ParsedExpr, [](ASTWalker::ParentTy Node) {
5116+
Finder(ParsedExpr, [](ASTWalker::ParentTy Node, ASTWalker::ParentTy Parent) {
50785117
if (auto E = Node.getAsExpr()) {
50795118
switch(E->getKind()) {
50805119
case ExprKind::Call:
50815120
case ExprKind::Binary:
50825121
case ExprKind::PrefixUnary:
50835122
case ExprKind::Assign:
50845123
return true;
5124+
case ExprKind::Tuple: {
5125+
auto ParentE = Parent.getAsExpr();
5126+
return !ParentE || (!isa<CallExpr>(ParentE) &&
5127+
!isa<BinaryExpr>(ParentE)&&
5128+
!isa<TupleShuffleExpr>(ParentE));
5129+
}
50855130
default:
50865131
return false;
50875132
}
@@ -5150,6 +5195,16 @@ class CodeCompletionTypeContextAnalyzer {
51505195
}
51515196
break;
51525197
}
5198+
case ExprKind::Tuple: {
5199+
if (!Parent->getType() || !Parent->getType()->is<TupleType>())
5200+
return;
5201+
unsigned Position = 0;
5202+
bool HasName;
5203+
if (CompletionLookup::getPositionInArgs(*DC, Parent, ParsedExpr, Position, HasName)) {
5204+
Callback(Parent->getType()->castTo<TupleType>()->getElementType(Position));
5205+
}
5206+
break;
5207+
}
51535208
default:
51545209
llvm_unreachable("Unhandled expression kind.");
51555210
}

test/IDE/complete_call_arg.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_TO_GENERIC | %FileCheck %s -check-prefix=GENERIC_TO_GENERIC
4949
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NESTED_CLOSURE | %FileCheck %s -check-prefix=NESTED_CLOSURE
5050

51+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SHUFFLE_1 | %FileCheck %s -check-prefix=SHUFFLE_1
52+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SHUFFLE_2 | %FileCheck %s -check-prefix=SHUFFLE_2
53+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SHUFFLE_3 | %FileCheck %s -check-prefix=SHUFFLE_3
54+
5155
var i1 = 1
5256
var i2 = 2
5357
var oi1 : Int?
@@ -91,6 +95,10 @@ class Gen {
9195

9296
func GenGenerator(_ i : Int) -> Gen { return Gen() }
9397

98+
enum SimpleEnum {
99+
case foo, bar, baz
100+
}
101+
94102
class C1 {
95103
func f1() {
96104
foo(3, b: #^ARG1^#)
@@ -181,7 +189,7 @@ class C2 {
181189
// EXPECT_STRING-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: f1()[#Void#]; name=f1()
182190
// EXPECT_STRING-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: f2()[#Void#]; name=f2()
183191
// EXPECT_STRING-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Identical]: stringGen()[#String#]; name=stringGen()
184-
// EXPECT_STRING-DAT: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]
192+
// EXPECT_STRING-DAG: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]
185193
// EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s1[#String#]; name=s1
186194
// EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2
187195
// EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule: os1[#String?#]; name=os1
@@ -435,3 +443,22 @@ func trailingClosureLocal(x: Int, fn: (Int) -> Void) {
435443
// TRAILING_CLOSURE_LOCAL: Decl[LocalVar]/Local: localArg[#Int#]; name=localArg
436444
// TRAILING_CLOSURE_LOCAL: Decl[LocalVar]/Local: localVar[#Int#]; name=localVar
437445
}
446+
447+
func shuffled(_ x: Int ..., y: String = "", z: SimpleEnum = .foo) {}
448+
func testTupleShuffle() {
449+
let _ = shuffled(1, 2, 3, 4, #^SHUFFLE_1^#, z: .foo)
450+
let _ = shuffled(1, 2, 3, y: #^SHUFFLE_2^#
451+
let _ = shuffled(z: .#^SHUFFLE_3^#)
452+
}
453+
// SHUFFLE_1: Begin completions
454+
// SHUFFLE_1-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: i1[#Int#]; name=i1
455+
// SHUFFLE_1-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: i2[#Int#]; name=i2
456+
457+
// SHUFFLE_2: Begin completions
458+
// SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s1[#String#]; name=s1
459+
// SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2
460+
461+
// SHUFFLE_3: Begin completions, 3 items
462+
// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: foo[#SimpleEnum#]; name=foo
463+
// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: bar[#SimpleEnum#]; name=bar
464+
// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: baz[#SimpleEnum#]; name=baz

test/IDE/complete_expr_postfix_begin.swift

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,19 @@
6262
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_3 | %FileCheck %s -check-prefix=IN_FOR_EACH_3
6363
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_4 | %FileCheck %s -check-prefix=IN_FOR_EACH_3
6464
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_5 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
65-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_6 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
65+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_6 | %FileCheck %s -check-prefix=IN_FOR_EACH_2
6666
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_7 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
6767
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_8 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
68-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_9 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
68+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_9 | %FileCheck %s -check-prefix=IN_FOR_EACH_4
6969
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_10 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
7070
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_11 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
71-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_12 | %FileCheck %s -check-prefix=IN_FOR_EACH_1
71+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_12 | %FileCheck %s -check-prefix=IN_FOR_EACH_2
7272

7373
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEPRECATED_1 | %FileCheck %s -check-prefix=DEPRECATED_1
7474

75+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_1 | %FileCheck %s -check-prefix=IN_TUPLE_1
76+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_2 | %FileCheck %s -check-prefix=IN_TUPLE_2
77+
7578
//
7679
// Test code completion at the beginning of expr-postfix.
7780
//
@@ -497,6 +500,13 @@ func testInForEach9(arg: Int) {
497500
let local = 2
498501
for index in [#^IN_FOR_EACH_9^#:2] {}
499502
let after = 4
503+
// NOTE: [Convertible] to AnyHashable.
504+
// IN_FOR_EACH_4-NOT: Decl[LocalVar]
505+
// IN_FOR_EACH_4: Decl[LocalVar]/Local/TypeRelation[Convertible]: local[#Int#];
506+
// FIXME: shouldn't show 'after' here.
507+
// IN_FOR_EACH_4: Decl[LocalVar]/Local/TypeRelation[Convertible]: after[#Int#];
508+
// IN_FOR_EACH_4: Decl[LocalVar]/Local/TypeRelation[Convertible]: arg[#Int#];
509+
// IN_FOR_EACH_4-NOT: Decl[LocalVar]
500510
}
501511
func testInForEach10(arg: Int) {
502512
let local = 2
@@ -530,3 +540,18 @@ struct Deprecated {
530540
// DEPRECATED_1-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: testDeprecated()[#Void#];
531541
// DEPRECATED_1-DAG: Decl[Struct]/CurrModule/NotRecommended: Deprecated[#Deprecated#];
532542
// DEPRECATED_1: End completions
543+
544+
func testTuple(localInt: Int) {
545+
let localStr: String = "foo"
546+
let _: (Int, String) = (1, #^IN_TUPLE_1^#)
547+
let _: (Int, String) = (#^IN_TUPLE_2^#, "foo")
548+
}
549+
// IN_TUPLE_1: Begin completions
550+
// IN_TUPLE_1: Decl[LocalVar]/Local/TypeRelation[Identical]: localStr[#String#]; name=localStr
551+
// IN_TUPLE_1: Decl[LocalVar]/Local: localInt[#Int#]; name=localInt
552+
// IN_TUPLE_1: End completions
553+
554+
// IN_TUPLE_2: Begin completions
555+
// IN_TUPLE_2: Decl[LocalVar]/Local: localStr[#String#]; name=localStr
556+
// IN_TUPLE_2: Decl[LocalVar]/Local/TypeRelation[Identical]: localInt[#Int#]; name=localInt
557+
// IN_TUPLE_2: End completions

test/IDE/complete_unresolved_members.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_28 | %FileCheck %s -check-prefix=UNRESOLVED_1
3838
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_29 | %FileCheck %s -check-prefix=UNRESOLVED_1
3939
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_30 | %FileCheck %s -check-prefix=UNRESOLVED_2
40+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_31 | %FileCheck %s -check-prefix=UNRESOLVED_2
41+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_32 | %FileCheck %s -check-prefix=UNRESOLVED_3
42+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_33 | %FileCheck %s -check-prefix=UNRESOLVED_3
4043

4144
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_AVAIL_1 | %FileCheck %s -check-prefix=ENUM_AVAIL_1
4245
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OPTIONS_AVAIL_1 | %FileCheck %s -check-prefix=OPTIONS_AVAIL_1
@@ -305,6 +308,11 @@ let TopLevelVar1 = OptionSetTaker7([.#^UNRESOLVED_28^#], Op2: [.Option4])
305308
let TopLevelVar2 = OptionSetTaker1([.#^UNRESOLVED_29^#])
306309

307310
let TopLevelVar3 = OptionSetTaker7([.Option1], Op2: [.#^UNRESOLVED_30^#])
311+
let TopLevelVar4 = OptionSetTaker7([.Option1], Op2: [.Option4, .#^UNRESOLVED_31^#])
312+
313+
let _: [SomeEnum1] = [.#^UNRESOLVED_32^#]
314+
let _: [SomeEnum1] = [.South, .#^UNRESOLVED_33^#]
315+
let _: [SomeEnum1:SomeEnum2] = [.South:.West, .#^UNRESOLVED_34^#:]
308316

309317
func testAvail1(_ x: EnumAvail1) {
310318
testAvail1(.#^ENUM_AVAIL_1^#)

0 commit comments

Comments
 (0)