diff --git a/Sources/SwiftParser/Patterns.swift b/Sources/SwiftParser/Patterns.swift index 9c7395afbaa..9b3d12988e6 100644 --- a/Sources/SwiftParser/Patterns.swift +++ b/Sources/SwiftParser/Patterns.swift @@ -175,10 +175,12 @@ extension Parser { && !self.currentToken.isAtStartOfLine && lookahead.canParseType() { - // Recovery if the user forgot to add ':' - let result = self.parseResultType() + let (unexpectedBeforeColon, colon) = self.expect(.colon) + let result = self.parseType() + type = RawTypeAnnotationSyntax( - colon: self.missingToken(.colon), + unexpectedBeforeColon, + colon: colon, type: result, arena: self.arena ) diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift index 913e5109dba..40c474f59e2 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -331,7 +331,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { } else if node.first?.as(TokenSyntax.self)?.tokenKind.isIdentifier == true, let previousToken = node.previousToken(viewMode: .sourceAccurate), previousToken.tokenKind.isIdentifier, - previousToken.parent?.is(DeclSyntax.self) == true + previousToken.parent?.is(DeclSyntax.self) == true || previousToken.parent?.is(IdentifierPatternSyntax.self) == true { // If multiple identifiers are used for a declaration name, offer to join them together. let tokens = diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index eee8a0d36ad..ac9a93d1294 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -813,9 +813,9 @@ final class RecoveryTests: XCTestCase { assertParse( #""" struct SS 1️⃣SS : Multi { - private var a 2️⃣b 3️⃣: Int = "" + private var a 2️⃣b : Int = "" func f() { - var c 4️⃣d = 5 + var c 3️⃣d = 5 let _ = 0 } } @@ -828,16 +828,24 @@ final class RecoveryTests: XCTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "expected ':' in type annotation", - fixIts: ["insert ':'"] + message: "found an unexpected second identifier in pattern; is there an accidental break?", + fixIts: ["join the identifiers together", "join the identifiers together with camel-case"] ), - DiagnosticSpec(locationMarker: "3️⃣", message: #"unexpected code ': Int = ""' before function"#), DiagnosticSpec( - locationMarker: "4️⃣", + locationMarker: "3️⃣", message: "expected ':' in type annotation", fixIts: ["insert ':'"] ), - ] + ], + fixedSource: #""" + struct SSSS : Multi { + private var ab : Int = "" + func f() { + var c: d = 5 + let _ = 0 + } + } + """# ) } @@ -869,6 +877,24 @@ final class RecoveryTests: XCTestCase { ) } + func testRecovery64c() { + assertParse( + """ + private var a 1️⃣b : Int = "" + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "found an unexpected second identifier in pattern; is there an accidental break?", + fixIts: ["join the identifiers together", "join the identifiers together with camel-case"] + ) + ], + fixedSource: """ + private var ab : Int = "" + """ + ) + } + func testRecovery65() { assertParse( """