From 5887f405e92ea7853463a76f7c92a0dad007535b Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Fri, 17 May 2024 23:19:22 -0700 Subject: [PATCH] Fix up a mismatch between effect specifier lookahead and parsing Lookahead was not consuming typed throw's error argument during lookahead for closures. `atFunctionTypeArrow` did mostly handle it, but only consumed a single token and thus wouldn't match `throws(any Error)`. Handle this in `consumeEffectsSpecifiers` and then use this for both. Fixes #2648. Resolves rdar://127750606. --- Sources/SwiftParser/Expressions.swift | 7 +++- Sources/SwiftParser/Types.swift | 45 +++------------------ Tests/SwiftParserTest/ExpressionTests.swift | 19 +++++++++ 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 7c560368694..2a7535d4fb2 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -2428,10 +2428,15 @@ extension Parser.Lookahead { // Consume 'async', 'throws', and 'rethrows', but in any order. mutating func consumeEffectsSpecifiers() { var loopProgress = LoopProgressCondition() - while let (_, handle) = self.at(anyIn: EffectSpecifier.self), + while let (spec, handle) = self.at(anyIn: EffectSpecifier.self), self.hasProgressed(&loopProgress) { self.eat(handle) + + if spec.isThrowsSpecifier, self.consume(if: .leftParen) != nil { + _ = self.canParseSimpleOrCompositionType() + self.consume(if: .rightParen) + } } } diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 133ecccd43f..73be3d11c53 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -37,7 +37,7 @@ extension Parser { mutating func parseTypeScalar(misplacedSpecifiers: [RawTokenSyntax] = []) -> RawTypeSyntax { let specifiersAndAttributes = self.parseTypeAttributeList(misplacedSpecifiers: misplacedSpecifiers) var base = RawTypeSyntax(self.parseSimpleOrCompositionType()) - if self.withLookahead({ $0.atFunctionTypeArrow() }) { + if self.withLookahead({ $0.canParseFunctionTypeArrow() }) { var effectSpecifiers = self.parseTypeEffectSpecifiers() let returnClause = self.parseFunctionReturnClause( effectSpecifiers: &effectSpecifiers, @@ -656,21 +656,9 @@ extension Parser.Lookahead { return false } - if self.atFunctionTypeArrow() { - // Handle type-function if we have an '->' with optional - // 'async' and/or 'throws'. - var loopProgress = LoopProgressCondition() - while let (_, handle) = self.at(anyIn: EffectSpecifier.self), self.hasProgressed(&loopProgress) { - self.eat(handle) - } - - guard self.consume(if: .arrow) != nil else { - return false - } - + if self.canParseFunctionTypeArrow() { return self.canParseType() } - return true } @@ -819,33 +807,12 @@ extension Parser.Lookahead { return self.consume(if: .rightParen) != nil } - mutating func atFunctionTypeArrow() -> Bool { - if self.at(.arrow) { + mutating func canParseFunctionTypeArrow() -> Bool { + if self.consume(if: .arrow) != nil { return true } - - if let effect = self.at(anyIn: EffectSpecifier.self) { - if self.peek().rawTokenKind == .arrow { - return true - } - - if effect.spec.isThrowsSpecifier && self.peek().rawTokenKind == .leftParen { - var lookahead = self.lookahead() - lookahead.consumeAnyToken() - lookahead.skipSingle() - return lookahead.atFunctionTypeArrow() - } - - if peek(isAtAnyIn: EffectSpecifier.self) != nil { - var lookahead = self.lookahead() - lookahead.consumeAnyToken() - return lookahead.atFunctionTypeArrow() - } - - return false - } - - return false + self.consumeEffectsSpecifiers() + return self.consume(if: .arrow) != nil } mutating func canParseTypeIdentifier(allowKeyword: Bool = false) -> Bool { diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index eba62a0aec5..786c61bda43 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -2972,6 +2972,9 @@ final class StatementExpressionTests: ParserTestCase { assertParse( "[() throws(MyError) -> Void]()" ) + assertParse( + "[() throws(any Error) -> Void]()" + ) assertParse( "X<() throws(MyError) -> Int>()" ) @@ -2980,6 +2983,22 @@ final class StatementExpressionTests: ParserTestCase { ) } + func testTypedThrowsClosureParam() { + assertParse( + """ + try foo { (a, b) throws(S) in 1 } + """ + ) + } + + func testTypedThrowsShorthandClosureParams() { + assertParse( + """ + try foo { a, b throws(S) in 1 } + """ + ) + } + func testArrayExprWithNoCommas() { assertParse("[() ()]")