Skip to content

Commit 68a4545

Browse files
authored
Merge pull request #13348 from benlangmuir/cc-generic-subscript
[parse] Recover better from malformed subscript decls for code-completion
2 parents 676c396 + dc5888d commit 68a4545

File tree

5 files changed

+140
-28
lines changed

5 files changed

+140
-28
lines changed

lib/AST/Decl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,9 +4558,15 @@ ObjCSubscriptKind SubscriptDecl::getObjCSubscriptKind(
45584558
}
45594559

45604560
SourceRange SubscriptDecl::getSourceRange() const {
4561-
if (getBracesRange().isValid())
4561+
if (getBracesRange().isValid()) {
45624562
return { getSubscriptLoc(), getBracesRange().End };
4563-
return { getSubscriptLoc(), ElementTy.getSourceRange().End };
4563+
} else if (ElementTy.getSourceRange().End.isValid()) {
4564+
return { getSubscriptLoc(), ElementTy.getSourceRange().End };
4565+
} else if (ArrowLoc.isValid()) {
4566+
return { getSubscriptLoc(), ArrowLoc };
4567+
} else {
4568+
return getSubscriptLoc();
4569+
}
45644570
}
45654571

45664572
SourceRange SubscriptDecl::getSignatureSourceRange() const {

lib/Parse/ParseDecl.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5622,21 +5622,37 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
56225622
ParserResult<ParameterList> Indices
56235623
= parseSingleParameterClause(ParameterContextKind::Subscript,
56245624
&argumentNames);
5625-
if (Indices.isNull() || Indices.hasCodeCompletion())
5626-
return ParserStatus(Indices);
5625+
Status |= Indices;
5626+
5627+
SignatureHasCodeCompletion |= Indices.hasCodeCompletion();
5628+
if (SignatureHasCodeCompletion && !CodeCompletion)
5629+
return makeParserCodeCompletionStatus();
56275630

56285631
// '->'
5629-
if (!Tok.is(tok::arrow)) {
5632+
SourceLoc ArrowLoc;
5633+
if (!consumeIf(tok::arrow, ArrowLoc)) {
56305634
if (!Indices.isParseError())
56315635
diagnose(Tok, diag::expected_arrow_subscript);
5632-
return makeParserError();
5636+
Status.setIsParseError();
5637+
}
5638+
5639+
if (!ArrowLoc.isValid() && (Indices.isNull() || Indices.get()->size() == 0)) {
5640+
// This doesn't look much like a subscript, so let regular recovery take
5641+
// care of it.
5642+
return Status;
56335643
}
5634-
SourceLoc ArrowLoc = consumeToken();
56355644

56365645
// type
56375646
ParserResult<TypeRepr> ElementTy = parseType(diag::expected_type_subscript);
5638-
if (ElementTy.isNull() || ElementTy.hasCodeCompletion())
5639-
return ParserStatus(ElementTy);
5647+
Status |= ElementTy;
5648+
SignatureHasCodeCompletion |= ElementTy.hasCodeCompletion();
5649+
if (SignatureHasCodeCompletion && !CodeCompletion) {
5650+
return makeParserCodeCompletionStatus();
5651+
}
5652+
if (ElementTy.isNull()) {
5653+
// Always set an element type.
5654+
ElementTy = makeParserResult(ElementTy, new (Context) ErrorTypeRepr());
5655+
}
56405656

56415657
diagnoseWhereClauseInGenericParamList(GenericParams);
56425658

@@ -5665,8 +5681,9 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
56655681
Subscript->setGenericParams(GenericParams);
56665682

56675683
// Pass the function signature to code completion.
5668-
if (SignatureHasCodeCompletion)
5684+
if (SignatureHasCodeCompletion && CodeCompletion) {
56695685
CodeCompletion->setParsedDecl(Subscript);
5686+
}
56705687

56715688
Decls.push_back(Subscript);
56725689

@@ -5676,12 +5693,15 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
56765693
if (Tok.isNot(tok::l_brace)) {
56775694
// Subscript declarations must always have at least a getter, so they need
56785695
// to be followed by a {.
5679-
if (Flags.contains(PD_InProtocol))
5680-
diagnose(Tok, diag::expected_lbrace_subscript_protocol)
5681-
.fixItInsertAfter(ElementTy.get()->getEndLoc(), " { get set }");
5682-
else
5683-
diagnose(Tok, diag::expected_lbrace_subscript);
5684-
Status.setIsParseError();
5696+
if (!Status.isError()) {
5697+
if (Flags.contains(PD_InProtocol)) {
5698+
diagnose(Tok, diag::expected_lbrace_subscript_protocol)
5699+
.fixItInsertAfter(ElementTy.get()->getEndLoc(), " { get set }");
5700+
} else {
5701+
diagnose(Tok, diag::expected_lbrace_subscript);
5702+
}
5703+
Status.setIsParseError();
5704+
}
56855705
} else {
56865706
if (parseGetSet(Flags, GenericParams,
56875707
Indices.get(), ElementTy.get(),
@@ -5700,11 +5720,6 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
57005720
Flags, /*static*/ SourceLoc(), Attributes,
57015721
ElementTy.get(), Indices.get(), Decls);
57025722

5703-
if (Invalid) {
5704-
Subscript->setInterfaceType(ErrorType::get(Context));
5705-
Subscript->setInvalid();
5706-
}
5707-
57085723
// No need to setLocalDiscriminator because subscripts cannot
57095724
// validly appear outside of type decls.
57105725
return makeParserResult(Status, Subscript);

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4254,8 +4254,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
42544254
SD->setIsBeingValidated();
42554255

42564256
auto dc = SD->getDeclContext();
4257-
assert(dc->isTypeContext() &&
4258-
"Decl parsing must prevent subscripts outside of types!");
42594257

42604258
if (auto gp = SD->getGenericParams()) {
42614259
// Write up generic parameters and check the generic parameter list.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_0 | %FileCheck %s -check-prefix=TOP_LEVEL_0
2+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_0 | %FileCheck %s -check-prefix=TOP_LEVEL_0
3+
4+
struct S0 {
5+
subscript(x: #^PARAM_0^#) -> Int { return 0 }
6+
subscript(x: Int) -> #^RETURN_0^# { }
7+
}
8+
// TOP_LEVEL_0: Keyword/None: Any[#Any#];
9+
// TOP_LEVEL_0: Decl[Struct]/CurrModule: S0[#S0#];
10+
// TOP_LEVEL_0: Decl[Struct]/OtherModule[Swift]: Int[#Int#];
11+
12+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_1 | %FileCheck %s -check-prefix=MYSTRUCT_0
13+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_1 | %FileCheck %s -check-prefix=MYSTRUCT_0
14+
struct S1 {
15+
struct MyStruct {}
16+
subscript(x: MyStruct#^PARAM_1^#) -> Int { return 0 }
17+
subscript(x: MyStruct) -> MyStruct#^RETURN_1^# { }
18+
}
19+
// MYSTRUCT_0: Keyword/None: .Type[#S1.MyStruct.Type#];
20+
// MYSTRUCT_0: Keyword/CurrNominal: .self[#S1.MyStruct#];
21+
22+
23+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_2 | %FileCheck %s -check-prefix=MYSTRUCT_1
24+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_2 | %FileCheck %s -check-prefix=MYSTRUCT_1
25+
struct S2 {
26+
struct MyStruct {}
27+
subscript(x: MyStruct.#^PARAM_2^#) -> Int { return 0 }
28+
subscript(x: MyStruct) -> MyStruct.#^RETURN_2^# { }
29+
}
30+
// MYSTRUCT_1: Keyword/None: Type[#S2.MyStruct.Type#];
31+
// MYSTRUCT_1: Keyword/CurrNominal: self[#S2.MyStruct#];
32+
33+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0
34+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0
35+
struct G0<T> {
36+
subscript(x: #^GEN_PARAM_0^#) -> Int { return 0 }
37+
subscript(x: T) -> #^GEN_RETURN_0^# { return 0 }
38+
}
39+
// GEN_TOP_LEVEL_0: Keyword/None: Any[#Any#];
40+
// GEN_TOP_LEVEL_0: Decl[GenericTypeParam]/Local: T[#T#];
41+
// GEN_TOP_LEVEL_0: Decl[Struct]/CurrModule: S0[#S0#];
42+
// GEN_TOP_LEVEL_0: Decl[Struct]/OtherModule[Swift]: Int[#Int#];
43+
44+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_1 | %FileCheck %s -check-prefix=GEN_PARAM_1
45+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_1 | %FileCheck %s -check-prefix=GEN_PARAM_1
46+
struct G1<T> {
47+
subscript(x: T#^GEN_PARAM_1^#) -> Int { return 0 }
48+
subscript(x: T) -> T#^GEN_RETURN_1^# { return 0 }
49+
}
50+
// GEN_PARAM_1: Keyword/None: .Type[#T.Type#];
51+
// GEN_PARAM_1: Keyword/CurrNominal: .self[#T#];
52+
53+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_2 | %FileCheck %s -check-prefix=GEN_PARAM_2
54+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_2 | %FileCheck %s -check-prefix=GEN_PARAM_2
55+
struct G2<T> {
56+
subscript(x: T.#^GEN_PARAM_2^#) -> Int { return 0 }
57+
subscript(x: T) -> T.#^GEN_RETURN_2^# { return 0 }
58+
}
59+
// GEN_PARAM_2: Keyword/None: Type[#T.Type#];
60+
// GEN_PARAM_2: Keyword/CurrNominal: self[#T#];
61+
62+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_3 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_1
63+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_3 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_1
64+
struct G3 {
65+
subscript<T>(x: #^GEN_PARAM_3^#) -> Int { return 0 }
66+
subscript<T>(x: T) -> #^GEN_RETURN_3^# { return 0 }
67+
}
68+
// GEN_TOP_LEVEL_1: Keyword/None: Any[#Any#];
69+
// GEN_TOP_LEVEL_1: Decl[GenericTypeParam]/Local: T[#T#];
70+
// GEN_TOP_LEVEL_1: Decl[Struct]/CurrModule: S0[#S0#];
71+
// GEN_TOP_LEVEL_1: Decl[Struct]/OtherModule[Swift]: Int[#Int#];
72+
73+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_4 | %FileCheck %s -check-prefix=GEN_PARAM_4
74+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_4 | %FileCheck %s -check-prefix=GEN_PARAM_4
75+
struct G4 {
76+
subscript<T>(x: T#^GEN_PARAM_4^#) -> Int { return 0 }
77+
subscript<T>(x: T) -> T#^GEN_RETURN_4^# { return 0 }
78+
}
79+
// GEN_PARAM_4: Keyword/None: .Type[#T.Type#];
80+
// GEN_PARAM_4: Keyword/CurrNominal: .self[#T#];
81+
82+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_5 | %FileCheck %s -check-prefix=GEN_PARAM_5
83+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_5 | %FileCheck %s -check-prefix=GEN_PARAM_5
84+
struct G5 {
85+
subscript<T>(x: T.#^GEN_PARAM_5^#) -> Int { return 0 }
86+
subscript<T>(x: T) -> T.#^GEN_RETURN_5^# { return 0 }
87+
}
88+
// GEN_PARAM_5: Keyword/None: Type[#T.Type#];
89+
// GEN_PARAM_5: Keyword/CurrNominal: self[#T#];

test/Parse/subscripting.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
struct X { }
3+
struct X { } // expected-note {{did you mean}}
44

55
// Simple examples
66
struct X1 {
@@ -101,10 +101,10 @@ struct A1 {
101101
subscript (i : Int) // expected-error{{expected '->' for subscript element type}}
102102
Int {
103103
get {
104-
return stored
104+
return stored // expected-error{{use of unresolved identifier}}
105105
}
106106
set {
107-
stored = value
107+
stored = newValue// expected-error{{use of unresolved identifier}}
108108
}
109109
}
110110
}
@@ -116,13 +116,14 @@ struct A2 {
116116
return stored
117117
}
118118
set {
119-
stored = value
119+
stored = newValue // expected-error{{use of unresolved identifier}}
120120
}
121121
}
122122
}
123123

124124
struct A3 {
125125
subscript(i : Int) // expected-error {{expected '->' for subscript element type}}
126+
// expected-error@-1 {{expected subscripting element type}}
126127
{
127128
get {
128129
return i
@@ -132,6 +133,7 @@ struct A3 {
132133

133134
struct A4 {
134135
subscript(i : Int) { // expected-error {{expected '->' for subscript element type}}
136+
// expected-error@-1 {{expected subscripting element type}}
135137
get {
136138
return i
137139
}
@@ -144,8 +146,10 @@ struct A5 {
144146

145147
struct A6 {
146148
subscript(i: Int)(j: Int) -> Int { // expected-error {{expected '->' for subscript element type}}
149+
// expected-error@-1 {{function types cannot have argument labels}}
150+
// expected-note@-2 {{did you mean}}
147151
get {
148-
return i + j
152+
return i + j // expected-error {{use of unresolved identifier}}
149153
}
150154
}
151155
}

0 commit comments

Comments
 (0)