Skip to content

Commit 41ce955

Browse files
committed
explicit_return: opt-in for closures
1 parent 5148dc6 commit 41ce955

5 files changed

Lines changed: 86 additions & 20 deletions

File tree

Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/ExplicitReturnConfiguration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct ExplicitReturnConfiguration: SeverityBasedRuleConfiguration {
1515
}
1616
}
1717

18-
static let defaultIncludedKinds = Set(ReturnKind.allCases)
18+
static let defaultIncludedKinds: Set<ReturnKind> = [.function, .getter, .subscript, .initializer]
1919

2020
@ConfigurationElement(key: "severity")
2121
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)

Source/SwiftLintBuiltInRules/Rules/Style/ExplicitReturnRule.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private extension ExplicitReturnRule {
3030

3131
override func visitPost(_ node: ClosureExprSyntax) {
3232
if configuration.isKindIncluded(.closure) {
33-
collectViolation(in: node.statements)
33+
collectViolation(in: node.statements, isInsideClosure: true)
3434
}
3535
}
3636

@@ -64,12 +64,15 @@ private extension ExplicitReturnRule {
6464
}
6565
}
6666

67-
private func collectViolation(in itemList: CodeBlockItemListSyntax) {
67+
private func collectViolation(in itemList: CodeBlockItemListSyntax, isInsideClosure: Bool = false) {
6868
guard let onlyItem = itemList.onlyElement?.item,
6969
!onlyItem.is(ReturnStmtSyntax.self),
7070
Syntax(onlyItem).isProtocol((any ExprSyntaxProtocol).self) else {
7171
return
7272
}
73+
if isInsideClosure, Syntax(onlyItem).isFunctionCallExpr {
74+
return
75+
}
7376
let position = onlyItem.positionAfterSkippingLeadingTrivia
7477
violations.append(
7578
at: position,
@@ -83,6 +86,21 @@ private extension ExplicitReturnRule {
8386
}
8487
}
8588

89+
private extension Syntax {
90+
var isFunctionCallExpr: Bool {
91+
if `is`(FunctionCallExprSyntax.self) {
92+
return true
93+
}
94+
if let tryExpr = `as`(TryExprSyntax.self) {
95+
return Syntax(tryExpr.expression).isFunctionCallExpr
96+
}
97+
if let awaitExpr = `as`(AwaitExprSyntax.self) {
98+
return Syntax(awaitExpr.expression).isFunctionCallExpr
99+
}
100+
return false
101+
}
102+
}
103+
86104
private extension FunctionSignatureSyntax {
87105
var allowsImplicitReturns: Bool {
88106
guard let returnClause else { return false }

Source/SwiftLintBuiltInRules/Rules/Style/ExplicitReturnRuleExamples.swift

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,91 @@
11
// swiftlint:disable file_length
22
// swiftlint:disable:next type_body_length
33
internal struct ExplicitReturnRuleExamples {
4+
private static let closureConfig: [String: any Sendable] = [
5+
"included": ["closure", "function", "getter", "subscript", "initializer"],
6+
]
7+
48
internal struct ClosureExamples {
59
static let nonTriggeringExamples = [
610
Example("""
711
foo.map {
812
return $0 + 1
913
}
10-
"""),
14+
""", configuration: closureConfig),
1115
Example("""
1216
foo.map({
1317
return $0 + 1
1418
})
15-
"""),
19+
""", configuration: closureConfig),
1620
Example("""
1721
foo.map { value in
1822
return value + 1
1923
}
20-
"""),
24+
""", configuration: closureConfig),
2125
Example("""
2226
[1, 2].first(where: {
2327
return true
2428
})
25-
"""),
29+
""", configuration: closureConfig),
2630
Example("""
2731
[1, 2].first(where: {
2832
bar($0)
2933
return true
3034
})
31-
"""),
35+
""", configuration: closureConfig),
36+
Example("""
37+
runOn(.main, {
38+
controller?.present(alert, animated: true, completion: nil)
39+
})
40+
""", configuration: closureConfig),
41+
Example("""
42+
foo.bar(failure: { error in
43+
cont.resume(throwing: error)
44+
})
45+
""", configuration: closureConfig),
46+
Example("""
47+
foo.map {
48+
someFunc($0)
49+
}
50+
""", configuration: closureConfig),
51+
Example("""
52+
foo.map {
53+
try someFunc($0)
54+
}
55+
""", configuration: closureConfig),
56+
Example("""
57+
foo.map {
58+
await someFunc($0)
59+
}
60+
""", configuration: closureConfig),
3261
]
3362

3463
static let triggeringExamples = [
3564
Example("""
3665
foo.map { value in
3766
↓value + 1
3867
}
39-
"""),
68+
""", configuration: closureConfig),
4069
Example("""
4170
foo.map {
4271
↓$0 + 1
4372
}
44-
"""),
45-
Example("foo.map({ ↓$0 + 1 })"),
73+
""", configuration: closureConfig),
74+
Example("foo.map({ ↓$0 + 1 })", configuration: closureConfig),
4675
Example("""
4776
[1, 2].first(where: {
4877
↓true
4978
})
50-
"""),
79+
""", configuration: closureConfig),
5180
]
5281

53-
static let corrections = [
82+
static let corrections: [Example: Example] = [
5483
Example("""
5584
foo.map { value in
5685
// Important comment
5786
value + 1
5887
}
59-
"""): Example("""
88+
""", configuration: closureConfig): Example("""
6089
foo.map { value in
6190
// Important comment
6291
return value + 1
@@ -66,17 +95,18 @@ internal struct ExplicitReturnRuleExamples {
6695
foo.map {
6796
$0 + 1
6897
}
69-
"""): Example("""
98+
""", configuration: closureConfig): Example("""
7099
foo.map {
71100
return $0 + 1
72101
}
73102
"""),
74-
Example("foo.map({ $0 + 1 })"): Example("foo.map({ return $0 + 1 })"),
103+
Example("foo.map({ $0 + 1 })", configuration: closureConfig):
104+
Example("foo.map({ return $0 + 1 })"),
75105
Example("""
76106
[1, 2].first(where: {
77107
true
78108
})
79-
"""): Example("""
109+
""", configuration: closureConfig): Example("""
80110
[1, 2].first(where: {
81111
return true
82112
})
@@ -422,20 +452,33 @@ internal struct ExplicitReturnRuleExamples {
422452
}
423453

424454
struct MixedExamples {
425-
static let corrections = [
455+
static let corrections: [Example: Example] = [
426456
Example("""
427457
func foo() -> Int {
428458
↓[1, 2].first(where: {
429459
↓true
430460
})!
431461
}
432-
"""): Example("""
462+
""", configuration: closureConfig): Example("""
433463
func foo() -> Int {
434464
return [1, 2].first(where: {
435465
return true
436466
})!
437467
}
438468
"""),
469+
Example("""
470+
func foo() -> Int {
471+
↓[1, 2].first(where: {
472+
true
473+
})!
474+
}
475+
"""): Example("""
476+
func foo() -> Int {
477+
return [1, 2].first(where: {
478+
true
479+
})!
480+
}
481+
"""),
439482
]
440483
}
441484

Tests/FileSystemAccessTests/ExplicitReturnRuleTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ final class ExplicitReturnRuleTests: SwiftLintTestCase {
88
nonTriggeringExamples.removeAll(
99
where: ExplicitReturnRuleExamples.ClosureExamples.triggeringExamples.contains
1010
)
11+
nonTriggeringExamples.removeAll { $0.configuration != nil }
1112

1213
verifySubset(
1314
nonTriggeringExamples: nonTriggeringExamples,
@@ -23,6 +24,7 @@ final class ExplicitReturnRuleTests: SwiftLintTestCase {
2324
nonTriggeringExamples.removeAll(
2425
where: ExplicitReturnRuleExamples.FunctionExamples.triggeringExamples.contains
2526
)
27+
nonTriggeringExamples.removeAll { $0.configuration != nil }
2628

2729
verifySubset(
2830
nonTriggeringExamples: nonTriggeringExamples,
@@ -38,6 +40,7 @@ final class ExplicitReturnRuleTests: SwiftLintTestCase {
3840
nonTriggeringExamples.removeAll(
3941
where: ExplicitReturnRuleExamples.GetterExamples.triggeringExamples.contains
4042
)
43+
nonTriggeringExamples.removeAll { $0.configuration != nil }
4144

4245
verifySubset(
4346
nonTriggeringExamples: nonTriggeringExamples,
@@ -53,6 +56,7 @@ final class ExplicitReturnRuleTests: SwiftLintTestCase {
5356
nonTriggeringExamples.removeAll(
5457
where: ExplicitReturnRuleExamples.InitializerExamples.triggeringExamples.contains
5558
)
59+
nonTriggeringExamples.removeAll { $0.configuration != nil }
5660

5761
verifySubset(
5862
nonTriggeringExamples: nonTriggeringExamples,
@@ -68,6 +72,7 @@ final class ExplicitReturnRuleTests: SwiftLintTestCase {
6872
nonTriggeringExamples.removeAll(
6973
where: ExplicitReturnRuleExamples.SubscriptExamples.triggeringExamples.contains
7074
)
75+
nonTriggeringExamples.removeAll { $0.configuration != nil }
7176

7277
verifySubset(
7378
nonTriggeringExamples: nonTriggeringExamples,

Tests/IntegrationTests/Resources/default_rule_configurations.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ explicit_init:
338338
correctable: true
339339
explicit_return:
340340
severity: warning
341-
included: [closure, function, getter, initializer, subscript]
341+
included: [function, getter, initializer, subscript]
342342
meta:
343343
opt-in: true
344344
correctable: true

0 commit comments

Comments
 (0)