diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 5a1f4da04cc..6b070287bb4 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -2718,8 +2718,10 @@ extension Parser.Lookahead { } // Parse pattern-tuple func-signature-result? 'in'. + var isBracketed = false if lookahead.at(.leftParen) { // Consume the '('. // While we don't have '->' or ')', eat balanced tokens. + isBracketed = true var skipProgress = LoopProgressCondition() while !lookahead.at(.endOfFile, .rightBrace, .keyword(.in)) && !lookahead.at(.arrow) && lookahead.hasProgressed(&skipProgress) @@ -2754,7 +2756,10 @@ extension Parser.Lookahead { } // Consume the ')', if it's there. - lookahead.consume(if: .rightParen) + var hasRightParen = false + if isBracketed { + hasRightParen = lookahead.consume(if: .rightParen) != nil + } lookahead.consumeEffectsSpecifiers() @@ -2775,9 +2780,14 @@ extension Parser.Lookahead { return true } + // If we started with '(', we MUST have found a ')' to trust the arrow. + if isBracketed && !hasRightParen { + return false + } // Even if 'in' is missing, the presence of a top-level '->' makes this look like a // closure signature. There's no other valid syntax that could legally // contain '->' at this position. + return sawTopLevelArrowInLookahead } } diff --git a/Tests/SwiftParserTest/Issue3163.swift b/Tests/SwiftParserTest/Issue3163.swift new file mode 100644 index 00000000000..4e07ba495ec --- /dev/null +++ b/Tests/SwiftParserTest/Issue3163.swift @@ -0,0 +1,23 @@ +import SwiftParser +import SwiftParserDiagnostics +import SwiftSyntax +import XCTest + +public class Issue3163Tests: XCTestCase { + func testFixForIssue3163() { + let source = "struct Foo {func a(s: S [{{g) -> Int {}}}}" + + // Parse the code + let tree = Parser.parse(source: source) + + // Get the error messages + let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: tree) + + // CHECK: Ensure the specific "missing 'in'" error is NOT present + let hasBadError = diagnostics.contains { $0.message.contains("expected 'in' in closure signature") } + + // This will pass if the error is gone (FIXED). + // This will fail if the error is still there (BROKEN). + XCTAssertFalse(hasBadError, "The parser incorrectly identified a closure signature and asked for 'in'") + } +}