Skip to content

Commit 3cd655f

Browse files
authored
Merge pull request #452 from hamishknight/express-yourself
2 parents 47eaedd + 7427959 commit 3cd655f

File tree

8 files changed

+200
-30
lines changed

8 files changed

+200
-30
lines changed

Sources/SwiftFormat/Pipelines+Generated.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class LintPipeline: SyntaxVisitor {
161161
return .visitChildren
162162
}
163163

164-
override func visit(_ node: IfStmtSyntax) -> SyntaxVisitorContinueKind {
164+
override func visit(_ node: IfExprSyntax) -> SyntaxVisitorContinueKind {
165165
visitIfEnabled(NoParensAroundConditions.visit, for: node)
166166
return .visitChildren
167167
}
@@ -271,7 +271,7 @@ class LintPipeline: SyntaxVisitor {
271271
return .visitChildren
272272
}
273273

274-
override func visit(_ node: SwitchStmtSyntax) -> SyntaxVisitorContinueKind {
274+
override func visit(_ node: SwitchExprSyntax) -> SyntaxVisitorContinueKind {
275275
visitIfEnabled(NoParensAroundConditions.visit, for: node)
276276
return .visitChildren
277277
}

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
480480
return .visitChildren
481481
}
482482

483-
override func visit(_ node: IfStmtSyntax) -> SyntaxVisitorContinueKind {
483+
override func visit(_ node: IfExprSyntax) -> SyntaxVisitorContinueKind {
484484
// There may be a consistent breaking group around this node, see `CodeBlockItemSyntax`. This
485485
// group is necessary so that breaks around and inside of the conditions aren't forced to break
486486
// when the if-stmt spans multiple lines.
@@ -515,7 +515,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
515515
// any newlines between `else` and the open brace or a following `if`.
516516
if let tokenAfterElse = elseKeyword.nextToken(viewMode: .all), tokenAfterElse.leadingTrivia.hasLineComment {
517517
after(node.elseKeyword, tokens: .break(.same, size: 1))
518-
} else if let elseBody = node.elseBody, elseBody.is(IfStmtSyntax.self) {
518+
} else if let elseBody = node.elseBody, elseBody.is(IfExprSyntax.self) {
519519
after(node.elseKeyword, tokens: .space)
520520
}
521521
}
@@ -680,7 +680,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
680680
return .visitChildren
681681
}
682682

683-
override func visit(_ node: SwitchStmtSyntax) -> SyntaxVisitorContinueKind {
683+
override func visit(_ node: SwitchExprSyntax) -> SyntaxVisitorContinueKind {
684684
before(node.switchKeyword, tokens: .open)
685685
after(node.switchKeyword, tokens: .space)
686686
before(node.leftBrace, tokens: .break(.reset))
@@ -1480,7 +1480,8 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
14801480

14811481
// This group applies to a top-level if-stmt so that all of the bodies will have the same
14821482
// breaking behavior.
1483-
if let ifStmt = node.item.as(IfStmtSyntax.self) {
1483+
if let exprStmt = node.item.as(ExpressionStmtSyntax.self),
1484+
let ifStmt = exprStmt.expression.as(IfExprSyntax.self) {
14841485
before(ifStmt.conditions.firstToken, tokens: .open(.consistent))
14851486
after(ifStmt.lastToken, tokens: .close)
14861487
}

Sources/SwiftFormatRules/NoParensAroundConditions.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ public final class NoParensAroundConditions: SyntaxFormatRule {
5252
)
5353
}
5454

55-
public override func visit(_ node: IfStmtSyntax) -> StmtSyntax {
55+
public override func visit(_ node: IfExprSyntax) -> ExprSyntax {
5656
let conditions = visit(node.conditions)
5757
var result = node.with(\.ifKeyword, node.ifKeyword.withOneTrailingSpace())
5858
.with(\.conditions, conditions)
5959
.with(\.body, visit(node.body))
6060
if let elseBody = node.elseBody {
6161
result = result.with(\.elseBody, visit(elseBody))
6262
}
63-
return StmtSyntax(result)
63+
return ExprSyntax(result)
6464
}
6565

6666
public override func visit(_ node: ConditionElementSyntax) -> ConditionElementSyntax {
@@ -72,14 +72,14 @@ public final class NoParensAroundConditions: SyntaxFormatRule {
7272
return node.with(\.condition, .expression(extractExpr(tup)))
7373
}
7474

75-
/// FIXME(hbh): Parsing for SwitchStmtSyntax is not implemented.
76-
public override func visit(_ node: SwitchStmtSyntax) -> StmtSyntax {
75+
/// FIXME(hbh): Parsing for SwitchExprSyntax is not implemented.
76+
public override func visit(_ node: SwitchExprSyntax) -> ExprSyntax {
7777
guard let tup = node.expression.as(TupleExprSyntax.self),
7878
tup.elementList.firstAndOnly != nil
7979
else {
8080
return super.visit(node)
8181
}
82-
return StmtSyntax(
82+
return ExprSyntax(
8383
node.with(\.expression, extractExpr(tup)).with(\.cases, visit(node.cases)))
8484
}
8585

Sources/SwiftFormatRules/UseEarlyExits.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ public final class UseEarlyExits: SyntaxFormatRule {
5454

5555
let result = CodeBlockItemListSyntax(
5656
codeBlockItems.flatMap { (codeBlockItem: CodeBlockItemSyntax) -> [CodeBlockItemSyntax] in
57-
// The `elseBody` of an `IfStmtSyntax` will be a `CodeBlockSyntax` if it's an `else` block,
58-
// or another `IfStmtSyntax` if it's an `else if` block. We only want to handle the former.
59-
guard let ifStatement = codeBlockItem.item.as(IfStmtSyntax.self),
60-
let elseBody = ifStatement.elseBody?.as(CodeBlockSyntax.self),
61-
codeBlockEndsWithEarlyExit(elseBody)
57+
// The `elseBody` of an `IfExprSyntax` will be a `CodeBlockSyntax` if it's an `else` block,
58+
// or another `IfExprSyntax` if it's an `else if` block. We only want to handle the former.
59+
guard let exprStmt = codeBlockItem.item.as(ExpressionStmtSyntax.self),
60+
let ifStatement = exprStmt.expression.as(IfExprSyntax.self),
61+
let elseBody = ifStatement.elseBody?.as(CodeBlockSyntax.self),
62+
codeBlockEndsWithEarlyExit(elseBody)
6263
else {
6364
return [codeBlockItem]
6465
}

Sources/SwiftFormatRules/UseWhereClausesInForLoops.swift

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,26 @@ public final class UseWhereClausesInForLoops: SyntaxFormatRule {
5252
forInStmt: ForInStmtSyntax
5353
) -> ForInStmtSyntax {
5454
switch Syntax(firstStmt).as(SyntaxEnum.self) {
55-
case .ifStmt(let ifStmt)
56-
where ifStmt.conditions.count == 1
57-
&& ifStmt.elseKeyword == nil
58-
&& forInStmt.body.statements.count == 1:
59-
// Extract the condition of the IfStmt.
60-
let conditionElement = ifStmt.conditions.first!
61-
guard let condition = conditionElement.condition.as(ExprSyntax.self) else {
55+
case .expressionStmt(let exprStmt):
56+
switch Syntax(exprStmt.expression).as(SyntaxEnum.self) {
57+
case .ifExpr(let ifExpr)
58+
where ifExpr.conditions.count == 1
59+
&& ifExpr.elseKeyword == nil
60+
&& forInStmt.body.statements.count == 1:
61+
// Extract the condition of the IfExpr.
62+
let conditionElement = ifExpr.conditions.first!
63+
guard let condition = conditionElement.condition.as(ExprSyntax.self) else {
64+
return forInStmt
65+
}
66+
diagnose(.useWhereInsteadOfIf, on: ifExpr)
67+
return updateWithWhereCondition(
68+
node: forInStmt,
69+
condition: condition,
70+
statements: ifExpr.body.statements
71+
)
72+
default:
6273
return forInStmt
6374
}
64-
diagnose(.useWhereInsteadOfIf, on: ifStmt)
65-
return updateWithWhereCondition(
66-
node: forInStmt,
67-
condition: condition,
68-
statements: ifStmt.body.statements
69-
)
70-
7175
case .guardStmt(let guardStmt)
7276
where guardStmt.conditions.count == 1
7377
&& guardStmt.body.statements.count == 1

Tests/SwiftFormatPrettyPrintTests/IfStmtTests.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,74 @@ final class IfStmtTests: PrettyPrintTestCase {
153153
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: config)
154154
}
155155

156+
func testIfExpression1() {
157+
let input =
158+
"""
159+
func foo() -> Int {
160+
if var1 < var2 {
161+
23
162+
}
163+
else if d < e {
164+
24
165+
}
166+
else {
167+
0
168+
}
169+
}
170+
"""
171+
172+
let expected =
173+
"""
174+
func foo() -> Int {
175+
if var1 < var2 {
176+
23
177+
} else if d < e {
178+
24
179+
} else {
180+
0
181+
}
182+
}
183+
184+
"""
185+
186+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 23)
187+
}
188+
189+
func testIfExpression2() {
190+
let input =
191+
"""
192+
func foo() -> Int {
193+
let x = if var1 < var2 {
194+
23
195+
}
196+
else if d < e {
197+
24
198+
}
199+
else {
200+
0
201+
}
202+
return x
203+
}
204+
"""
205+
206+
let expected =
207+
"""
208+
func foo() -> Int {
209+
let x = if var1 < var2 {
210+
23
211+
} else if d < e {
212+
24
213+
} else {
214+
0
215+
}
216+
return x
217+
}
218+
219+
"""
220+
221+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 26)
222+
}
223+
156224
func testMatchingPatternConditions() {
157225
let input =
158226
"""

Tests/SwiftFormatPrettyPrintTests/SwitchStmtTests.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,77 @@ final class SwitchStmtTests: PrettyPrintTestCase {
205205
assertPrettyPrintEqual(input: input, expected: expected, linelength: 45)
206206
}
207207

208+
func testSwitchExpression1() {
209+
let input =
210+
"""
211+
func foo() -> Int {
212+
switch value1 + value2 + value3 + value4 {
213+
case "a":
214+
0
215+
case "b":
216+
1
217+
default:
218+
2
219+
}
220+
}
221+
"""
222+
223+
let expected =
224+
"""
225+
func foo() -> Int {
226+
switch value1 + value2 + value3
227+
+ value4
228+
{
229+
case "a":
230+
0
231+
case "b":
232+
1
233+
default:
234+
2
235+
}
236+
}
237+
238+
"""
239+
240+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 35)
241+
}
242+
243+
244+
func testSwitchExpression2() {
245+
let input =
246+
"""
247+
func foo() -> Int {
248+
let x = switch value1 + value2 + value3 + value4 {
249+
case "a":
250+
0
251+
case "b":
252+
1
253+
default:
254+
2
255+
}
256+
return x
257+
}
258+
"""
259+
260+
let expected =
261+
"""
262+
func foo() -> Int {
263+
let x = switch value1 + value2 + value3 + value4 {
264+
case "a":
265+
0
266+
case "b":
267+
1
268+
default:
269+
2
270+
}
271+
return x
272+
}
273+
274+
"""
275+
276+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 52)
277+
}
278+
208279
func testUnknownDefault() {
209280
let input =
210281
"""

Tests/SwiftFormatRulesTests/NoParensAroundConditionsTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,29 @@ final class NoParensAroundConditionsTests: LintOrFormatRuleTestCase {
135135
if foo.someCall({ if x {} }) {}
136136
""")
137137
}
138+
139+
func testParensAroundIfAndSwitchExprs() {
140+
XCTAssertFormatting(
141+
NoParensAroundConditions.self,
142+
input: """
143+
let x = if (x) {}
144+
let y = switch (4) { default: break }
145+
func foo() {
146+
return if (x) {}
147+
}
148+
func bar() {
149+
return switch (4) { default: break }
150+
}
151+
""",
152+
expected: """
153+
let x = if x {}
154+
let y = switch 4 { default: break }
155+
func foo() {
156+
return if x {}
157+
}
158+
func bar() {
159+
return switch 4 { default: break }
160+
}
161+
""")
162+
}
138163
}

0 commit comments

Comments
 (0)