Skip to content

Commit 67f8d52

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 43dc7a5 commit 67f8d52

File tree

7 files changed

+106
-84
lines changed

7 files changed

+106
-84
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,16 +2099,20 @@ extension Parser.Lookahead {
20992099
}
21002100
}
21012101

2102+
// MARK: Conditional Expressions
2103+
21022104
extension Parser {
2103-
/// Parse an if statement.
2105+
/// Parse an if statement/expression.
21042106
///
21052107
/// Grammar
21062108
/// =======
21072109
///
2108-
/// if-statement → 'if' condition-list code-block else-clause?
2110+
/// if-expression → 'if' condition-list code-block else-clause?
21092111
/// else-clause → 'else' code-block | else if-statement
21102112
@_spi(RawSyntax)
2111-
public mutating func parseIfStatement(ifHandle: RecoveryConsumptionHandle) -> RawIfStmtSyntax {
2113+
public mutating func parseIfExpression(
2114+
ifHandle: RecoveryConsumptionHandle
2115+
) -> RawIfExprSyntax {
21122116
let (unexpectedBeforeIfKeyword, ifKeyword) = self.eat(ifHandle)
21132117
// A scope encloses the condition and true branch for any variables bound
21142118
// by a conditional binding. The else branch does *not* see these variables.
@@ -2117,18 +2121,20 @@ extension Parser {
21172121

21182122
// The else branch, if any, is outside of the scope of the condition.
21192123
let elseKeyword = self.consume(if: .keyword(.else))
2120-
let elseBody: RawIfStmtSyntax.ElseBody?
2124+
let elseBody: RawIfExprSyntax.ElseBody?
21212125
if elseKeyword != nil {
21222126
if self.at(.keyword(.if)) {
2123-
elseBody = .ifStmt(self.parseIfStatement(ifHandle: .constant(.keyword(.if))))
2127+
elseBody = .ifExpr(
2128+
self.parseIfExpression(ifHandle: .constant(.keyword(.if)))
2129+
)
21242130
} else {
21252131
elseBody = .codeBlock(self.parseCodeBlock(introducer: ifKeyword))
21262132
}
21272133
} else {
21282134
elseBody = nil
21292135
}
21302136

2131-
return RawIfStmtSyntax(
2137+
return RawIfExprSyntax(
21322138
unexpectedBeforeIfKeyword,
21332139
ifKeyword: ifKeyword,
21342140
conditions: conditions,

Sources/SwiftParser/Statements.swift

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

104104
case (.ifKeyword, let handle)?:
105-
return label(self.parseIfStatement(ifHandle: handle), with: optLabel)
105+
let ifExpr = self.parseIfExpression(ifHandle: handle)
106+
let ifStmt = RawExpressionStmtSyntax(
107+
expression: RawExprSyntax(ifExpr),
108+
arena: self.arena
109+
)
110+
return label(ifStmt, with: optLabel)
106111
case (.guardKeyword, let handle)?:
107112
return label(self.parseGuardStatement(guardHandle: handle), with: optLabel)
108113
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.with(\.conditions, ConditionElementListSyntax(newConditions)))
60+
return node.with(\.conditions, ConditionElementListSyntax(newConditions))
6161
}
6262
}

Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,30 +116,30 @@ extension ExtensionDeclSyntax: HasTrailingMemberDeclBlock {}
116116
extension ProtocolDeclSyntax: HasTrailingMemberDeclBlock {}
117117
extension StructDeclSyntax: HasTrailingMemberDeclBlock {}
118118

119-
// MARK: - IfStmtSyntax
120-
// IfStmtSyntax is a special scenario as we also have the `else` body or an if-else
119+
// MARK: - IfExprSyntax
120+
// IfExprSyntax is a special scenario as we also have the `else` body or an if-else
121121
// So we cannot conform to `HasTrailingCodeBlock`
122122

123-
public extension IfStmtSyntax {
123+
public extension IfExprSyntax {
124124
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () throws -> CodeBlockItemListSyntax, @CodeBlockItemListBuilder `else` elseBuilder: () throws -> CodeBlockItemListSyntax? = { nil }) throws {
125-
let stmt = StmtSyntax("\(header) {}")
126-
guard let ifStmt = stmt.as(IfStmtSyntax.self) else {
127-
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualNode: stmt)
125+
let expr = ExprSyntax("\(header) {}")
126+
guard let ifExpr = expr.as(Self.self) else {
127+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualNode: expr)
128128
}
129-
self = ifStmt
129+
self = ifExpr
130130
self.body = try CodeBlockSyntax(statements: bodyBuilder())
131131
self.elseBody = try elseBuilder().map { .codeBlock(CodeBlockSyntax(statements: $0)) }
132132
self.elseKeyword = elseBody != nil ? .keyword(.else) : nil
133133
}
134134

135-
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, elseIf: IfStmtSyntax) throws {
136-
let stmt = StmtSyntax("\(header) {}")
137-
guard let ifStmt = stmt.as(IfStmtSyntax.self) else {
138-
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualNode: stmt)
135+
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, elseIf: IfExprSyntax) throws {
136+
let expr = ExprSyntax("\(header) {}")
137+
guard let ifExpr = expr.as(Self.self) else {
138+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualNode: expr)
139139
}
140-
self = ifStmt
140+
self = ifExpr
141141
self.body = CodeBlockSyntax(statements: bodyBuilder())
142-
self.elseBody = .ifStmt(elseIf)
142+
self.elseBody = .ifExpr(elseIf)
143143
self.elseKeyword = elseBody != nil ? .keyword(.else) : nil
144144
}
145145
}

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: .keyword(.if),
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: .keyword(.if),
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
}

0 commit comments

Comments
 (0)