diff --git a/Rules.md b/Rules.md index 98ebcdb4..b81f8aba 100644 --- a/Rules.md +++ b/Rules.md @@ -3087,47 +3087,40 @@ Option | Description foo, bar, - baz - ] - - let array = [ - foo, - bar, + baz, ] ``` ```diff -func foo( -- bar _: Int -) {} - -func foo( -+ bar _: Int, -) {} + func foo( + bar _: Int, +- baaz _: Int ++ baaz _: Int + ) {} ``` ```diff -let foo = ( - bar: 0, -- baz: 1 -) - -let foo = ( - bar: 0, -+ baz: 1, -) + let foo = ( + bar: 0, +- baz: 1 ++ baz: 1, + ) ``` ```diff -@Foo( - "bar", -- "baz" -) + if + let foo, +- let baaz ++ let baaz, + { ... } +``` -@Foo( - "bar", -+ "baz", -) +```diff + guard + let foo, +- let baaz ++ let baaz, + else { return } ``` diff --git a/Sources/FormattingHelpers.swift b/Sources/FormattingHelpers.swift index a16c76f6..c64792b9 100644 --- a/Sources/FormattingHelpers.swift +++ b/Sources/FormattingHelpers.swift @@ -1374,7 +1374,7 @@ extension Formatter { while let conditionalBranchIndex = nextConditionalBranchIndex, conditionalBranchIndex == ifIndex || tokens[conditionalBranchIndex] == .keyword("else"), - let startOfBody = index(of: .startOfScope("{"), after: conditionalBranchIndex), + let startOfBody = startOfConditionalBranchBody(after: conditionalBranchIndex), let endOfBody = endOfScope(at: startOfBody) { branches.append((startOfBranch: startOfBody, endOfBranch: endOfBody)) @@ -1390,6 +1390,20 @@ extension Formatter { return branches } + /// Returns the `startOfScope("{")` token index for this conditional branch + func startOfConditionalBranchBody(after index: Int) -> Int? { + guard let startOfBody = self.index(of: .startOfScope("{"), after: index) else { return nil } + + // If we find a closure, skip over it. + if isStartOfClosure(at: startOfBody), + let endOfClosure = endOfScope(at: startOfBody) + { + return startOfConditionalBranchBody(after: endOfClosure) + } + + return startOfBody + } + /// Finds all of the branch bodies in a switch statement. /// Returns the index of the `startOfScope` and `endOfScope` of each branch. func switchStatementBranches(at switchIndex: Int) -> [ConditionalBranch]? { diff --git a/Sources/ParsingHelpers.swift b/Sources/ParsingHelpers.swift index 917bd26a..d599ff0d 100644 --- a/Sources/ParsingHelpers.swift +++ b/Sources/ParsingHelpers.swift @@ -920,11 +920,30 @@ extension Formatter { return true } case .delimiter(","): - guard let scope = scope ?? currentScope(at: i) else { - return false + // If this is the trailing comma in an if/guard/while condition list, then don't indent the following code (the condition body) + if isConditionalStatement(at: i), + let startOfBranchBody = startOfConditionalBranchBody(after: i), + let nextToken = index(of: .nonSpaceOrCommentOrLinebreak, after: i) + { + if nextToken == startOfBranchBody { + return true + } + + // Handle guard statement lists ending with `else {` instead of just `{` + if tokens[nextToken] == .keyword("else"), + let followingToken = index(of: .nonSpaceOrCommentOrLinebreak, after: nextToken), + followingToken == startOfBranchBody + { + return true + } } + // For arrays or argument lists, we already indent - return ["<", "[", "(", "case", "default"].contains(scope.string) + if let scope = scope ?? currentScope(at: i) { + return ["<", "[", "(", "case", "default"].contains(scope.string) + } + + return false case .delimiter(":"): guard let scope = scope ?? currentScope(at: i) else { return false diff --git a/Sources/Rules/OpaqueGenericParameters.swift b/Sources/Rules/OpaqueGenericParameters.swift index e0a99398..1f13c09b 100644 --- a/Sources/Rules/OpaqueGenericParameters.swift +++ b/Sources/Rules/OpaqueGenericParameters.swift @@ -225,6 +225,10 @@ public extension FormatRule { [type.definitionSourceRange] + type.conformances.map(\.sourceRange) }) + if genericsEligibleToRemove.isEmpty { + return + } + // We perform modifications to the function signature in reverse order // so we don't invalidate any of the indices we've recorded. So first // we remove components of the where clause. diff --git a/Sources/Rules/TrailingCommas.swift b/Sources/Rules/TrailingCommas.swift index 630d081b..63290ae8 100644 --- a/Sources/Rules/TrailingCommas.swift +++ b/Sources/Rules/TrailingCommas.swift @@ -15,85 +15,54 @@ public extension FormatRule { help: "Add or remove trailing commas where applicable.", options: ["commas"] ) { formatter in - formatter.forEach(.endOfScope("]")) { i, _ in - guard let prevTokenIndex = formatter.index(of: .nonSpaceOrComment, before: i), - let scopeType = formatter.scopeType(at: i) - else { - return - } - switch scopeType { - case .array, .dictionary: - switch formatter.tokens[prevTokenIndex] { - case .linebreak: - guard let prevTokenIndex = formatter.index( - of: .nonSpaceOrCommentOrLinebreak, before: prevTokenIndex + 1 - ) else { - break - } - switch formatter.tokens[prevTokenIndex] { - case .startOfScope("["), .delimiter(":"): - break // do nothing - case .delimiter(","): - if !formatter.options.trailingCommas { - formatter.removeToken(at: prevTokenIndex) - } - default: - if formatter.options.trailingCommas { - formatter.insert(.delimiter(","), at: prevTokenIndex + 1) - } - } - case .delimiter(","): - formatter.removeToken(at: prevTokenIndex) + formatter.forEachToken { i, token in + switch token { + case .endOfScope("]"): + switch formatter.scopeType(at: i) { + case .array, .dictionary: + formatter.addOrRemoveTrailingComma(before: i, trailingCommaSupported: true) + case .subscript, .captureList: + formatter.addOrRemoveTrailingComma(before: i, trailingCommaSupported: formatter.options.swiftVersion >= "6.1") default: - break + return } - default: - return - } - } - guard formatter.options.swiftVersion >= "6.1" else { return } + case .endOfScope(")"), .endOfScope(">"): + formatter.addOrRemoveTrailingComma(before: i, trailingCommaSupported: formatter.options.swiftVersion >= "6.1") - formatter.forEach(.endOfScope(")")) { i, _ in - guard let startIndex = formatter.startOfScope(at: i), - formatter.tokens[startIndex] == .startOfScope("(") - else { - return - } + case .keyword("if"): + guard let startOfConditions = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i), + let startOfBody = formatter.startOfConditionalBranchBody(after: startOfConditions) + else { return } - guard let prevToStartTokenIndex = formatter.index(of: .nonSpaceOrComment, before: startIndex) else { - return - } + formatter.addOrRemoveTrailingComma(before: startOfBody, trailingCommaSupported: formatter.options.swiftVersion >= "6.1") - guard formatter.tokens[prevToStartTokenIndex] != .delimiter(":") else { - return - } + case .keyword("guard"): + guard let startOfConditions = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i), + let startOfBody = formatter.startOfConditionalBranchBody(after: startOfConditions), + let elseKeyword = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: startOfBody), + formatter.tokens[elseKeyword] == .keyword("else") + else { return } - guard let prevToEndTokenIndex = formatter.index(of: .nonSpaceOrComment, before: i) else { - return - } + formatter.addOrRemoveTrailingComma(before: elseKeyword, trailingCommaSupported: formatter.options.swiftVersion >= "6.1") - switch formatter.tokens[prevToEndTokenIndex] { - case .linebreak: - guard let lastArgIndex = formatter.index( - of: .nonSpaceOrCommentOrLinebreak, before: prevToEndTokenIndex + 1 - ) else { - break + case .keyword("while"): + guard let startOfConditions = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i), + let startOfBody = formatter.startOfConditionalBranchBody(after: startOfConditions) + else { return } + + // Ensure this isn't a `repeat { ... } while ...` condition where any `{` token after the while keyword would be unrelated + if let previousToken = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: i), + formatter.tokens[previousToken] == .endOfScope("}"), + let startOfScope = formatter.startOfScope(at: previousToken), + let tokenBeforeStartOfScope = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: startOfScope), + formatter.tokens[tokenBeforeStartOfScope] == .keyword("repeat") + { + return } - switch formatter.tokens[lastArgIndex] { - case .delimiter(","): - if !formatter.options.trailingCommas { - formatter.removeToken(at: lastArgIndex) - } - case .startOfScope("("): - break - default: - if formatter.options.trailingCommas { - formatter.insert(.delimiter(","), at: lastArgIndex + 1) - } - } - case .delimiter(","): - formatter.removeToken(at: prevToEndTokenIndex) + + formatter.addOrRemoveTrailingComma(before: startOfBody, trailingCommaSupported: formatter.options.swiftVersion >= "6.1") + default: break } @@ -105,48 +74,75 @@ public extension FormatRule { foo, bar, - baz - ] - - let array = [ - foo, - bar, + baz, ] ``` ```diff - func foo( - - bar _: Int - ) {} - - func foo( - + bar _: Int, - ) {} + func foo( + bar _: Int, + - baaz _: Int + + baaz _: Int + ) {} ``` ```diff - let foo = ( - bar: 0, - - baz: 1 - ) - - let foo = ( - bar: 0, - + baz: 1, - ) + let foo = ( + bar: 0, + - baz: 1 + + baz: 1, + ) ``` ```diff - @Foo( - "bar", - - "baz" - ) + if + let foo, + - let baaz + + let baaz, + { ... } + ``` - @Foo( - "bar", - + "baz", - ) + ```diff + guard + let foo, + - let baaz + + let baaz, + else { return } ``` """ } } + +extension Formatter { + /// Adds or removes a trailing comma before the given index that marks the end of a comma-separated list. + /// Trailing commas can always be removed. `trailingCommaSupported` indicates whether or not a trailing + /// comma is allowed at this position. + func addOrRemoveTrailingComma(before endOfListIndex: Int, trailingCommaSupported: Bool) { + guard let prevTokenIndex = index(of: .nonSpaceOrComment, before: endOfListIndex) else { return } + + switch tokens[prevTokenIndex] { + case .linebreak: + guard let prevTokenIndex = index( + of: .nonSpaceOrCommentOrLinebreak, before: prevTokenIndex + 1 + ) else { + break + } + switch tokens[prevTokenIndex] { + case .startOfScope("["), .delimiter(":"), .startOfScope("("): + break // do nothing + case .delimiter(","): + if !options.trailingCommas { + removeToken(at: prevTokenIndex) + } + default: + if options.trailingCommas, trailingCommaSupported { + insert(.delimiter(","), at: prevTokenIndex + 1) + } + } + case .delimiter(","): + removeToken(at: prevTokenIndex) + default: + break + } + } +} diff --git a/Tests/Rules/IndentTests.swift b/Tests/Rules/IndentTests.swift index 1fc34b45..a83af76a 100644 --- a/Tests/Rules/IndentTests.swift +++ b/Tests/Rules/IndentTests.swift @@ -4265,4 +4265,56 @@ class IndentTests: XCTestCase { """ testFormatting(for: input, rule: .indent) } + + func testNoIndentIfConditionBodyAfterTrailingCommaInClosure() { + let input = """ + withTrailingClosure { + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz, + ), + { + print("passed condition") + } + } + """ + + testFormatting(for: input, rule: .indent) + } + + func testNoIndentGuardConditionBodyAfterTrailingComma() { + let input = """ + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz, + ), + else { + return + } + """ + + testFormatting(for: input, rule: .indent) + } + + func testNoIndentGuardConditionBodyAfterTrailingCommaInClosure() { + let input = """ + withTrailingClosure { + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz, + ), + else { + return + } + } + """ + + testFormatting(for: input, rule: .indent) + } } diff --git a/Tests/Rules/RedundantReturnTests.swift b/Tests/Rules/RedundantReturnTests.swift index cfe6418d..53428393 100644 --- a/Tests/Rules/RedundantReturnTests.swift +++ b/Tests/Rules/RedundantReturnTests.swift @@ -545,6 +545,30 @@ class RedundantReturnTests: XCTestCase { options: options) } + func testRedundantIfStatementWithClosureCondition() { + let input = """ + func foo(condition: Bool) -> String { + if condition, { true }(), { false }() { + return "foo" + } else { + return "bar" + } + } + """ + + let output = """ + func foo(condition: Bool) -> String { + if condition, { true }(), { false }() { + "foo" + } else { + "bar" + } + } + """ + let options = FormatOptions(swiftVersion: "5.9") + testFormatting(for: input, [output], rules: [.redundantReturn, .conditionalAssignment], options: options, exclude: [.redundantClosure]) + } + func testNonRedundantIfStatementReturnSwift5_9() { let input = """ func foo(condition: Bool) -> String { diff --git a/Tests/Rules/TrailingCommasTests.swift b/Tests/Rules/TrailingCommasTests.swift index a72a4b98..320823d4 100644 --- a/Tests/Rules/TrailingCommasTests.swift +++ b/Tests/Rules/TrailingCommasTests.swift @@ -288,6 +288,16 @@ class TrailingCommasTests: XCTestCase { testFormatting(for: input, output, rule: .trailingCommas, options: options) } + func testTrailingCommasNotAddedToFunctionParametersBeforeSwift6_1() { + let input = """ + func foo( + bar _: Int + ) {} + """ + let options = FormatOptions(trailingCommas: true) + testFormatting(for: input, rule: .trailingCommas, options: options) + } + func testTrailingCommasRemovedFromFunctionParameters() { let input = """ func foo( @@ -299,7 +309,41 @@ class TrailingCommasTests: XCTestCase { bar _: Int ) {} """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromFunctionParametersWithParenOnSameLine_trailingCommasDisabled() { + let input = """ + func foo( + bar _: Int, + baaz _: Int,) + {} + """ + let output = """ + func foo( + bar _: Int, + baaz _: Int) + {} + """ + let options = FormatOptions(trailingCommas: false, closingParenPosition: .sameLine) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromFunctionParametersWithParenOnSameLine_trailingCommasEnabled() { + let input = """ + func foo( + bar _: Int, + baaz _: Int,) + {} + """ + let output = """ + func foo( + bar _: Int, + baaz _: Int) + {} + """ + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -329,7 +373,7 @@ class TrailingCommasTests: XCTestCase { bar _: Int ) {} """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -367,7 +411,7 @@ class TrailingCommasTests: XCTestCase { ) } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -397,7 +441,7 @@ class TrailingCommasTests: XCTestCase { 1 ) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -431,7 +475,7 @@ class TrailingCommasTests: XCTestCase { baz: 1 ) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -485,7 +529,7 @@ class TrailingCommasTests: XCTestCase { ) } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -539,7 +583,7 @@ class TrailingCommasTests: XCTestCase { ) } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -582,138 +626,314 @@ class TrailingCommasTests: XCTestCase { func testTrailingCommasAddedToIf() { let input = """ - if ( - true - ) {} + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + { + print("passed condition") + } """ let output = """ - if ( - true, - ) {} + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { + print("passed condition") + } """ - let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasAddedToIfWithClosureCondition() { + let input = """ + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }() + { + print("passed condition") + } + """ + let output = """ + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }(), + { + print("passed condition") + } + """ + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options, exclude: [.redundantClosure]) + } + + func testTrailingCommasNotAddedToIfBeforeSwift6_1() { + let input = """ + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + { + print("passed condition") + } + """ + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine) + testFormatting(for: input, rule: .trailingCommas, options: options, exclude: [.redundantParens]) } func testTrailingCommasRemovedFromIf() { let input = """ - if ( - true, - ) {} + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { + print("passed condition") + } """ let output = """ - if ( - true - ) {} + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + { + print("passed condition") + } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + let options = FormatOptions(trailingCommas: false, closingParenPosition: .sameLine) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromIfWithClosureArgument() { + let input = """ + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }(), + { + print("passed condition") + } + """ + let output = """ + if + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }() + { + print("passed condition") + } + """ + let options = FormatOptions(trailingCommas: false, closingParenPosition: .sameLine) + testFormatting(for: input, output, rule: .trailingCommas, options: options, exclude: [.redundantClosure]) + } + + func testTrailingCommasRemovedFromSingleLineIf() { + let input = """ + if foo.bar, foo.baaz(), { + print("passed condition") + } + """ + let output = """ + if foo.bar, foo.baaz() { + print("passed condition") + } + """ + let options = FormatOptions(trailingCommas: true) + testFormatting(for: input, output, rule: .trailingCommas, options: options) } func testTrailingCommasAddedToGuard() { let input = """ - guard ( - true - ) else { + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + else { return } """ let output = """ - guard ( - true, - ) else { + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + else { return } """ - let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasNotAddedToGuardBeforeSwift6_1() { + let input = """ + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + else { + return + } + """ + let options = FormatOptions(trailingCommas: true, closingParenPosition: .sameLine) + testFormatting(for: input, rule: .trailingCommas, options: options, exclude: [.redundantParens]) } func testTrailingCommasRemovedFromGuard() { let input = """ - guard ( - true, - ) else { + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + else { return } """ let output = """ - guard ( - true - ) else { + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz) + else { return } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + let options = FormatOptions(trailingCommas: false, closingParenPosition: .sameLine) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromGuardWithClosureArgument() { + let input = """ + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }(), + else { + return + } + """ + let output = """ + guard + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz), + { true }() + else { + return + } + """ + let options = FormatOptions(trailingCommas: false, closingParenPosition: .sameLine) + testFormatting(for: input, output, rule: .trailingCommas, options: options, exclude: [.redundantClosure]) + } + + func testTrailingCommasRemovedFromSingleLineGuard() { + let input = """ + guard foo.bar, foo.baaz(), else { + return + } + """ + let output = """ + guard foo.bar, foo.baaz() else { + return + } + """ + let options = FormatOptions(trailingCommas: true) + testFormatting(for: input, output, rule: .trailingCommas, options: options) } func testTrailingCommasAddedToWhile() { let input = """ - while ( - true - ) {} + while + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz + ) + { + break + } """ let output = """ - while ( - true, - ) {} + while + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz, + ), + { + break + } """ let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasNotAddedToRepeatWhile() { + let input = """ + repeat + { + print(foo) + } while foo + + // Ensure we don't confuse this trailing closure for a condition in the while statement: + trailingClosure + { + print("foo") + } + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, rule: .trailingCommas, options: options, exclude: [.redundantParens, .braces, .indent]) } func testTrailingCommasRemovedFromWhile() { let input = """ - while ( - true, - ) {} + while + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz, + ), + { + break + } """ let output = """ - while ( - true - ) {} + while + foo.bar, + foo.baaz( + bar: foo.bar, + baaz: foo.baaz + ) + { + break + } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) - } - - func testTrailingCommasAddedToBool() { - let input = """ - let foo: Bool = ( - true && true - ) - """ - let output = """ - let foo: Bool = ( - true && true, - ) - """ - let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) - } - - func testTrailingCommasRemovedFromBool() { - let input = """ - let foo: Bool = ( - true && true, - ) - """ - let output = """ - let foo: Bool = ( - true && true - ) - """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") - testFormatting(for: input, output, rule: .trailingCommas, - options: options, exclude: [.redundantParens]) + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) } func testTrailingCommasNotAddedToTypeAnnotation() { @@ -723,7 +943,7 @@ class TrailingCommasTests: XCTestCase { baz: Int ) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, rule: .trailingCommas, options: options) } @@ -769,7 +989,7 @@ class TrailingCommasTests: XCTestCase { ): break } """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -803,7 +1023,7 @@ class TrailingCommasTests: XCTestCase { bar ) = (0, 1) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -813,7 +1033,7 @@ class TrailingCommasTests: XCTestCase { ) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, rule: .trailingCommas, options: options, exclude: [ .blankLinesAtEndOfScope, @@ -859,7 +1079,7 @@ class TrailingCommasTests: XCTestCase { ) \""" """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -897,7 +1117,7 @@ class TrailingCommasTests: XCTestCase { ) struct Qux {} """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) testFormatting(for: input, output, rule: .trailingCommas, options: options) } @@ -931,7 +1151,181 @@ class TrailingCommasTests: XCTestCase { "baz" ) """ - let options = FormatOptions(trailingCommas: false, swiftVersion: "6.1") + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasAddedToGenericList() { + let input = """ + struct S< + T1, + T2, + T3 + > {} + """ + let output = """ + struct S< + T1, + T2, + T3, + > {} + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromGenericList() { + let input = """ + struct S< + T1, + T2, + T3, + > {} + """ + let output = """ + struct S< + T1, + T2, + T3 + > {} + """ + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromSingleLineGenericList() { + let input = """ + struct S {} + """ + let output = """ + struct S {} + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasAddedToCaptureList() { + let input = """ + { [ + capturedValue1, + capturedValue2 + ] in + } + """ + let output = """ + { [ + capturedValue1, + capturedValue2, + ] in + } + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromCaptureList() { + let input = """ + { [ + capturedValue1, + capturedValue2, + ] in + } + """ + let output = """ + { [ + capturedValue1, + capturedValue2 + ] in + } + """ + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromSingleLineCaptureList() { + let input = """ + { [capturedValue1, capturedValue2,] in + print(capturedValue1, capturedValue2) + } + """ + let output = """ + { [capturedValue1, capturedValue2] in + print(capturedValue1, capturedValue2) + } + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasAddedToSubscript() { + let input = """ + let value = m[ + x, + y + ] + """ + let output = """ + let value = m[ + x, + y, + ] + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromSubscript() { + let input = """ + let value = m[ + x, + y, + ] + """ + let output = """ + let value = m[ + x, + y + ] + """ + let options = FormatOptions(trailingCommas: false) + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testTrailingCommasRemovedFromSingleLineSubscript() { + let input = """ + let value = m[x, y,] + """ + let output = """ + let value = m[x, y] + """ + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") + testFormatting(for: input, output, rule: .trailingCommas, options: options) + } + + func testAddingTrailingCommaDoesntConflictWithOpaqueGenericParametersRule() { + let input = """ + private func foo< + Foo: Bar, + Bar: Baaz + >(a: Foo, b: Foo) + where Foo == Bar + { + print(a, b) + } + """ + + let output = """ + private func foo< + Foo: Bar, + Bar: Baaz, + >(a: Foo, b: Foo) + where Foo == Bar + { + print(a, b) + } + """ + + let options = FormatOptions(trailingCommas: true, swiftVersion: "6.1") testFormatting(for: input, output, rule: .trailingCommas, options: options) } }