Skip to content

Skip parsing trailing closures after literals #2313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
}
7 changes: 7 additions & 0 deletions Tests/SwiftParserTest/DeclarationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3059,4 +3059,11 @@ final class DeclarationTests: ParserTestCase {
"""
)
}

func testLiteralInitializerWithTrailingClosure() {
assertParse(
"let foo = 1 { return 1 }",
substructure: AccessorBlockSyntax(accessors: .getter([CodeBlockItemSyntax("return 1")]))
)
}
}
84 changes: 79 additions & 5 deletions Tests/SwiftParserTest/ExpressionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
"""
)
}
}
Expand Down
9 changes: 9 additions & 0 deletions Tests/SwiftParserTest/LexerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
]
)
}
}
32 changes: 22 additions & 10 deletions Tests/SwiftParserTest/translated/RecoveryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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]
}]
}
Expand Down Expand Up @@ -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",
Expand All @@ -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++}]
{
}
}
"""
Expand Down