diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b6e94d0120d4b..167d4e7b8bab0 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -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( @@ -2588,6 +2590,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( // Parse the func-signature-result, if present. if (consumeIf(tok::arrow)) { + sawTopLevelArrowInLookahead = true; if (!canParseType()) return makeParserSuccess(); @@ -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. diff --git a/test/Parse/closure-missing-in.swift b/test/Parse/closure-missing-in.swift new file mode 100644 index 0000000000000..bcb2a2846d0d7 --- /dev/null +++ b/test/Parse/closure-missing-in.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend -parse -verify -swift-version 5 %s + +func test(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 } +} diff --git a/test/Parse/type_expr.swift b/test/Parse/type_expr.swift index eb5ad76179445..dbee822c29dbe 100644 --- a/test/Parse/type_expr.swift +++ b/test/Parse/type_expr.swift @@ -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 '->'}}