Skip to content

Commit 5fc2cfa

Browse files
authored
Merge pull request #29258 from rintaro/ide-completion-dictliteral-rdar57096392
[CodeCompletion] Improve context type analysis for dictionary literals
2 parents 0a30529 + 9d44c45 commit 5fc2cfa

File tree

4 files changed

+85
-12
lines changed

4 files changed

+85
-12
lines changed

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,46 @@ class ExprContextAnalyzer {
602602
// Check context types of the array literal expression.
603603
ExprContextInfo arrayCtxtInfo(DC, Parent);
604604
for (auto arrayT : arrayCtxtInfo.getPossibleTypes()) {
605-
if (auto boundGenericT = arrayT->getAs<BoundGenericType>())
605+
if (auto boundGenericT = arrayT->getAs<BoundGenericType>()) {
606+
// let _: [Element] = [#HERE#]
607+
// In this case, 'Element' is the expected type.
606608
if (boundGenericT->getDecl() == Context.getArrayDecl())
607609
recordPossibleType(boundGenericT->getGenericArgs()[0]);
610+
611+
// let _: [Key : Value] = [#HERE#]
612+
// In this case, 'Key' is the expected type.
613+
if (boundGenericT->getDecl() == Context.getDictionaryDecl())
614+
recordPossibleType(boundGenericT->getGenericArgs()[0]);
615+
}
616+
}
617+
break;
618+
}
619+
case ExprKind::Dictionary: {
620+
// Check context types of the dictionary literal expression.
621+
ExprContextInfo dictCtxtInfo(DC, Parent);
622+
623+
for (auto dictT : dictCtxtInfo.getPossibleTypes()) {
624+
if (auto boundGenericT = dictT->getAs<BoundGenericType>()) {
625+
if (boundGenericT->getDecl() == Context.getDictionaryDecl()) {
626+
if (ParsedExpr->isImplicit() && isa<TupleExpr>(ParsedExpr)) {
627+
// let _: [Key : Value] = [#HERE#:]
628+
// let _: [Key : Value] = [#HERE#:val]
629+
// let _: [Key : Value] = [key:#HERE#]
630+
// In this case, this is called by 'ExprKind::Tuple' case. Return
631+
// '(Key,Value)' here, 'ExprKind::Tuple' branch can decide which
632+
// type in the tuple type is the exprected type.
633+
SmallVector<TupleTypeElt, 2> elts;
634+
for (auto genericArg : boundGenericT->getGenericArgs())
635+
elts.emplace_back(genericArg);
636+
recordPossibleType(TupleType::get(elts, DC->getASTContext()));
637+
} else {
638+
// let _: [Key : Value] = [key: val, #HERE#]
639+
// In this case, assume 'Key' is the expected type.
640+
if (boundGenericT->getDecl() == Context.getDictionaryDecl())
641+
recordPossibleType(boundGenericT->getGenericArgs()[0]);
642+
}
643+
}
644+
}
608645
}
609646
break;
610647
}
@@ -629,13 +666,25 @@ class ExprContextAnalyzer {
629666
break;
630667
}
631668
case ExprKind::Tuple: {
632-
if (!Parent->getType() || !Parent->getType()->is<TupleType>())
633-
return;
669+
TupleType *tupleT = nullptr;
670+
if (Parent->getType() && Parent->getType()->is<TupleType>()) {
671+
tupleT = Parent->getType()->castTo<TupleType>();
672+
} else {
673+
ExprContextInfo tupleCtxtInfo(DC, Parent);
674+
for (auto possibleT : tupleCtxtInfo.getPossibleTypes()) {
675+
if (auto possibleTupleT = possibleT->getAs<TupleType>()) {
676+
tupleT = possibleTupleT;
677+
break;
678+
}
679+
}
680+
if (!tupleT)
681+
return;
682+
}
683+
634684
unsigned Position = 0;
635685
bool HasName;
636686
if (getPositionInArgs(*DC, Parent, ParsedExpr, Position, HasName)) {
637-
recordPossibleType(
638-
Parent->getType()->castTo<TupleType>()->getElementType(Position));
687+
recordPossibleType(tupleT->getElementType(Position));
639688
}
640689
break;
641690
}
@@ -811,6 +860,7 @@ class ExprContextAnalyzer {
811860
case ExprKind::PrefixUnary:
812861
case ExprKind::Assign:
813862
case ExprKind::Array:
863+
case ExprKind::Dictionary:
814864
return true;
815865
case ExprKind::UnresolvedMember:
816866
return true;

lib/Parse/ParseExpr.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3513,15 +3513,26 @@ Parser::parseExprCollectionElement(Optional<bool> &isDictionary) {
35133513

35143514
// Parse the ':'.
35153515
if (!consumeIf(tok::colon)) {
3516+
if (Element.hasCodeCompletion()) {
3517+
// Return the completion expression itself so we can analyze the type
3518+
// later.
3519+
return Element;
3520+
}
35163521
diagnose(Tok, diag::expected_colon_in_dictionary_literal);
35173522
return ParserStatus(Element) | makeParserError();
35183523
}
35193524

35203525
// Parse the value.
35213526
auto Value = parseExpr(diag::expected_value_in_dictionary_literal);
35223527

3523-
if (Value.isNull())
3524-
Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc));
3528+
if (Value.isNull()) {
3529+
if (!Element.hasCodeCompletion()) {
3530+
Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc));
3531+
} else {
3532+
Value = makeParserResult(Value,
3533+
new (Context) CodeCompletionExpr(PreviousLoc));
3534+
}
3535+
}
35253536

35263537
// Make a tuple of Key Value pair.
35273538
return makeParserResult(

test/IDE/complete_expr_postfix_begin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@
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
6565
// 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
67-
// 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
67+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_8 | %FileCheck %s -check-prefix=IN_FOR_EACH_4
6868
// 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
70-
// 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
70+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_11 | %FileCheck %s -check-prefix=IN_FOR_EACH_2
7171
// 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

test/IDE/complete_unresolved_members.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,14 @@
4545
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_34 | %FileCheck %s -check-prefix=UNRESOLVED_3
4646
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_35 | %FileCheck %s -check-prefix=UNRESOLVED_1
4747
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_36 | %FileCheck %s -check-prefix=UNRESOLVED_3
48-
// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_37 | %FileCheck %s -check-prefix=UNRESOLVED_3
49-
// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_38 | %FileCheck %s -check-prefix=UNRESOLVED_3
48+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_37 | %FileCheck %s -check-prefix=UNRESOLVED_3
49+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_38 | %FileCheck %s -check-prefix=UNRESOLVED_3
50+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_39 | %FileCheck %s -check-prefix=UNRESOLVED_3
51+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_40 | %FileCheck %s -check-prefix=UNRESOLVED_3
52+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_41 | %FileCheck %s -check-prefix=UNRESOLVED_3
53+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_42 | %FileCheck %s -check-prefix=UNRESOLVED_3
54+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_43 | %FileCheck %s -check-prefix=UNRESOLVED_3
55+
// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_44 | %FileCheck %s -check-prefix=UNRESOLVED_3
5056

5157
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_AVAIL_1 | %FileCheck %s -check-prefix=ENUM_AVAIL_1
5258
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OPTIONS_AVAIL_1 | %FileCheck %s -check-prefix=OPTIONS_AVAIL_1
@@ -119,7 +125,7 @@ enum SomeEnum2 {
119125
case West
120126
}
121127

122-
enum SomeEnum3 {
128+
enum SomeEnum3: Hashable {
123129
case Payload(SomeEnum1)
124130
}
125131

@@ -388,6 +394,12 @@ let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .South:.#^UNRESOLVED_35^#]
388394
let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_36^#:.Option1]
389395
let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_37^#]
390396
let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_38^#:]
397+
let _: [SomeEnum1:SomeOptions1] = [.#^UNRESOLVED_39^#]
398+
let _: [SomeEnum1:SomeOptions1] = [.#^UNRESOLVED_40^#:]
399+
let _: [SomeEnum1:SomeEnum3] = [.South:.Payload(.South), .South: .Payload(.#^UNRESOLVED_41^#)]
400+
let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_42^#):.South]
401+
let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_43^#):]
402+
let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_44^#)]
391403

392404
func testAvail1(_ x: EnumAvail1) {
393405
testAvail1(.#^ENUM_AVAIL_1^#)

0 commit comments

Comments
 (0)