Skip to content

Commit 3d32f5f

Browse files
authored
Merge pull request #73123 from rintaro/ide-complete-docatch-boundvar-rdar126699879
[CodeCompletion] Fix completion for 'catch' pattern bound values
2 parents 1a0a44c + 0e12254 commit 3d32f5f

File tree

6 files changed

+63
-96
lines changed

6 files changed

+63
-96
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2746,9 +2746,15 @@ NamingPatternRequest::evaluate(Evaluator &evaluator, VarDecl *VD) const {
27462746
// We have some other parent stmt. Type check it completely.
27472747
if (auto CS = dyn_cast<CaseStmt>(parentStmt))
27482748
parentStmt = CS->getParentStmt();
2749+
2750+
bool LeaveBodyUnchecked = true;
2751+
// type-checking 'catch' patterns depends on the type checked body.
2752+
if (isa<DoCatchStmt>(parentStmt))
2753+
LeaveBodyUnchecked = false;
2754+
27492755
ASTNode node(parentStmt);
27502756
TypeChecker::typeCheckASTNode(node, VD->getDeclContext(),
2751-
/*LeaveBodyUnchecked=*/true);
2757+
LeaveBodyUnchecked);
27522758
}
27532759
namingPattern = VD->getCanonicalVarDecl()->NamingPattern;
27542760
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,14 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
16691669
bool limitExhaustivityChecks = true;
16701670

16711671
Type caughtErrorType = TypeChecker::catchErrorType(DC, S);
1672+
1673+
// If there was no throwing expression in the body, let's pretend it can
1674+
// throw 'any Error' just for type checking the pattern. That avoids
1675+
// superfluous diagnostics. Note that we still diagnose unreachable 'catch'
1676+
// separately in TypeCheckEffects.
1677+
if (caughtErrorType->isNever())
1678+
caughtErrorType = Ctx.getErrorExistentialType();
1679+
16721680
auto catches = S->getCatches();
16731681
checkSiblingCaseStmts(catches.begin(), catches.end(),
16741682
CaseParentKind::DoCatch, limitExhaustivityChecks,

test/IDE/complete_exception.swift

Lines changed: 46 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,5 @@
1-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH1 | %FileCheck %s -check-prefix=CATCH1
2-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW1 > %t.throw1
3-
// RUN: %FileCheck %s -check-prefix=THROW1 < %t.throw1
4-
// RUN: %FileCheck %s -check-prefix=THROW1-LOCAL < %t.throw1
5-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH2 | %FileCheck %s -check-prefix=CATCH2
6-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW2 | %FileCheck %s -check-prefix=THROW2
7-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH3 | %FileCheck %s -check-prefix=CATCH3
8-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW3 | %FileCheck %s -check-prefix=THROW3
9-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH1 | %FileCheck %s -check-prefix=CATCH1
10-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW1 | %FileCheck %s -check-prefix=THROW1
11-
12-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH2 | %FileCheck %s -check-prefix=CATCH2
13-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW2 | %FileCheck %s -check-prefix=THROW2
14-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW3 | %FileCheck %s -check-prefix=THROW3
15-
16-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH1 > %t.inside_catch1
17-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch1
18-
// RUN: %FileCheck %s -check-prefix=IMPLICIT_ERROR < %t.inside_catch1
19-
20-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH2 > %t.inside_catch2
21-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch2
22-
// RUN: %FileCheck %s -check-prefix=EXPLICIT_ERROR_E < %t.inside_catch2
23-
24-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH3 > %t.inside_catch3
25-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch3
26-
// RUN: %FileCheck %s -check-prefix=EXPLICIT_NSERROR_E < %t.inside_catch3
27-
28-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH4 > %t.inside_catch4
29-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch4
30-
// RUN: %FileCheck %s -check-prefix=EXPLICIT_ERROR_PAYLOAD_I < %t.inside_catch4
31-
32-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH5 > %t.inside_catch5
33-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch5
34-
// RUN: %FileCheck %s -check-prefix=EXPLICIT_ERROR_E < %t.inside_catch5
35-
// RUN: %FileCheck %s -check-prefix=NO_ERROR_AND_A < %t.inside_catch5
36-
37-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH6 > %t.inside_catch6
38-
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch6
39-
// RUN: %FileCheck %s -check-prefix=NO_E < %t.inside_catch6
40-
// RUN: %FileCheck %s -check-prefix=NO_ERROR_AND_A < %t.inside_catch6
41-
42-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT1 | %FileCheck %s -check-prefix=ERROR_DOT
43-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT2 | %FileCheck %s -check-prefix=ERROR_DOT
44-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT3 | %FileCheck %s -check-prefix=NSERROR_DOT
45-
// RUNFIXME: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT4 | %FileCheck %s -check-prefix=INT_DOT
46-
47-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_INSIDE_CATCH1 > %t.top_level_inside_catch1
48-
// RUN: %FileCheck %s -check-prefix=STMT < %t.top_level_inside_catch1
49-
// RUN: %FileCheck %s -check-prefix=IMPLICIT_ERROR < %t.top_level_inside_catch1
50-
51-
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_INSIDE_CATCH_ERR_DOT1 | %FileCheck %s -check-prefix=ERROR_DOT
1+
// RUN: %empty-directory(%t/batch-code-completion)
2+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/batch-code-completion
523

534
// REQUIRES: objc_interop
545

@@ -71,10 +22,10 @@ func getNSError() -> NSError { return NSError(domain: "", code: 1, userInfo: [:]
7122
func test001() {
7223
do {} catch #^CATCH1^#
7324

74-
// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}}
75-
// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}}
76-
// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}}
77-
// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}}
25+
// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}}
26+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}}
27+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}}
28+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}}
7829
// CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}}
7930
// CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
8031
// CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}}
@@ -84,7 +35,7 @@ func test002() {
8435
let text = "NonError"
8536
let e1 = Error1()
8637
let e2 = Error2()
87-
throw #^THROW1^#
38+
throw #^THROW1?check=THROW1,THROW1-LOCAL^#
8839

8940
// THROW1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}}
9041
// THROW1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}}
@@ -93,45 +44,42 @@ func test002() {
9344
// THROW1-DAG: Decl[Protocol]/CurrModule/Flair[RareType]/TypeRelation[Convertible]: ErrorPro1[#ErrorPro1#]; name=ErrorPro1{{$}}
9445
// THROW1-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: getError1()[#Error1#]{{; name=.+$}}
9546
// THROW1-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: getNSError()[#NSError#]{{; name=.+$}}
47+
// THROW1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
9648

97-
// If we could prove that there is no way to get to an Error value by
98-
// starting from these, we could remove them. But that may be infeasible in
99-
// the presence of overloaded operators.
100-
// THROW1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
10149
// THROW1-LOCAL-DAG: Decl[LocalVar]/Local: text[#String#]; name=text{{$}}
10250
// THROW1-LOCAL-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: e1[#Error1#]; name=e1{{$}}
10351
// THROW1-LOCAL-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: e2[#Error2#]; name=e2{{$}}
10452
}
10553

10654
func test003() {
10755
do {} catch Error4.#^CATCH2^#
108-
// CATCH2: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}}
109-
// CATCH2: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}}
56+
// CATCH2-DAG: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}}
57+
// CATCH2-DAG: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}}
11058
}
11159

11260
func test004() {
11361
throw Error4.#^THROW2^#
114-
// THROW2: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]{{; name=.+$}}
115-
// THROW2: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]{{; name=.+$}}
62+
// THROW2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]{{; name=.+$}}
63+
// THROW2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]{{; name=.+$}}
11664
}
11765

11866
func test005() {
11967
do {} catch Error4.E2#^CATCH3^#
120-
// CATCH3: Pattern/CurrModule/Flair[ArgLabels]: ({#Int32#})[#Error4#]{{; name=.+$}}
68+
// CATCH3-DAG: Pattern/CurrModule/Flair[ArgLabels]: ({#Int32#})[#Error4#]{{; name=.+$}}
12169
}
12270

12371
func testInvalid() {
12472
try throw Error4.#^THROW3^#
125-
// THROW3: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]{{; name=.+$}}
126-
// THROW3: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]{{; name=.+$}}
73+
// THROW3-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]{{; name=.+$}}
74+
// THROW3-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]{{; name=.+$}}
12775
}
12876

12977
//===--- Top-level throw/catch
130-
do {} catch #^TOP_LEVEL_CATCH1^# {}
131-
throw #^TOP_LEVEL_THROW1^#
132-
do {} catch Error4.#^TOP_LEVEL_CATCH2^# {}
133-
throw Error4.#^TOP_LEVEL_THROW2^#
134-
try throw Error4.#^TOP_LEVEL_THROW3^#
78+
do {} catch #^TOP_LEVEL_CATCH1?check=CATCH1^# {}
79+
throw #^TOP_LEVEL_THROW1?check=THROW1^#
80+
do {} catch Error4.#^TOP_LEVEL_CATCH2?check=CATCH2^# {}
81+
throw Error4.#^TOP_LEVEL_THROW2?check=THROW2^#
82+
try throw Error4.#^TOP_LEVEL_THROW3?check=THROW3^#
13583

13684
//===--- Inside catch body
13785

@@ -145,38 +93,37 @@ try throw Error4.#^TOP_LEVEL_THROW3^#
14593
func test006() {
14694
do {
14795
} catch {
148-
#^INSIDE_CATCH1^#
96+
#^INSIDE_CATCH1?check=STMT,IMPLICIT_ERROR^#
14997
}
150-
// IMPLICIT_ERROR: Decl[LocalVar]/Local: error[#any Error#]; name=error
98+
// IMPLICIT_ERROR-DAG: Decl[LocalVar]/Local: error[#any Error#]; name=error
15199
}
152100
func test007() {
153101
do {
154102
} catch let e {
155-
#^INSIDE_CATCH2^#
103+
#^INSIDE_CATCH2?check=STMT,EXPLICIT_ERROR_E^#
156104
}
157-
// EXPLICIT_ERROR_E: Decl[LocalVar]/Local: e[#any Error#]; name=e
105+
// EXPLICIT_ERROR_E-DAG: Decl[LocalVar]/Local: e[#any Error#]; name=e
158106
}
159107
func test008() {
160108
do {
161109
} catch let e as NSError {
162-
#^INSIDE_CATCH3^#
110+
#^INSIDE_CATCH3?check=STMT,EXPLICIT_NSERROR_E^#
163111
}
164-
// EXPLICIT_NSERROR_E: Decl[LocalVar]/Local: e[#NSError#]; name=e
112+
// EXPLICIT_NSERROR_E-DAG: Decl[LocalVar]/Local: e[#NSError#]; name=e
165113
}
166114
func test009() {
167115
do {
168116
} catch Error4.E2(let i) {
169-
#^INSIDE_CATCH4^#
117+
#^INSIDE_CATCH4?check=STMT,EXPLICIT_ERROR_PAYLOAD_I^#
170118
}
171-
172119
// FIXME: we're getting parentheses around the type when it's unnamed...
173-
// EXPLICIT_ERROR_PAYLOAD_I: Decl[LocalVar]/Local: i[#<<error type>>#]; name=i
120+
// EXPLICIT_ERROR_PAYLOAD_I-DAG: Decl[LocalVar]/Local: i[#(Int32)#]; name=i
174121
}
175122
func test010() {
176123
do {
177124
} catch let awesomeError {
178125
} catch let e {
179-
#^INSIDE_CATCH5^#
126+
#^INSIDE_CATCH5?check=STMT,EXPLICIT_ERROR_E;check=NO_ERROR_AND_A^#
180127
} catch {}
181128
// NO_ERROR_AND_A-NOT: awesomeError
182129
// NO_ERROR_AND_A-NOT: Decl[LocalVar]/Local: error
@@ -186,26 +133,26 @@ func test011() {
186133
} catch let awesomeError {
187134
} catch let excellentError {
188135
} catch {}
189-
#^INSIDE_CATCH6^#
136+
#^INSIDE_CATCH6?check=STMT;check=NO_ERROR_AND_A,NO_E^#
190137
// NO_E-NOT: excellentError
191138
}
192139
func test012() {
193140
do {
194141
} catch {
195-
error.#^INSIDE_CATCH_ERR_DOT1^#
142+
error.#^INSIDE_CATCH_ERR_DOT1?check=ERROR_DOT^#
196143
}
197144
}
198-
// ERROR_DOT: Keyword[self]/CurrNominal: self[#any Error#]; name=self
145+
// ERROR_DOT-DAG: Keyword[self]/CurrNominal: self[#any Error#]; name=self
199146
func test013() {
200147
do {
201148
} catch let e {
202-
e.#^INSIDE_CATCH_ERR_DOT2^#
149+
e.#^INSIDE_CATCH_ERR_DOT2?check=ERROR_DOT^#
203150
}
204151
}
205152
func test014() {
206153
do {
207154
} catch let e as NSError {
208-
e.#^INSIDE_CATCH_ERR_DOT3^#
155+
e.#^INSIDE_CATCH_ERR_DOT3?check=NSERROR_DOT^#
209156
}
210157
// NSERROR_DOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: domain[#String#]; name=domain
211158
// NSERROR_DOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: code[#Int#]; name=code
@@ -218,7 +165,7 @@ func test014() {
218165
func test015() {
219166
do {
220167
} catch Error4.E2(let i) where i == 2 {
221-
i.#^INSIDE_CATCH_ERR_DOT4^#
168+
i.#^INSIDE_CATCH_ERR_DOT4?check=INT_DOT^#
222169
}
223170
}
224171
// Check that we can complete on the bound value; Not exhaustive..
@@ -228,9 +175,18 @@ func test015() {
228175
//===--- Inside catch body top-level
229176
do {
230177
} catch {
231-
#^TOP_LEVEL_INSIDE_CATCH1^#
178+
#^TOP_LEVEL_INSIDE_CATCH1?check=STMT,IMPLICIT_ERROR^#
232179
}
233180
do {
234181
} catch {
235-
error.#^TOP_LEVEL_INSIDE_CATCH_ERR_DOT1^#
182+
error.#^TOP_LEVEL_INSIDE_CATCH_ERR_DOT1?check=ERROR_DOT^#
183+
}
184+
185+
func canThrowError4() throws(Error4) {}
186+
func test016() {
187+
do {
188+
try canThrowError4()
189+
} catch .E2(let i) {
190+
i.#^INSIDE_CATCH_TYPEDERR_DOT?check=INT_DOT^#
191+
}
236192
}

test/Sema/redeclaration-checking.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ func stmtTest() {
8787
// expected-note@-1 {{'x' previously declared here}}
8888
// expected-error@-2 {{invalid redeclaration of 'x'}}
8989
// expected-warning@-3 {{unreachable}}
90-
// expected-error@-4{{pattern of type 'MyError' cannot match 'Never'}}
9190
}
9291

9392
func fullNameTest() {

test/StringProcessing/Parse/forward-slash-regex.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ default:
262262
}
263263

264264
do {} catch /x/ {}
265-
// expected-error@-1 {{expression pattern of type 'Regex<Substring>' cannot match values of type 'Never'}}
265+
// expected-error@-1 {{expression pattern of type 'Regex<Substring>' cannot match values of type 'any Error'}}
266266
// expected-warning@-2 {{'catch' block is unreachable because no errors are thrown in 'do' block}}
267267

268268
switch /x/ {

test/stmt/typed_throws.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,8 @@ func testTryIncompatibleTyped(cond: Bool) throws(HomeworkError) {
134134
}
135135
} catch let error as Never {
136136
// expected-warning@-1{{'catch' block is unreachable because no errors are thrown in 'do' block}}
137-
// expected-warning@-2{{'as' test is always true}}
138137
throw .forgot
139-
}
138+
} // expected-error {{thrown expression type 'any Error' cannot be converted to error type 'HomeworkError'}}
140139
}
141140

142141
func doSomethingWithoutThrowing() { }
@@ -145,7 +144,6 @@ func testDoCatchWithoutThrowing() {
145144
do {
146145
try doSomethingWithoutThrowing() // expected-warning{{no calls to throwing functions occur within 'try' expression}}
147146
} catch HomeworkError.forgot { // expected-warning{{'catch' block is unreachable because no errors are thrown in 'do' block}}
148-
// expected-error@-1{{pattern of type 'HomeworkError' cannot match 'Never'}}
149147
} catch {
150148
}
151149
}

0 commit comments

Comments
 (0)