Skip to content

Commit 9b68204

Browse files
authored
Merge pull request #84278 from kwikysw/parse-closure-missing-in
[Parse] Diagnose missing 'in' after closure signature
2 parents 00c1eb8 + c14ba92 commit 9b68204

File tree

3 files changed

+45
-8
lines changed

3 files changed

+45
-8
lines changed

lib/Parse/ParseExpr.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2546,7 +2546,9 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
25462546
skipSingle();
25472547
}
25482548
};
2549-
2549+
2550+
bool sawTopLevelArrowInLookahead = false;
2551+
25502552
// If we have a leading token that may be part of the closure signature, do a
25512553
// speculative parse to validate it and look for 'in'.
25522554
if (Tok.isAny(
@@ -2588,6 +2590,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
25882590

25892591
// Parse the func-signature-result, if present.
25902592
if (consumeIf(tok::arrow)) {
2593+
sawTopLevelArrowInLookahead = true;
25912594
if (!canParseType())
25922595
return makeParserSuccess();
25932596

@@ -2612,17 +2615,23 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
26122615

26132616
// Parse the func-signature-result, if present.
26142617
if (consumeIf(tok::arrow)) {
2618+
sawTopLevelArrowInLookahead = true;
26152619
if (!canParseType())
26162620
return makeParserSuccess();
2617-
2621+
26182622
consumeEffectsSpecifiers();
26192623
}
26202624
}
26212625

26222626
// Parse the 'in' at the end.
2623-
if (Tok.isNot(tok::kw_in))
2624-
return makeParserSuccess();
2627+
if (Tok.isNot(tok::kw_in)) {
2628+
// Even if 'in' is missing, the presence of '->' makes this look
2629+
// like a closure signature. There's no other valid syntax that
2630+
// could legally contain '->' at this position.
2631+
if (!sawTopLevelArrowInLookahead)
2632+
return makeParserSuccess();
26252633

2634+
}
26262635
// Okay, we have a closure signature.
26272636
} else {
26282637
// No closure signature.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -parse -verify -swift-version 5 %s
2+
3+
func test<T>(make: () -> [T], consume: (T) -> Void) { consume(make()[0]) }
4+
5+
func main1() {
6+
test(make: { () -> [Int] // expected-error@+1 {{expected 'in' after the closure signature}}
7+
return [3]
8+
}, consume: { _ in })
9+
}
10+
11+
12+
// Resync path: there are tokens before `in` — should diagnose once, then recover.
13+
func main2() {
14+
_ = { () -> Int
15+
0 // expected-error {{unexpected tokens prior to 'in'}}
16+
in
17+
1
18+
}
19+
}
20+
21+
func main3() {
22+
_ = { x, y -> Int
23+
x + y // expected-error {{expected 'in' after the closure signature}}
24+
}
25+
}
26+
27+
func ok() {
28+
_ = { (x: Int) -> Int in x + 1 }
29+
}

test/Parse/type_expr.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,9 @@ func testFunctionCollectionTypes() {
344344

345345
func testInvalidArrowWithClosure() {
346346
_ = { undefined -> undefined2 }
347-
// expected-error@-1 {{cannot find 'undefined' in scope}}
348-
// expected-error@-2 {{cannot find 'undefined2' in scope}}
349-
// expected-error@-3 {{expected type before '->'}}
350-
// expected-error@-4 {{expected type after '->'}}
347+
// expected-error@-1 {{expected 'in' after the closure signature}}
348+
// expected-error@-2 {{cannot find type 'undefined2' in scope}}
349+
// expected-error@-3 {{cannot infer type of closure parameter 'undefined' without a type annotation}}
351350

352351
() -> { let x: Int = "" }
353352
// expected-error@-1 {{expected type after '->'}}

0 commit comments

Comments
 (0)