diff --git a/Sources/SwiftParser/Parameters.swift b/Sources/SwiftParser/Parameters.swift index 3c592f90476..355684d109f 100644 --- a/Sources/SwiftParser/Parameters.swift +++ b/Sources/SwiftParser/Parameters.swift @@ -96,11 +96,33 @@ extension Parser { let modifiers = parseParameterModifiers(isClosure: false) let misplacedSpecifiers = parseMisplacedSpecifiers() - var names = self.parseParameterNames() - let (unexpectedBeforeColon, colon) = self.expect(.colon) + var names: ParameterNames + let unexpectedBeforeColon: RawUnexpectedNodesSyntax? + let colon: RawTokenSyntax let type: RawTypeSyntax + // try to parse the type regardless of the presence of the preceding colon + // to tackle any unnamed parameter or missing colon + // e.g. [X], (:[X]) or (x [X]) + let canParseType = withLookahead { + $0.currentToken.tokenText.isStartingWithUppercase && $0.canParseType() && $0.at(.comma, .rightParen) + } + + if canParseType { + names = ParameterNames( + unexpectedBeforeFirstName: nil, + firstName: nil, + unexpectedBeforeSecondName: nil, + secondName: nil + ) + unexpectedBeforeColon = nil + colon = missingToken(.colon) + } else { + names = self.parseParameterNames() + (unexpectedBeforeColon, colon) = self.expect(.colon) + } + if colon.presence == .missing, let secondName = names.secondName, secondName.tokenKind == .identifier, diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index 1038026eb26..ef1b83f699f 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -3403,4 +3403,30 @@ final class StatementExpressionTests: ParserTestCase { substructureAfterMarker: "1️⃣" ) } + + func testFunctionParameterWithMalformedParameters() { + assertParse( + """ + init(1️⃣String.Index, x:(Int) -> Int) rethrows + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier and ':' in parameter", fixIts: ["insert identifier and ':'"]) + ], + fixedSource: """ + init(<#identifier#>: String.Index, x:(Int) -> Int) rethrows + """ + ) + + assertParse( + """ + init(1️⃣String.Index) rethrows + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier and ':' in parameter", fixIts: ["insert identifier and ':'"]) + ], + fixedSource: """ + init(<#identifier#>: String.Index) rethrows + """ + ) + } }