diff --git a/Sources/Rules.swift b/Sources/Rules.swift index f5ec3083..72797a27 100644 --- a/Sources/Rules.swift +++ b/Sources/Rules.swift @@ -2710,15 +2710,20 @@ public struct _FormatRules { innerParens = nestedParens(in: i ... closingIndex) } var isClosure = false + let previousIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: i) ?? -1 + let prevToken = formatter.token(at: previousIndex) ?? .space("") let nextToken = formatter.next(.nonSpaceOrCommentOrLinebreak, after: closingIndex) ?? .space("") switch nextToken { case .operator("->", .infix), .keyword("throws"), .keyword("rethrows"), .identifier("async"), .keyword("in"): - guard let prevIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: i) else { - return + if prevToken != .keyword("throws"), + formatter.index(before: i, where: { + [.endOfScope(")"), .operator("->", .infix), .keyword("for")].contains($0) + }) == nil, + let scopeIndex = formatter.startOfScope(at: i) + { + isClosure = formatter.isStartOfClosure(at: scopeIndex) } - isClosure = formatter.tokens[prevIndex] == .endOfScope("]") || - formatter.isStartOfClosure(at: prevIndex) if !isClosure, nextToken != .keyword("in") { return // It's a closure type or function declaration } @@ -2731,8 +2736,6 @@ public struct _FormatRules { default: break } - let previousIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: i) ?? -1 - let prevToken = formatter.token(at: previousIndex) ?? .space("") switch prevToken { case _ where isClosure: if formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i) == closingIndex || diff --git a/Tests/RulesTests+Parens.swift b/Tests/RulesTests+Parens.swift index 678af2b7..b34387ea 100644 --- a/Tests/RulesTests+Parens.swift +++ b/Tests/RulesTests+Parens.swift @@ -570,6 +570,12 @@ class ParensTests: RulesTests { testFormatting(for: input, output, rule: FormatRules.redundantParens, exclude: ["unusedArguments"]) } + func testSingleMainActorClosureArgumentUnwrapped() { + let input = "{ @MainActor (foo) in }" + let output = "{ @MainActor foo in }" + testFormatting(for: input, output, rule: FormatRules.redundantParens, exclude: ["unusedArguments"]) + } + func testSingleClosureArgumentWithReturnValueUnwrapped() { let input = "{ (foo) -> Int in 5 }" let output = "{ foo -> Int in 5 }" @@ -631,7 +637,21 @@ class ParensTests: RulesTests { func testNoRemoveParensAroundArrayInitializer() { let input = "let foo = bar { [Int](foo) }" - testFormatting(for: input, rule: FormatRules.spaceAroundParens) + testFormatting(for: input, rule: FormatRules.redundantParens) + } + + func testNoRemoveParensAroundForIndexInsideClosure() { + let input = """ + let foo = { + for (i, token) in bar {} + }() + """ + testFormatting(for: input, rule: FormatRules.redundantParens) + } + + func testNoRemoveRequiredParensInsideClosure() { + let input = "let foo = { _ in (a + b).c }" + testFormatting(for: input, rule: FormatRules.redundantParens) } // before trailing closure