Skip to content

Commit 8785276

Browse files
committed
Fix missing diagnostics for multiple expressions on the same line in closures, switches, and getters
Switch loops that used `parseCodeBlockItem` without semicolon checking over to using `parseCodeBlockItemList`.
1 parent 9c2899d commit 8785276

File tree

5 files changed

+60
-34
lines changed

5 files changed

+60
-34
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,20 +1768,14 @@ extension Parser {
17681768
)
17691769
}
17701770

1771-
var body = [RawCodeBlockItemSyntax]()
1772-
var codeBlockProgress = LoopProgressCondition()
1773-
while !self.at(.rightBrace),
1774-
let newItem = self.parseCodeBlockItem(),
1775-
codeBlockProgress.evaluate(currentToken)
1776-
{
1777-
body.append(newItem)
1778-
}
1771+
let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) })
1772+
17791773
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
17801774
return .getter(
17811775
RawCodeBlockSyntax(
17821776
unexpectedBeforeLBrace,
17831777
leftBrace: lbrace,
1784-
statements: RawCodeBlockItemListSyntax(elements: body, arena: self.arena),
1778+
statements: body,
17851779
unexpectedBeforeRBrace,
17861780
rightBrace: rbrace,
17871781
arena: self.arena

Sources/SwiftParser/Expressions.swift

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,21 +1710,15 @@ extension Parser {
17101710
let signature = self.parseClosureSignatureIfPresent()
17111711

17121712
// Parse the body.
1713-
var elements = [RawCodeBlockItemSyntax]()
1714-
do {
1715-
var loopProgress = LoopProgressCondition()
1716-
while !self.at(.rightBrace), let newItem = self.parseCodeBlockItem(), loopProgress.evaluate(currentToken) {
1717-
elements.append(newItem)
1718-
}
1719-
}
1713+
let elements = parseCodeBlockItemList(until: { $0.at(.rightBrace) })
17201714

17211715
// Parse the closing '}'.
17221716
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
17231717
return RawClosureExprSyntax(
17241718
unexpectedBeforeLBrace,
17251719
leftBrace: lbrace,
17261720
signature: signature,
1727-
statements: RawCodeBlockItemListSyntax(elements: elements, arena: arena),
1721+
statements: elements,
17281722
unexpectedBeforeRBrace,
17291723
rightBrace: rbrace,
17301724
arena: self.arena
@@ -2326,16 +2320,9 @@ extension Parser {
23262320
}
23272321

23282322
mutating func parseSwitchCaseBody() -> RawCodeBlockItemListSyntax {
2329-
var items = [RawCodeBlockItemSyntax]()
2330-
var loopProgress = LoopProgressCondition()
2331-
while !self.at(.rightBrace) && !self.at(.poundEndifKeyword, .poundElseifKeyword, .poundElseKeyword)
2332-
&& !self.withLookahead({ $0.isStartOfConditionalSwitchCases() }),
2333-
let newItem = self.parseCodeBlockItem(),
2334-
loopProgress.evaluate(currentToken)
2335-
{
2336-
items.append(newItem)
2337-
}
2338-
return RawCodeBlockItemListSyntax(elements: items, arena: self.arena)
2323+
parseCodeBlockItemList(until: {
2324+
$0.at(.rightBrace) || $0.at(.poundEndifKeyword, .poundElseifKeyword, .poundElseKeyword) || $0.withLookahead({ $0.isStartOfConditionalSwitchCases() })
2325+
})
23392326
}
23402327

23412328
/// Parse a single switch case clause.

Sources/SwiftParser/TopLevel.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ extension Parser {
5959
}
6060

6161
extension Parser {
62-
mutating func parseCodeBlockItemList(isAtTopLevel: Bool, allowInitDecl: Bool = true, stopCondition: (inout Parser) -> Bool) -> RawCodeBlockItemListSyntax {
62+
mutating func parseCodeBlockItemList(isAtTopLevel: Bool = false, allowInitDecl: Bool = true, until stopCondition: (inout Parser) -> Bool) -> RawCodeBlockItemListSyntax {
6363
var elements = [RawCodeBlockItemSyntax]()
6464
var loopProgress = LoopProgressCondition()
6565
while !stopCondition(&self), loopProgress.evaluate(currentToken) {
@@ -89,7 +89,7 @@ extension Parser {
8989
///
9090
/// top-level-declaration → statements?
9191
mutating func parseTopLevelCodeBlockItems() -> RawCodeBlockItemListSyntax {
92-
return parseCodeBlockItemList(isAtTopLevel: true, stopCondition: { _ in false })
92+
return parseCodeBlockItemList(isAtTopLevel: true, until: { _ in false })
9393
}
9494

9595
/// The optional form of `parseCodeBlock` that checks to see if the parser has
@@ -116,7 +116,7 @@ extension Parser {
116116
/// indented to close this code block or a surrounding context. See `expectRightBrace`.
117117
mutating func parseCodeBlock(introducer: RawTokenSyntax? = nil, allowInitDecl: Bool = true) -> RawCodeBlockSyntax {
118118
let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace)
119-
let itemList = parseCodeBlockItemList(isAtTopLevel: false, allowInitDecl: allowInitDecl, stopCondition: { $0.at(.rightBrace) })
119+
let itemList = parseCodeBlockItemList(allowInitDecl: allowInitDecl, until: { $0.at(.rightBrace) })
120120
let (unexpectedBeforeRBrace, rbrace) = self.expectRightBrace(leftBrace: lbrace, introducer: introducer)
121121

122122
return .init(
@@ -148,7 +148,7 @@ extension Parser {
148148
/// statement → compiler-control-statement
149149
/// statements → statement statements?
150150
@_spi(RawSyntax)
151-
public mutating func parseCodeBlockItem(isAtTopLevel: Bool = false, allowInitDecl: Bool = true) -> RawCodeBlockItemSyntax? {
151+
public mutating func parseCodeBlockItem(isAtTopLevel: Bool, allowInitDecl: Bool) -> RawCodeBlockItemSyntax? {
152152
if let remainingTokens = remainingTokensIfMaximumNestingLevelReached() {
153153
return RawCodeBlockItemSyntax(
154154
remainingTokens,
@@ -229,7 +229,7 @@ extension Parser {
229229
// If config of attributes is parsed as part of declaration parsing as it
230230
// doesn't constitute its own code block item.
231231
let directive = self.parsePoundIfDirective { (parser, _) in
232-
parser.parseCodeBlockItem()
232+
parser.parseCodeBlockItem(isAtTopLevel: false, allowInitDecl: true)
233233
} addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, parser in
234234
if lastElement.semicolon == nil && !newItemAtStartOfLine {
235235
return RawCodeBlockItemSyntax(

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,50 @@ final class StatementExpressionTests: XCTestCase {
18111811
)
18121812
}
18131813

1814+
func testConsecutiveStatements1() {
1815+
assertParse(
1816+
"{a1️⃣ b2️⃣ c}",
1817+
diagnostics: [
1818+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive statements on a line must be separated by ';'"),
1819+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
1820+
]
1821+
)
1822+
}
1823+
1824+
func testConsecutiveStatements2() {
1825+
assertParse(
1826+
"switch x {case y: a1️⃣ b2️⃣ c}",
1827+
diagnostics: [
1828+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive statements on a line must be separated by ';'"),
1829+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
1830+
]
1831+
)
1832+
}
1833+
1834+
func testConsecutiveStatements3() {
1835+
assertParse(
1836+
"""
1837+
var i: Int { a1️⃣ b2️⃣ c }
1838+
""",
1839+
diagnostics: [
1840+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive statements on a line must be separated by ';'"),
1841+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
1842+
]
1843+
)
1844+
}
1845+
1846+
func testConsecutiveStatements4() {
1847+
assertParse(
1848+
"""
1849+
var i: Int { get {a1️⃣ b} set {c2️⃣ d} }
1850+
""",
1851+
diagnostics: [
1852+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive statements on a line must be separated by ';'"),
1853+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
1854+
]
1855+
)
1856+
}
1857+
18141858
func testStringLiteralAfterKeyPath() {
18151859
assertParse(
18161860
#"""

Tests/SwiftParserTest/translated/InvalidTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ final class InvalidTests: XCTestCase {
162162
1️⃣let y = "foo"
163163
switch y {
164164
case "bar":
165-
blah blah // ignored
165+
blah2️⃣ blah // ignored
166166
}
167167
case "baz":
168168
break
@@ -174,7 +174,8 @@ final class InvalidTests: XCTestCase {
174174
}
175175
"""#,
176176
diagnostics: [
177-
DiagnosticSpec(message: "all statements inside a switch must be covered by a 'case' or 'default' label")
177+
DiagnosticSpec(locationMarker: "1️⃣", message: "all statements inside a switch must be covered by a 'case' or 'default' label"),
178+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
178179
]
179180
)
180181
}

0 commit comments

Comments
 (0)