Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2546,7 +2546,9 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
skipSingle();
}
};


bool sawTopLevelArrowInLookahead = false;

// If we have a leading token that may be part of the closure signature, do a
// speculative parse to validate it and look for 'in'.
if (Tok.isAny(
Expand Down Expand Up @@ -2588,6 +2590,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent(

// Parse the func-signature-result, if present.
if (consumeIf(tok::arrow)) {
sawTopLevelArrowInLookahead = true;
if (!canParseType())
return makeParserSuccess();

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

// Parse the func-signature-result, if present.
if (consumeIf(tok::arrow)) {
sawTopLevelArrowInLookahead = true;
if (!canParseType())
return makeParserSuccess();

consumeEffectsSpecifiers();
}
}

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

}
// Okay, we have a closure signature.
} else {
// No closure signature.
Expand Down
29 changes: 29 additions & 0 deletions test/Parse/closure-missing-in.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %target-swift-frontend -parse -verify -swift-version 5 %s

func test<T>(make: () -> [T], consume: (T) -> Void) { consume(make()[0]) }

func main1() {
test(make: { () -> [Int] // expected-error@+1 {{expected 'in' after the closure signature}}
return [3]
}, consume: { _ in })
}


// Resync path: there are tokens before `in` — should diagnose once, then recover.
func main2() {
_ = { () -> Int
0 // expected-error {{unexpected tokens prior to 'in'}}
in
1
}
}

func main3() {
_ = { x, y -> Int
x + y // expected-error {{expected 'in' after the closure signature}}
}
}

func ok() {
_ = { (x: Int) -> Int in x + 1 }
}
7 changes: 3 additions & 4 deletions test/Parse/type_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,9 @@ func testFunctionCollectionTypes() {

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

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