diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index f6b7e05bea8..7b388f58764 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1069,6 +1069,30 @@ extension Parser { arena: self.arena ) ) + } else if self.at(.colon) { + var lookahead = self.lookahead() + + // We only want to continue with a dictionary if we can parse a colon and a simpletype. + // Otherwise we can get a wrong diagnostic if we get a Python-style function declaration. + guard lookahead.consume(if: .colon) != nil && lookahead.canParseSimpleType() else { + return result + } + + let (unexpectedBeforeColon, colon) = self.expect(.colon) + let secondType = self.parseSimpleType() + let rightSquareBracket = self.consume(if: .rightSquareBracket) ?? self.missingToken(.rightSquareBracket) + + result = RawTypeSyntax( + RawDictionaryTypeSyntax( + leftSquareBracket: self.missingToken(.leftSquareBracket), + keyType: result, + unexpectedBeforeColon, + colon: colon, + valueType: secondType, + rightSquareBracket: rightSquareBracket, + arena: self.arena + ) + ) } var loopProgress = LoopProgressCondition() diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index ac9a93d1294..9ef9f1bb7aa 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -1204,22 +1204,63 @@ final class RecoveryTests: XCTestCase { """ struct ErrorTypeInVarDeclDictionaryType { let a1: String1️⃣: - let a2: String2️⃣: Int] - let a3: String3️⃣: [Int] - let a4: String4️⃣: Int + let a2: 2️⃣String: Int] + let a3: 3️⃣String: [Int]4️⃣ + let a4: 5️⃣String: Int6️⃣ + let a4: 7️⃣String: Int]?? } """, diagnostics: [ - // TODO: Old parser expected error on line 2: unexpected ':' in type; did you mean to write a dictionary type?, Fix-It replacements: 11 - 11 = '[' - DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"]), - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code ':' before variable"), - // TODO: Old parser expected error on line 3: unexpected ':' in type; did you mean to write a dictionary type?, Fix-It replacements: 11 - 11 = '[' - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code ': Int]' before variable"), - // TODO: Old parser expected error on line 4: unexpected ':' in type; did you mean to write a dictionary type?, Fix-It replacements: 11 - 11 = '[', 24 - 24 = ']' - DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code ': [Int]' before variable"), - // TODO: Old parser expected error on line 5: unexpected ':' in type; did you mean to write a dictionary type?, Fix-It replacements: 11 - 11 = '[', 22 - 22 = ']' - DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code ': Int' in struct"), - ] + DiagnosticSpec( + locationMarker: "1️⃣", + message: "consecutive declarations on a line must be separated by ';'", + fixIts: ["insert ';'"] + ), + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code ':' before variable" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected '[' to start dictionary type", + fixIts: ["insert '['"] + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "expected '[' to start dictionary type", + fixIts: ["insert '['"] + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected ']' to end dictionary type", + fixIts: ["insert ']'"] + ), + DiagnosticSpec( + locationMarker: "5️⃣", + message: "expected '[' to start dictionary type", + fixIts: ["insert '['"] + ), + DiagnosticSpec( + locationMarker: "6️⃣", + message: "expected ']' to end dictionary type", + fixIts: ["insert ']'"] + ), + DiagnosticSpec( + locationMarker: "7️⃣", + message: "expected '[' to start dictionary type", + fixIts: ["insert '['"] + ), + + ], + fixedSource: """ + struct ErrorTypeInVarDeclDictionaryType { + let a1: String;: + let a2: [String: Int] + let a3: [String: [Int]] + let a4: [String: Int] + let a4: [String: Int]?? + } + """ ) } @@ -2340,4 +2381,14 @@ final class RecoveryTests: XCTestCase { """ ) } + + // https://github.com/apple/swift-syntax/pull/1484/files#r1176740738 + func testRecovery184() { + assertParse( + "func foo() -> Int1️⃣:", + diagnostics: [ + DiagnosticSpec(message: "extraneous code ':' at top level") + ] + ) + } }