diff --git a/Sources/SwiftParser/Patterns.swift b/Sources/SwiftParser/Patterns.swift index 81007bbd5ff..0304d29c663 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 1db5b02763d..33ea874aeb5 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -315,7 +315,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 d768064ee74..291d402884e 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -806,19 +806,39 @@ 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 } } """#, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "found an unexpected second identifier in struct; is there an accidental break?", fixIts: ["join the identifiers together"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "expected ':' in type annotation"), - DiagnosticSpec(locationMarker: "3️⃣", message: #"unexpected code ': Int = ""' before function"#), - DiagnosticSpec(locationMarker: "4️⃣", message: "expected ':' in type annotation"), - ] + DiagnosticSpec( + locationMarker: "1️⃣", + message: "found an unexpected second identifier in struct; is there an accidental break?", + fixIts: ["join the identifiers together"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + 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: "expected ':' in type annotation", + fixIts: ["insert ':'"] + ), + ], + fixedSource: #""" + struct SSSS : Multi { + private var ab : Int = "" + func f() { + var c: d = 5 + let _ = 0 + } + } + """# ) } @@ -850,6 +870,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( """