Skip to content

Commit a13a52f

Browse files
committed
[Lint/Format] Extend return omission rule to include computed variables
1 parent e5fce04 commit a13a52f

File tree

3 files changed

+106
-51
lines changed

3 files changed

+106
-51
lines changed

Sources/SwiftFormat/Core/Pipelines+Generated.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class LintPipeline: SyntaxVisitor {
232232
}
233233

234234
override func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind {
235+
visitIfEnabled(OmitReturns.visit, for: node)
235236
visitIfEnabled(UseSingleLinePropertyGetter.visit, for: node)
236237
return .visitChildren
237238
}

Sources/SwiftFormat/Rules/OmitReturns.swift

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -44,62 +44,29 @@ public final class OmitReturns: SyntaxFormatRule {
4444
return decl
4545
}
4646

47-
if let accessorBlock = `subscript`.accessorBlock {
48-
// We are assuming valid Swift code here where only
49-
// one `get { ... }` is allowed.
50-
switch accessorBlock.accessors {
51-
case .accessors(let accessors):
52-
guard var getter = accessors.filter({
53-
$0.accessorSpecifier.tokenKind == .keyword(.get)
54-
}).first else {
55-
return decl
56-
}
57-
58-
guard let body = getter.body,
59-
let `return` = containsSingleReturn(body.statements) else {
60-
return decl
61-
}
62-
63-
guard let getterAt = accessors.firstIndex(where: {
64-
$0.accessorSpecifier.tokenKind == .keyword(.get)
65-
}) else {
66-
return decl
67-
}
68-
69-
getter.body?.statements = unwrapReturnStmt(`return`)
70-
71-
`subscript`.accessorBlock = .init(
72-
leadingTrivia: accessorBlock.leadingTrivia,
73-
leftBrace: accessorBlock.leftBrace,
74-
accessors: .accessors(accessors.with(\.[getterAt], getter)),
75-
rightBrace: accessorBlock.rightBrace,
76-
trailingTrivia: accessorBlock.trailingTrivia)
77-
78-
diagnose(.omitReturnStatement, on: `return`, severity: .refactoring)
79-
80-
return DeclSyntax(`subscript`)
81-
82-
case .getter(let getter):
83-
guard let `return` = containsSingleReturn(getter) else {
84-
return decl
85-
}
86-
87-
`subscript`.accessorBlock = .init(
88-
leadingTrivia: accessorBlock.leadingTrivia,
89-
leftBrace: accessorBlock.leftBrace,
90-
accessors: .getter(unwrapReturnStmt(`return`)),
91-
rightBrace: accessorBlock.rightBrace,
92-
trailingTrivia: accessorBlock.trailingTrivia)
93-
94-
diagnose(.omitReturnStatement, on: `return`, severity: .refactoring)
95-
96-
return DeclSyntax(`subscript`)
97-
}
47+
if let accessorBlock = `subscript`.accessorBlock,
48+
// We are assuming valid Swift code here where only
49+
// one `get { ... }` is allowed.
50+
let transformed = transformAccessorBlock(accessorBlock) {
51+
`subscript`.accessorBlock = transformed
52+
return DeclSyntax(`subscript`)
9853
}
9954

10055
return decl
10156
}
10257

58+
public override func visit(_ node: PatternBindingSyntax) -> PatternBindingSyntax {
59+
var binding = node
60+
61+
if let accessorBlock = binding.accessorBlock,
62+
let transformed = transformAccessorBlock(accessorBlock) {
63+
binding.accessorBlock = transformed
64+
return binding
65+
}
66+
67+
return node
68+
}
69+
10370
public override func visit(_ node: ClosureExprSyntax) -> ExprSyntax {
10471
let expr = super.visit(node)
10572

@@ -114,6 +81,55 @@ public final class OmitReturns: SyntaxFormatRule {
11481
return expr
11582
}
11683

84+
private func transformAccessorBlock(_ accessorBlock: AccessorBlockSyntax) -> AccessorBlockSyntax? {
85+
// We are assuming valid Swift code here where only
86+
// one `get { ... }` is allowed.
87+
switch accessorBlock.accessors {
88+
case .accessors(let accessors):
89+
guard var getter = accessors.filter({
90+
$0.accessorSpecifier.tokenKind == .keyword(.get)
91+
}).first else {
92+
return nil
93+
}
94+
95+
guard let body = getter.body,
96+
let `return` = containsSingleReturn(body.statements) else {
97+
return nil
98+
}
99+
100+
guard let getterAt = accessors.firstIndex(where: {
101+
$0.accessorSpecifier.tokenKind == .keyword(.get)
102+
}) else {
103+
return nil
104+
}
105+
106+
getter.body?.statements = unwrapReturnStmt(`return`)
107+
108+
diagnose(.omitReturnStatement, on: `return`, severity: .refactoring)
109+
110+
return .init(
111+
leadingTrivia: accessorBlock.leadingTrivia,
112+
leftBrace: accessorBlock.leftBrace,
113+
accessors: .accessors(accessors.with(\.[getterAt], getter)),
114+
rightBrace: accessorBlock.rightBrace,
115+
trailingTrivia: accessorBlock.trailingTrivia)
116+
117+
case .getter(let getter):
118+
guard let `return` = containsSingleReturn(getter) else {
119+
return nil
120+
}
121+
122+
diagnose(.omitReturnStatement, on: `return`, severity: .refactoring)
123+
124+
return .init(
125+
leadingTrivia: accessorBlock.leadingTrivia,
126+
leftBrace: accessorBlock.leftBrace,
127+
accessors: .getter(unwrapReturnStmt(`return`)),
128+
rightBrace: accessorBlock.rightBrace,
129+
trailingTrivia: accessorBlock.trailingTrivia)
130+
}
131+
}
132+
117133
private func containsSingleReturn(_ body: CodeBlockItemListSyntax) -> ReturnStmtSyntax? {
118134
if let element = body.firstAndOnly?.as(CodeBlockItemSyntax.self),
119135
let ret = element.item.as(ReturnStmtSyntax.self),

Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,42 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
7272
}
7373
""")
7474
}
75+
76+
func testOmitReturnInComputedVars() {
77+
XCTAssertFormatting(
78+
OmitReturns.self,
79+
input: """
80+
var x: Int {
81+
return 42
82+
}
83+
""",
84+
expected: """
85+
var x: Int {
86+
42
87+
}
88+
""")
89+
90+
XCTAssertFormatting(
91+
OmitReturns.self,
92+
input: """
93+
struct Test {
94+
var x: Int {
95+
get {
96+
return 42
97+
}
98+
set { }
99+
}
100+
}
101+
""",
102+
expected: """
103+
struct Test {
104+
var x: Int {
105+
get {
106+
42
107+
}
108+
set { }
109+
}
110+
}
111+
""")
112+
}
75113
}

0 commit comments

Comments
 (0)