Skip to content

Commit 03f302a

Browse files
committed
Parse IfExprSyntax
Update the parser to support parsing IfExprSyntax. For now, this only updates the existing statement parsing, and does not attempt to parse in expression position.
1 parent c061f83 commit 03f302a

File tree

8 files changed

+85
-63
lines changed

8 files changed

+85
-63
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,16 +2524,20 @@ extension Parser.Lookahead {
25242524
}
25252525
}
25262526

2527+
// MARK: Conditional Expressions
2528+
25272529
extension Parser {
2528-
/// Parse an if statement.
2530+
/// Parse an if statement/expression.
25292531
///
25302532
/// Grammar
25312533
/// =======
25322534
///
2533-
/// if-statement → 'if' condition-list code-block else-clause?
2535+
/// if-expression → 'if' condition-list code-block else-clause?
25342536
/// else-clause → 'else' code-block | else if-statement
25352537
@_spi(RawSyntax)
2536-
public mutating func parseIfStatement(ifHandle: RecoveryConsumptionHandle) -> RawIfStmtSyntax {
2538+
public mutating func parseIfExpression(
2539+
ifHandle: RecoveryConsumptionHandle
2540+
) -> RawIfExprSyntax {
25372541
let (unexpectedBeforeIfKeyword, ifKeyword) = self.eat(ifHandle)
25382542
// A scope encloses the condition and true branch for any variables bound
25392543
// by a conditional binding. The else branch does *not* see these variables.
@@ -2542,18 +2546,20 @@ extension Parser {
25422546

25432547
// The else branch, if any, is outside of the scope of the condition.
25442548
let elseKeyword = self.consume(if: .elseKeyword)
2545-
let elseBody: RawIfStmtSyntax.ElseBody?
2549+
let elseBody: RawIfExprSyntax.ElseBody?
25462550
if elseKeyword != nil {
25472551
if self.at(.ifKeyword) {
2548-
elseBody = .ifStmt(self.parseIfStatement(ifHandle: .constant(.ifKeyword)))
2552+
elseBody = .ifExpr(
2553+
self.parseIfExpression(ifHandle: .constant(.ifKeyword))
2554+
)
25492555
} else {
25502556
elseBody = .codeBlock(self.parseCodeBlock(introducer: ifKeyword))
25512557
}
25522558
} else {
25532559
elseBody = nil
25542560
}
25552561

2556-
return RawIfStmtSyntax(
2562+
return RawIfExprSyntax(
25572563
unexpectedBeforeIfKeyword,
25582564
ifKeyword: ifKeyword,
25592565
conditions: conditions,

Sources/SwiftParser/Statements.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,12 @@ extension Parser {
9797
return label(self.parseRepeatWhileStatement(repeatHandle: handle), with: optLabel)
9898

9999
case (.ifKeyword, let handle)?:
100-
return label(self.parseIfStatement(ifHandle: handle), with: optLabel)
100+
let ifExpr = self.parseIfExpression(ifHandle: handle)
101+
let ifStmt = RawExpressionStmtSyntax(
102+
expression: RawExprSyntax(ifExpr),
103+
arena: self.arena
104+
)
105+
return label(ifStmt, with: optLabel)
101106
case (.guardKeyword, let handle)?:
102107
return label(self.parseGuardStatement(guardHandle: handle), with: optLabel)
103108
case (.switchKeyword, let handle)?:

Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import SwiftSyntax
1414
import SwiftParser
1515

16-
/// ``MigrateToNewIfLetSyntax`` will visit each if statement in the Syntax tree, and
16+
/// ``MigrateToNewIfLetSyntax`` will visit each if expression in the Syntax tree, and
1717
/// checks if the there is an if condition which is of the pre Swift 5.7 "if-let-style"
1818
/// and rewrites it to the new one.
1919
///
@@ -34,7 +34,7 @@ import SwiftParser
3434
/// // ...
3535
/// }
3636
public struct MigrateToNewIfLetSyntax: RefactoringProvider {
37-
public static func refactor(syntax node: IfStmtSyntax, in context: ()) -> StmtSyntax? {
37+
public static func refactor(syntax node: IfExprSyntax, in context: ()) -> IfExprSyntax? {
3838
// Visit all conditions in the node.
3939
let newConditions = node.conditions.enumerated().map { (index, condition) -> ConditionElementListSyntax.Element in
4040
var conditionCopy = condition
@@ -57,6 +57,6 @@ public struct MigrateToNewIfLetSyntax: RefactoringProvider {
5757
}
5858
return conditionCopy
5959
}
60-
return StmtSyntax(node.withConditions(ConditionElementListSyntax(newConditions)))
60+
return node.withConditions(ConditionElementListSyntax(newConditions))
6161
}
6262
}

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ extension FunctionParameter {
188188

189189
// MARK: - IfStmt
190190

191-
extension IfStmt {
191+
extension IfExprSyntax {
192192
/// A convenience initializer that uses builder closures to express an
193193
/// if body, potentially with a second trailing builder closure for an else
194194
/// body.

Tests/SwiftParserTest/StatementTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ final class StatementTests: XCTestCase {
2121
if let baz {}
2222
""",
2323
substructure: Syntax(
24-
IfStmtSyntax(
24+
IfExprSyntax(
2525
ifKeyword: .ifKeyword(),
2626
conditions: ConditionElementListSyntax([
2727
ConditionElementSyntax(
@@ -47,7 +47,7 @@ final class StatementTests: XCTestCase {
4747
if let self = self {}
4848
""",
4949
substructure: Syntax(
50-
IfStmtSyntax(
50+
IfExprSyntax(
5151
ifKeyword: .ifKeyword(),
5252
conditions: ConditionElementListSyntax([
5353
ConditionElementSyntax(

Tests/SwiftRefactorTest/MigrateToNewIfLetSyntax.swift

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,102 +17,113 @@ import SwiftSyntaxBuilder
1717
import XCTest
1818
import _SwiftSyntaxTestSupport
1919

20+
@discardableResult
21+
func testRefactorIfLet(
22+
_ syntax: ExprSyntax,
23+
expected: ExprSyntax,
24+
file: StaticString = #file,
25+
line: UInt = #line
26+
) throws -> ExprSyntax {
27+
let ifExpr = try XCTUnwrap(
28+
syntax.as(IfExprSyntax.self),
29+
file: file,
30+
line: line
31+
)
32+
33+
let refactored = try XCTUnwrap(
34+
MigrateToNewIfLetSyntax.refactor(syntax: ifExpr),
35+
file: file,
36+
line: line
37+
)
38+
39+
AssertStringsEqualWithDiff(expected.description, refactored.description)
40+
return ExprSyntax(refactored)
41+
}
42+
2043
final class MigrateToNewIfLetSyntaxTest: XCTestCase {
2144
func testRefactoring() throws {
22-
let baselineSyntax: StmtSyntax = """
45+
let baselineSyntax: ExprSyntax = """
2346
if let x = x {}
2447
"""
2548

26-
let expectedSyntax: StmtSyntax = """
49+
let expectedSyntax: ExprSyntax = """
2750
if let x {}
2851
"""
2952

30-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
31-
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
32-
33-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
34-
35-
AssertStringsEqualWithDiff(expected.description, refactored.description)
53+
try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
3654
}
3755

3856
func testIdempotence() throws {
39-
let baselineSyntax: StmtSyntax = """
57+
let baselineSyntax: ExprSyntax = """
4058
if let x = x {}
4159
"""
4260

43-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
44-
45-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
46-
let refactoredAgain = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
61+
let expectedSyntax: ExprSyntax = """
62+
if let x {}
63+
"""
4764

48-
AssertStringsEqualWithDiff(refactored.description, refactoredAgain.description)
65+
let refactored = try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
66+
try testRefactorIfLet(refactored, expected: expectedSyntax)
4967
}
5068

5169
func testMultiBinding() throws {
52-
let baselineSyntax: StmtSyntax = """
70+
let baselineSyntax: ExprSyntax = """
5371
if let x = x, var y = y, let z = z {}
5472
"""
5573

56-
let expectedSyntax: StmtSyntax = """
74+
let expectedSyntax: ExprSyntax = """
5775
if let x, var y, let z {}
5876
"""
5977

60-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
61-
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
62-
63-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
64-
65-
AssertStringsEqualWithDiff(expected.description, refactored.description)
78+
try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
6679
}
6780

6881
func testMixedBinding() throws {
69-
let baselineSyntax: StmtSyntax = """
82+
let baselineSyntax: ExprSyntax = """
7083
if let x = x, var y = x, let z = y.w {}
7184
"""
7285

73-
let expectedSyntax: StmtSyntax = """
86+
let expectedSyntax: ExprSyntax = """
7487
if let x, var y = x, let z = y.w {}
7588
"""
7689

77-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
78-
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
79-
80-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
81-
82-
AssertStringsEqualWithDiff(expected.description, refactored.description)
90+
try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
8391
}
8492

8593
func testConditions() throws {
86-
let baselineSyntax: StmtSyntax = """
94+
let baselineSyntax: ExprSyntax = """
8795
if let x = x + 1, x == x, !x {}
8896
"""
8997

90-
let expectedSyntax: StmtSyntax = """
98+
let expectedSyntax: ExprSyntax = """
9199
if let x = x + 1, x == x, !x {}
92100
"""
93101

94-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
95-
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
96-
97-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
98-
99-
AssertStringsEqualWithDiff(expected.description, refactored.description)
102+
try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
100103
}
101104

102105
func testWhitespaceNormalization() throws {
103-
let baselineSyntax: StmtSyntax = """
106+
let baselineSyntax: ExprSyntax = """
104107
if let x = x , let y = y {}
105108
"""
106109

107-
let expectedSyntax: StmtSyntax = """
110+
let expectedSyntax: ExprSyntax = """
108111
if let x, let y {}
109112
"""
110113

111-
let baseline = try XCTUnwrap(baselineSyntax.as(IfStmtSyntax.self))
112-
let expected = try XCTUnwrap(expectedSyntax.as(IfStmtSyntax.self))
114+
try testRefactorIfLet(baselineSyntax, expected: expectedSyntax)
115+
}
113116

114-
let refactored = try XCTUnwrap(MigrateToNewIfLetSyntax.refactor(syntax: baseline))
117+
func testIfStmt() throws {
118+
let baselineSyntax: StmtSyntax = """
119+
if let x = x {}
120+
"""
121+
122+
let expectedSyntax: ExprSyntax = """
123+
if let x {}
124+
"""
115125

116-
AssertStringsEqualWithDiff(expected.description, refactored.description)
126+
let exprStmt = try XCTUnwrap(baselineSyntax.as(ExpressionStmtSyntax.self))
127+
try testRefactorIfLet(exprStmt.expression, expected: expectedSyntax)
117128
}
118129
}

Tests/SwiftSyntaxBuilderTest/FunctionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import _SwiftSyntaxTestSupport
1919
final class FunctionTests: XCTestCase {
2020
func testFibonacci() {
2121
let buildable = FunctionDecl("func fibonacci(_ n: Int) -> Int") {
22-
IfStmt("if n <= 1 { return n }")
22+
IfExpr("if n <= 1 { return n }")
2323

2424
ReturnStmt("return fibonacci(n - 1) + self.fibonacci(n - 2)")
2525
}

Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import SwiftSyntax
1515
import SwiftSyntaxBuilder
1616

1717
final class IfStmtTests: XCTestCase {
18-
func testEmptyIfStmt() {
18+
func testEmptyIfExpr() {
1919
// Use the convenience initializer from IfStmtConvenienceInitializers. This is
2020
// disambiguated by the absence of a labelName parameter and the use of a
2121
// trailing closure.
22-
let buildable = IfStmt(conditions: ConditionElementList { BooleanLiteralExpr(false) }) {}
22+
let buildable = IfExpr(conditions: ConditionElementList { BooleanLiteralExpr(false) }) {}
2323
AssertBuildResult(
2424
buildable,
2525
"""
@@ -32,7 +32,7 @@ final class IfStmtTests: XCTestCase {
3232
func testIfElseStmt() {
3333
// Use the convenience initializer from IfStmtConvenienceInitializers
3434
// with an else branch expressed by a second trailing closure.
35-
let buildable = IfStmt(conditions: ConditionElementList { BooleanLiteralExpr(true) }) {
35+
let buildable = IfExpr(conditions: ConditionElementList { BooleanLiteralExpr(true) }) {
3636
FunctionCallExpr(callee: ExprSyntax("print")) {
3737
TupleExprElement(expression: StringLiteralExpr(content: "Hello from the if-branch!"))
3838
}
@@ -54,7 +54,7 @@ final class IfStmtTests: XCTestCase {
5454
}
5555

5656
func testIfLetStmt() {
57-
let buildable = IfStmt(
57+
let buildable = IfExpr(
5858
conditions: ConditionElementList {
5959
OptionalBindingCondition(
6060
letOrVarKeyword: .let,
@@ -73,7 +73,7 @@ final class IfStmtTests: XCTestCase {
7373
}
7474

7575
func testIfCaseStmt() {
76-
let buildable = IfStmt(
76+
let buildable = IfExpr(
7777
conditions: ConditionElementList {
7878
MatchingPatternCondition(
7979
pattern: ExpressionPattern(expression: MemberAccessExpr(name: "x")),

0 commit comments

Comments
 (0)