mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Fix redundantClosure when redundantReturn disabled
This commit is contained in:
@@ -1110,7 +1110,9 @@ extension Formatter {
|
||||
/// has a single statement. This makes it eligible to be used with implicit return.
|
||||
func blockBodyHasSingleStatement(
|
||||
atStartOfScope startOfScopeIndex: Int,
|
||||
includingConditionalStatements: Bool = true
|
||||
includingConditionalStatements: Bool,
|
||||
includingReturnStatements: Bool,
|
||||
includingReturnInConditionalStatements: Bool? = nil
|
||||
) -> Bool {
|
||||
guard let endOfScopeIndex = endOfScope(at: startOfScopeIndex) else { return false }
|
||||
let startOfBody = self.startOfBody(atStartOfScope: startOfScopeIndex)
|
||||
@@ -1123,7 +1125,7 @@ extension Formatter {
|
||||
}
|
||||
|
||||
// Skip over any optional `return` keyword
|
||||
if tokens[firstTokenInBody] == .keyword("return") {
|
||||
if includingReturnStatements, tokens[firstTokenInBody] == .keyword("return") {
|
||||
guard let tokenAfterReturnKeyword = index(of: .nonSpaceOrCommentOrLinebreak, after: firstTokenInBody) else { return false }
|
||||
firstTokenInBody = tokenAfterReturnKeyword
|
||||
}
|
||||
@@ -1149,7 +1151,12 @@ extension Formatter {
|
||||
return false
|
||||
}
|
||||
|
||||
return blockBodyHasSingleStatement(atStartOfScope: branch.startOfBranch, includingConditionalStatements: true)
|
||||
return blockBodyHasSingleStatement(
|
||||
atStartOfScope: branch.startOfBranch,
|
||||
includingConditionalStatements: true,
|
||||
includingReturnStatements: includingReturnInConditionalStatements ?? includingReturnStatements,
|
||||
includingReturnInConditionalStatements: includingReturnInConditionalStatements
|
||||
)
|
||||
}
|
||||
|
||||
let endOfStatement = conditionalBranches.last?.endOfBranch ?? firstTokenInBody
|
||||
|
||||
+23
-5
@@ -3175,7 +3175,11 @@ public struct _FormatRules {
|
||||
}
|
||||
|
||||
// Make sure the body only has a single statement
|
||||
guard formatter.blockBodyHasSingleStatement(atStartOfScope: startOfScopeIndex) else {
|
||||
guard formatter.blockBodyHasSingleStatement(
|
||||
atStartOfScope: startOfScopeIndex,
|
||||
includingConditionalStatements: true,
|
||||
includingReturnStatements: true
|
||||
) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6222,13 +6226,22 @@ public struct _FormatRules {
|
||||
{
|
||||
/// Whether or not this closure has a single, simple expression in its body.
|
||||
/// These closures can always be simplified / removed regardless of the context.
|
||||
let hasSingleSimpleExpression = formatter.blockBodyHasSingleStatement(atStartOfScope: closureStartIndex, includingConditionalStatements: false)
|
||||
let hasSingleSimpleExpression = formatter.blockBodyHasSingleStatement(
|
||||
atStartOfScope: closureStartIndex,
|
||||
includingConditionalStatements: false,
|
||||
includingReturnStatements: true
|
||||
)
|
||||
|
||||
/// Whether or not this closure has a single if/switch expression in its body.
|
||||
/// Since if/switch expressions are only valid in the `return` position or as an `=` assignment,
|
||||
/// these closures can only sometimes be simplified / removed.
|
||||
let hasSingleConditionalExpression = !hasSingleSimpleExpression &&
|
||||
formatter.blockBodyHasSingleStatement(atStartOfScope: closureStartIndex, includingConditionalStatements: true)
|
||||
formatter.blockBodyHasSingleStatement(
|
||||
atStartOfScope: closureStartIndex,
|
||||
includingConditionalStatements: true,
|
||||
includingReturnStatements: true,
|
||||
includingReturnInConditionalStatements: false
|
||||
)
|
||||
|
||||
guard hasSingleSimpleExpression || hasSingleConditionalExpression else {
|
||||
return
|
||||
@@ -6912,7 +6925,8 @@ public struct _FormatRules {
|
||||
}
|
||||
|
||||
public let conditionalAssignment = FormatRule(
|
||||
help: "Assign properties using if / switch expressions."
|
||||
help: "Assign properties using if / switch expressions.",
|
||||
orderAfter: ["redundantReturn"]
|
||||
) { formatter in
|
||||
// If / switch expressions were added in Swift 5.9 (SE-0380)
|
||||
guard formatter.options.swiftVersion >= "5.9" else {
|
||||
@@ -7010,7 +7024,11 @@ public struct _FormatRules {
|
||||
tempScopeTokens.append(.endOfScope("}"))
|
||||
|
||||
let tempFormatter = Formatter(tempScopeTokens, options: formatter.options)
|
||||
guard tempFormatter.blockBodyHasSingleStatement(atStartOfScope: 0) else {
|
||||
guard tempFormatter.blockBodyHasSingleStatement(
|
||||
atStartOfScope: 0,
|
||||
includingConditionalStatements: true,
|
||||
includingReturnStatements: false
|
||||
) else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -2722,6 +2722,22 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantReturn, options: options)
|
||||
}
|
||||
|
||||
func testClosureAroundConditionalAssignmentNotRedundantForExplicitReturn() {
|
||||
let input = """
|
||||
let myEnum = MyEnum.a
|
||||
let test: Int = {
|
||||
switch myEnum {
|
||||
case .a:
|
||||
return 0
|
||||
case .b:
|
||||
return 1
|
||||
}
|
||||
}()
|
||||
"""
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, rule: FormatRules.redundantClosure, options: options, exclude: ["redundantReturn"])
|
||||
}
|
||||
|
||||
func testNonRedundantSwitchStatementReturnInFunction() {
|
||||
let input = """
|
||||
func foo(condition: Bool) -> String {
|
||||
@@ -7833,7 +7849,8 @@ class RedundancyTests: RulesTests {
|
||||
"""
|
||||
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options, exclude: ["indent"])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure],
|
||||
options: options, exclude: ["indent"])
|
||||
}
|
||||
|
||||
func testRedundantClosureWithExplicitReturn2() {
|
||||
@@ -7869,7 +7886,7 @@ class RedundancyTests: RulesTests {
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, exclude: ["redundantReturn"])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure])
|
||||
}
|
||||
|
||||
func testKeepsClosureThatIsNotCalled() {
|
||||
@@ -7936,7 +7953,8 @@ class RedundancyTests: RulesTests {
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantClosure, FormatRules.semicolons])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure,
|
||||
FormatRules.semicolons])
|
||||
}
|
||||
|
||||
func testRemoveRedundantClosureInWrappedPropertyDeclaration_beforeFirst() {
|
||||
@@ -8222,14 +8240,14 @@ class RedundancyTests: RulesTests {
|
||||
"""
|
||||
let output = """
|
||||
let user2: User? = if let data2 = defaults.data(forKey: defaultsKey) {
|
||||
return try PropertyListDecoder().decode(User.self, from: data2)
|
||||
try PropertyListDecoder().decode(User.self, from: data2)
|
||||
} else {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options,
|
||||
exclude: ["redundantReturn", "indent"])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure],
|
||||
options: options, exclude: ["indent"])
|
||||
}
|
||||
|
||||
func testRedundantClosureDoesntLeaveStrayTryAwait() {
|
||||
@@ -8244,14 +8262,14 @@ class RedundancyTests: RulesTests {
|
||||
"""
|
||||
let output = """
|
||||
let user2: User? = if let data2 = defaults.data(forKey: defaultsKey) {
|
||||
return try await PropertyListDecoder().decode(User.self, from: data2)
|
||||
try await PropertyListDecoder().decode(User.self, from: data2)
|
||||
} else {
|
||||
return nil
|
||||
nil
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options,
|
||||
exclude: ["redundantReturn", "indent"])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure],
|
||||
options: options, exclude: ["indent"])
|
||||
}
|
||||
|
||||
func testRedundantClosureDoesntLeaveInvalidSwitchExpressionInOperatorChain() {
|
||||
@@ -8525,7 +8543,8 @@ class RedundancyTests: RulesTests {
|
||||
"""
|
||||
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options, exclude: ["redundantReturn", "indent"])
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantClosure],
|
||||
options: options, exclude: ["redundantReturn", "indent"])
|
||||
}
|
||||
|
||||
func testRedundantClosureRemovesClosureAsImplicitReturnStatement() {
|
||||
@@ -8706,7 +8725,70 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options, exclude: ["indent"])
|
||||
}
|
||||
|
||||
// MARK: Redundant optional binding
|
||||
func testRedundantClosureDoesntBreakBuildWithRedundantReturnRuleDisabled() {
|
||||
let input = """
|
||||
enum MyEnum {
|
||||
case a
|
||||
case b
|
||||
}
|
||||
let myEnum = MyEnum.a
|
||||
let test: Int = {
|
||||
return 0
|
||||
}()
|
||||
"""
|
||||
|
||||
let output = """
|
||||
enum MyEnum {
|
||||
case a
|
||||
case b
|
||||
}
|
||||
let myEnum = MyEnum.a
|
||||
let test: Int = 0
|
||||
"""
|
||||
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, output, rule: FormatRules.redundantClosure, options: options,
|
||||
exclude: ["redundantReturn", "blankLinesBetweenScopes"])
|
||||
}
|
||||
|
||||
func testRedundantClosureWithSwitchExpressionDoesntBreakBuildWithRedundantReturnRuleDisabled() {
|
||||
// From https://github.com/nicklockwood/SwiftFormat/issues/1565
|
||||
let input = """
|
||||
enum MyEnum {
|
||||
case a
|
||||
case b
|
||||
}
|
||||
let myEnum = MyEnum.a
|
||||
let test: Int = {
|
||||
switch myEnum {
|
||||
case .a:
|
||||
return 0
|
||||
case .b:
|
||||
return 1
|
||||
}
|
||||
}()
|
||||
"""
|
||||
|
||||
let output = """
|
||||
enum MyEnum {
|
||||
case a
|
||||
case b
|
||||
}
|
||||
let myEnum = MyEnum.a
|
||||
let test: Int = switch myEnum {
|
||||
case .a:
|
||||
0
|
||||
case .b:
|
||||
1
|
||||
}
|
||||
"""
|
||||
|
||||
let options = FormatOptions(swiftVersion: "5.9")
|
||||
testFormatting(for: input, [output], rules: [FormatRules.redundantReturn, FormatRules.redundantClosure],
|
||||
options: options, exclude: ["indent", "blankLinesBetweenScopes"])
|
||||
}
|
||||
|
||||
// MARK: - redundantOptionalBinding
|
||||
|
||||
func testRemovesRedundantOptionalBindingsInSwift5_7() {
|
||||
let input = """
|
||||
|
||||
Reference in New Issue
Block a user