Skip to content

Commit e5fce04

Browse files
committed
[Lint/Format] Extend return omission rule to include subscripts
1 parent ed1c8a6 commit e5fce04

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

Sources/SwiftFormat/Core/Pipelines+Generated.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ class LintPipeline: SyntaxVisitor {
281281
override func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind {
282282
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
283283
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
284+
visitIfEnabled(OmitReturns.visit, for: node)
284285
visitIfEnabled(UseTripleSlashForDocumentationComments.visit, for: node)
285286
return .visitChildren
286287
}

Sources/SwiftFormat/Rules/OmitReturns.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,69 @@ public final class OmitReturns: SyntaxFormatRule {
3737
return decl
3838
}
3939

40+
public override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax {
41+
let decl = super.visit(node)
42+
43+
guard var `subscript` = decl.as(SubscriptDeclSyntax.self) else {
44+
return decl
45+
}
46+
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+
}
98+
}
99+
100+
return decl
101+
}
102+
40103
public override func visit(_ node: ClosureExprSyntax) -> ExprSyntax {
41104
let expr = super.visit(node)
42105

Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,46 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
3030
}
3131
""")
3232
}
33+
34+
func testOmitReturnInSubscript() {
35+
XCTAssertFormatting(
36+
OmitReturns.self,
37+
input: """
38+
struct Test {
39+
subscript(x: Int) -> Bool {
40+
return false
41+
}
42+
}
43+
""",
44+
expected: """
45+
struct Test {
46+
subscript(x: Int) -> Bool {
47+
false
48+
}
49+
}
50+
""")
51+
52+
XCTAssertFormatting(
53+
OmitReturns.self,
54+
input: """
55+
struct Test {
56+
subscript(x: Int) -> Bool {
57+
get {
58+
return false
59+
}
60+
set { }
61+
}
62+
}
63+
""",
64+
expected: """
65+
struct Test {
66+
subscript(x: Int) -> Bool {
67+
get {
68+
false
69+
}
70+
set { }
71+
}
72+
}
73+
""")
74+
}
3375
}

0 commit comments

Comments
 (0)