diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 69ef9ab61d2..390d9a79b10 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -790,7 +790,7 @@ extension Parser { } // Check for a trailing closure, if allowed. - if self.at(.leftBrace) && self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) { + if self.at(.leftBrace) && !leadingExpr.raw.kind.isLiteral && self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) { // FIXME: if Result has a trailing closure, break out. // Add dummy blank argument list to the call expression syntax. let list = RawLabeledExprListSyntax(elements: [], arena: self.arena) @@ -2543,3 +2543,14 @@ extension Parser.Lookahead { } } } + +extension SyntaxKind { + fileprivate var isLiteral: Bool { + switch self { + case .arrayExpr, .booleanLiteralExpr, .dictionaryExpr, .floatLiteralExpr, .integerLiteralExpr, .nilLiteralExpr, .regexLiteralExpr, .stringLiteralExpr: + return true + default: + return false + } + } +} diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index ee24ab0d121..96b65969710 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -3059,4 +3059,11 @@ final class DeclarationTests: ParserTestCase { """ ) } + + func testLiteralInitializerWithTrailingClosure() { + assertParse( + "let foo = 1 { return 1 }", + substructure: AccessorBlockSyntax(accessors: .getter([CodeBlockItemSyntax("return 1")])) + ) + } } diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index fab76ddd87d..853520a2be8 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -1463,12 +1463,86 @@ final class ExpressionTests: ParserTestCase { ) } - func testNulCharacterInSourceFile() { + func testLiteralWithTrailingClosure() { + let expectedDiagnostics = [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ) + ] + assertParse( - "let a = 1️⃣\u{0}1", - diagnostics: [ - DiagnosticSpec(message: "nul character embedded in middle of file", severity: .warning) - ] + "_ = true1️⃣ { return true }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = true + { return true } + """ + ) + assertParse( + "_ = nil1️⃣ { return nil }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = nil + { return nil } + """ + ) + assertParse( + "_ = 11️⃣ { return 1 }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = 1 + { return 1 } + """ + ) + assertParse( + "_ = 1.01️⃣ { return 1.0 }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = 1.0 + { return 1.0 } + """ + ) + assertParse( + #"_ = "foo"1️⃣ { return "foo" }"#, + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = "foo" + { return "foo" } + """ + ) + assertParse( + "_ = /foo/1️⃣ { return /foo/ }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = /foo/ + { return /foo/ } + """ + ) + assertParse( + "_ = [1]1️⃣ { return [1] }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = [1] + { return [1] } + """ + ) + assertParse( + "_ = [1: 1]1️⃣ { return [1: 1] }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = [1: 1] + { return [1: 1] } + """ + ) + + assertParse( + "_ = 1 + 11️⃣ { return 1 }", + diagnostics: expectedDiagnostics, + fixedSource: """ + _ = 1 + 1 + { return 1 } + """ ) } } diff --git a/Tests/SwiftParserTest/LexerTests.swift b/Tests/SwiftParserTest/LexerTests.swift index 38d62424434..284ea7e1c07 100644 --- a/Tests/SwiftParserTest/LexerTests.swift +++ b/Tests/SwiftParserTest/LexerTests.swift @@ -1495,4 +1495,13 @@ public class LexerTests: ParserTestCase { ] ) } + + func testNulCharacterInSourceFile() { + assertParse( + "let a = 1️⃣\u{0}1", + diagnostics: [ + DiagnosticSpec(message: "nul character embedded in middle of file", severity: .warning) + ] + ) + } } diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index 1f2f9428685..37be18c06fe 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -1810,14 +1810,14 @@ final class RecoveryTests: ParserTestCase { assertParse( """ struct ErrorInFunctionSignatureResultArrayType2 1️⃣{ - func foo() -> Int2️⃣[0 { + func foo() -> Int2️⃣[0 3️⃣{ return [0] - }3️⃣ - 4️⃣} + }4️⃣ + 5️⃣} """, diagnostics: [ - // TODO: Old parser expected error on line 2: expected ']' in array type - // TODO: Old parser expected note on line 2: to match this opening '[' + // TODO: Old parser expected error to add `]` on line 2, but we should just recover to + // `{` with `[0` becoming unexpected. DiagnosticSpec( locationMarker: "2️⃣", message: "expected '}' to end struct", @@ -1826,19 +1826,24 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "3️⃣", + message: "expected ',' in array element", + fixIts: ["insert ','"] + ), + DiagnosticSpec( + locationMarker: "4️⃣", message: "expected ']' to end array", notes: [NoteSpec(locationMarker: "2️⃣", message: "to match this opening '['")], fixIts: ["insert ']'"] ), DiagnosticSpec( - locationMarker: "4️⃣", + locationMarker: "5️⃣", message: "extraneous brace at top level" ), ], fixedSource: """ struct ErrorInFunctionSignatureResultArrayType2 { func foo() -> Int - }[0 { + }[0, { return [0] }] } @@ -1895,11 +1900,12 @@ final class RecoveryTests: ParserTestCase { assertParse( """ struct ErrorInFunctionSignatureResultArrayType11 ℹ️{ - func foo() -> Int1️⃣[(a){a++}] { + func foo() -> Int1️⃣[(a){a++}]2️⃣ { } - 2️⃣} + 3️⃣} """, diagnostics: [ + // TODO: We should just recover to `{` with `[(a){a++}]` becoming unexpected. DiagnosticSpec( locationMarker: "1️⃣", message: "expected '}' to end struct", @@ -1908,13 +1914,19 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + locationMarker: "3️⃣", message: "extraneous brace at top level" ), ], fixedSource: """ struct ErrorInFunctionSignatureResultArrayType11 { func foo() -> Int - }[(a){a++}] { + }[(a){a++}] + { } } """