mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Add preliminary support for typed throws
This commit is contained in:
@@ -594,10 +594,7 @@ extension Formatter {
|
||||
"func", "subscript", "catch":
|
||||
return false
|
||||
case "throws", "rethrows":
|
||||
return next(
|
||||
.nonSpaceOrLinebreak,
|
||||
after: prevKeywordIndex
|
||||
) == .keyword("in")
|
||||
return next(.keyword, after: prevKeywordIndex) == .keyword("in")
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
+1
-1
@@ -233,7 +233,7 @@ public struct _FormatRules {
|
||||
}
|
||||
return false
|
||||
case "private", "fileprivate", "internal",
|
||||
"init", "subscript":
|
||||
"init", "subscript", "throws":
|
||||
return false
|
||||
case "await":
|
||||
return formatter.options.swiftVersion >= "5.5" ||
|
||||
|
||||
@@ -550,6 +550,23 @@ class ParsingHelpersTests: XCTestCase {
|
||||
XCTAssert(formatter.isStartOfClosure(at: 6))
|
||||
}
|
||||
|
||||
func testTypedThrowingClosure() {
|
||||
let formatter = Formatter(tokenize("""
|
||||
let foo = { bar throws(Foo) in bar }
|
||||
"""))
|
||||
XCTAssert(formatter.isStartOfClosure(at: 6))
|
||||
}
|
||||
|
||||
func testNestedTypedThrowingClosures() {
|
||||
let formatter = Formatter(tokenize("""
|
||||
try! str.withCString(encodedAs: UTF8.self) { _ throws(Foo) in
|
||||
try! str.withCString(encodedAs: UTF8.self) { _ throws(Foo) in }
|
||||
}
|
||||
"""))
|
||||
XCTAssert(formatter.isStartOfClosure(at: 15))
|
||||
XCTAssert(formatter.isStartOfClosure(at: 42))
|
||||
}
|
||||
|
||||
func testTrailingClosureOnOptionalMethod() {
|
||||
let formatter = Formatter(tokenize("""
|
||||
foo.bar? { print("") }
|
||||
|
||||
@@ -3710,6 +3710,17 @@ class IndentTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.indent, options: options)
|
||||
}
|
||||
|
||||
func testAsyncTypedThrowsNotUnindented() {
|
||||
let input = """
|
||||
func multilineFunction(
|
||||
foo _: String,
|
||||
bar _: String)
|
||||
async throws(Foo) -> String {}
|
||||
"""
|
||||
let options = FormatOptions(closingParenOnSameLine: true)
|
||||
testFormatting(for: input, rule: FormatRules.indent, options: options)
|
||||
}
|
||||
|
||||
func testIndentAsyncLet() {
|
||||
let input = """
|
||||
func foo() async {
|
||||
|
||||
@@ -852,6 +852,19 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.redundantInit, exclude: ["indent"])
|
||||
}
|
||||
|
||||
func testNoRemoveInitInsideIfdef2() {
|
||||
let input = """
|
||||
func myFunc() async throws(Foo) -> String {
|
||||
#if DEBUG
|
||||
.init("foo")
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: FormatRules.redundantInit, exclude: ["indent"])
|
||||
}
|
||||
|
||||
// MARK: - redundantLetError
|
||||
|
||||
func testCatchLetError() {
|
||||
@@ -4089,6 +4102,18 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.redundantSelf)
|
||||
}
|
||||
|
||||
func testTypedThrowingNestedClosureInNotMistakenForForLoop() {
|
||||
let input = """
|
||||
func f() {
|
||||
let str = "hello"
|
||||
try! str.withCString(encodedAs: UTF8.self) { _ throws(Foo) in
|
||||
try! str.withCString(encodedAs: UTF8.self) { _ throws(Foo) in }
|
||||
}
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: FormatRules.redundantSelf)
|
||||
}
|
||||
|
||||
func testRedundantSelfPreservesSelfInClosureWithExplicitStrongCaptureBefore5_3() {
|
||||
let input = """
|
||||
class Foo {
|
||||
@@ -6135,6 +6160,27 @@ class RedundancyTests: RulesTests {
|
||||
exclude: ["hoistPatternLet"])
|
||||
}
|
||||
|
||||
func testSelfRemovalParsingBug7() {
|
||||
let input = """
|
||||
extension Dictionary where Key == String {
|
||||
func requiredValue<T>(for keyPath: String) throws(Foo) -> T {
|
||||
return keyPath as! T
|
||||
}
|
||||
|
||||
func optionalValue<T>(for keyPath: String) throws(Foo) -> T? {
|
||||
guard let anyValue = self[keyPath] else {
|
||||
return nil
|
||||
}
|
||||
guard let value = anyValue as? T else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: FormatRules.redundantSelf)
|
||||
}
|
||||
|
||||
func testSelfNotRemovedInCaseIfElse() {
|
||||
let input = """
|
||||
class Foo {
|
||||
@@ -6929,6 +6975,11 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testNoRemoveClosureTypedThrows() {
|
||||
let input = "let foo = { () throws(Foo) in }"
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testNoRemoveClosureGenericReturnTypes() {
|
||||
let input = "let foo = { () -> Promise<String> in bar }"
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
@@ -7097,11 +7148,22 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testUnusedTypedThrowingClosureArgument() {
|
||||
let input = "foo = { bar throws(Foo) in \"\" }"
|
||||
let output = "foo = { _ throws(Foo) in \"\" }"
|
||||
testFormatting(for: input, output, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testUsedThrowingClosureArgument() {
|
||||
let input = "let foo = { bar throws in bar + \"\" }"
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testUsedTypedThrowingClosureArgument() {
|
||||
let input = "let foo = { bar throws(Foo) in bar + \"\" }"
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testUnusedTrailingAsyncClosureArgument() {
|
||||
let input = """
|
||||
app.get { foo async in
|
||||
@@ -7483,6 +7545,17 @@ class RedundancyTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testTypedTryAwaitArgumentNotMarkedUnused() {
|
||||
let input = """
|
||||
func foo(bar: String) async throws(Foo) -> String? {
|
||||
let bar = try
|
||||
await parse(bar)
|
||||
return bar
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: FormatRules.unusedArguments)
|
||||
}
|
||||
|
||||
func testConditionalIfLetMarkedAsUnused() {
|
||||
let input = """
|
||||
func foo(bar: UIViewController) {
|
||||
|
||||
@@ -199,6 +199,12 @@ class SpacingTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.spaceAroundParens, exclude: ["redundantVoidReturnType"])
|
||||
}
|
||||
|
||||
func testAddSpaceBetweenCaptureListAndArguments8() {
|
||||
let input = "{ [weak self]() throws(Foo) -> Void in }"
|
||||
let output = "{ [weak self] () throws(Foo) -> Void in }"
|
||||
testFormatting(for: input, output, rule: FormatRules.spaceAroundParens, exclude: ["redundantVoidReturnType"])
|
||||
}
|
||||
|
||||
func testAddSpaceBetweenEscapingAndParenthesizedClosure() {
|
||||
let input = "@escaping(() -> Void)"
|
||||
let output = "@escaping (() -> Void)"
|
||||
@@ -316,6 +322,12 @@ class SpacingTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.spaceAroundParens)
|
||||
}
|
||||
|
||||
func testNoAddSpaceAroundTypedThrowsFunctionType() {
|
||||
let input = "func foo() throws (Bar) -> Baz {}"
|
||||
let output = "func foo() throws(Bar) -> Baz {}"
|
||||
testFormatting(for: input, output, rule: FormatRules.spaceAroundParens)
|
||||
}
|
||||
|
||||
// MARK: - spaceInsideParens
|
||||
|
||||
func testSpaceInsideParens() {
|
||||
|
||||
@@ -217,6 +217,12 @@ class SyntaxTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testFunctionThatReturnsAFunctionThatHasTypedThrows() {
|
||||
let input = "(Void) -> Void throws(Foo) -> ()"
|
||||
let output = "(Void) -> () throws(Foo) -> Void"
|
||||
testFormatting(for: input, output, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testChainOfFunctionsIsNotChanged() {
|
||||
let input = "() -> () -> () -> Void"
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
@@ -227,11 +233,21 @@ class SyntaxTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testChainOfFunctionsWithTypedThrowsIsNotChanged() {
|
||||
let input = "() -> () throws(Foo) -> () throws(Foo) -> Void"
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testVoidThrowsIsNotMangled() {
|
||||
let input = "(Void) throws -> Void"
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testVoidTypedThrowsIsNotMangled() {
|
||||
let input = "(Void) throws(Foo) -> Void"
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
}
|
||||
|
||||
func testEmptyClosureArgsNotMangled() {
|
||||
let input = "{ () in }"
|
||||
testFormatting(for: input, rule: FormatRules.void)
|
||||
@@ -2965,8 +2981,10 @@ class SyntaxTests: RulesTests {
|
||||
let input = """
|
||||
func foo<Foo>(_: (Foo) -> Void) {}
|
||||
func bar<Foo>(_: (Foo) throws -> Void) {}
|
||||
func baz<Foo>(_: (Foo) throws(Bar) -> Void) {}
|
||||
func baaz<Foo>(_: (Foo) async -> Void) {}
|
||||
func quux<Foo>(_: (Foo) async throws -> Void) {}
|
||||
func qux<Foo>(_: (Foo) async throws -> Void) {}
|
||||
func quux<Foo>(_: (Foo) async throws(Bar) -> Void) {}
|
||||
func qaax<Foo>(_: ([Foo]) -> Void) {}
|
||||
func qaax<Foo>(_: ((Foo, Bar)) -> Void) {}
|
||||
"""
|
||||
|
||||
@@ -873,6 +873,28 @@ class WrappingTests: RulesTests {
|
||||
testFormatting(for: input, [output, output2], rules: [FormatRules.wrap], options: options, exclude: ["wrapMultilineStatementBraces"])
|
||||
}
|
||||
|
||||
func testWrapFunctionIfReturnTypeExceedsMaxWidth2WithXcodeIndentation2() {
|
||||
let input = """
|
||||
func testFunc() throws(Foo) -> (ReturnType, ReturnType2) {
|
||||
doSomething()
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func testFunc() throws(Foo)
|
||||
-> (ReturnType, ReturnType2) {
|
||||
doSomething()
|
||||
}
|
||||
"""
|
||||
let output2 = """
|
||||
func testFunc() throws(Foo)
|
||||
-> (ReturnType, ReturnType2) {
|
||||
doSomething()
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(xcodeIndentation: true, maxWidth: 35)
|
||||
testFormatting(for: input, [output, output2], rules: [FormatRules.wrap], options: options, exclude: ["wrapMultilineStatementBraces"])
|
||||
}
|
||||
|
||||
func testWrapFunctionIfReturnTypeExceedsMaxWidth3() {
|
||||
let input = """
|
||||
func testFunc() -> (Bool, String) -> String? {
|
||||
@@ -989,6 +1011,22 @@ class WrappingTests: RulesTests {
|
||||
testFormatting(for: input, output, rule: FormatRules.wrap, options: options, exclude: ["wrapMultilineStatementBraces"])
|
||||
}
|
||||
|
||||
func testWrapTypedThrowingFunctionIfReturnTypeExceedsMaxWidth() {
|
||||
let input = """
|
||||
func testFunc(_: () -> Void) throws(Foo) -> (Bool, String) -> String? {
|
||||
doSomething()
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func testFunc(_: () -> Void) throws(Foo)
|
||||
-> (Bool, String) -> String? {
|
||||
doSomething()
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(maxWidth: 42)
|
||||
testFormatting(for: input, output, rule: FormatRules.wrap, options: options, exclude: ["wrapMultilineStatementBraces"])
|
||||
}
|
||||
|
||||
func testNoWrapInterpolatedStringLiteral() {
|
||||
let input = """
|
||||
"a very long \\(string) literal"
|
||||
@@ -1783,6 +1821,28 @@ class WrappingTests: RulesTests {
|
||||
options: options)
|
||||
}
|
||||
|
||||
func testWrapParametersListBeforeFirstInTypedThrowingClosureType() {
|
||||
let input = """
|
||||
var mathFunction: (Int,
|
||||
Int, String) throws(Foo) -> Int = { _, _, _ in
|
||||
0
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var mathFunction: (
|
||||
Int,
|
||||
Int,
|
||||
String
|
||||
) throws(Foo) -> Int = { _, _, _ in
|
||||
0
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(wrapParameters: .beforeFirst)
|
||||
testFormatting(for: input, [output],
|
||||
rules: [FormatRules.wrapArguments],
|
||||
options: options)
|
||||
}
|
||||
|
||||
func testWrapParametersListBeforeFirstInRethrowingClosureType() {
|
||||
let input = """
|
||||
var mathFunction: (Int,
|
||||
@@ -2959,6 +3019,21 @@ class WrappingTests: RulesTests {
|
||||
testFormatting(for: input, rule: FormatRules.wrapArguments, options: options)
|
||||
}
|
||||
|
||||
func testDoesntWrapReturnAndTypedEffectOnSingleLineFunctionDeclaration() {
|
||||
let input = """
|
||||
func singleLineFunction() async throws(Foo) -> String {}
|
||||
"""
|
||||
|
||||
let options = FormatOptions(
|
||||
wrapArguments: .beforeFirst,
|
||||
closingParenOnSameLine: true,
|
||||
wrapReturnType: .ifMultiline,
|
||||
wrapEffects: .ifMultiline
|
||||
)
|
||||
|
||||
testFormatting(for: input, rule: FormatRules.wrapArguments, options: options)
|
||||
}
|
||||
|
||||
func testWrapEffectOnMultilineFunctionDeclaration() {
|
||||
let input = """
|
||||
func multilineFunction(
|
||||
@@ -3630,6 +3705,35 @@ class WrappingTests: RulesTests {
|
||||
], options: options)
|
||||
}
|
||||
|
||||
func testWrapsMultilineStatementConsistently2_withTypedEffects() {
|
||||
let input = """
|
||||
func aFunc(
|
||||
one _: Int,
|
||||
two _: Int) async throws(Foo) -> String {
|
||||
"one"
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
func aFunc(
|
||||
one _: Int,
|
||||
two _: Int
|
||||
) async throws(Foo) -> String {
|
||||
"one"
|
||||
}
|
||||
"""
|
||||
|
||||
let options = FormatOptions(
|
||||
wrapArguments: .beforeFirst,
|
||||
closingParenOnSameLine: false,
|
||||
wrapEffects: .never
|
||||
)
|
||||
testFormatting(for: input, [output], rules: [
|
||||
FormatRules.wrapMultilineStatementBraces,
|
||||
FormatRules.wrapArguments,
|
||||
], options: options)
|
||||
}
|
||||
|
||||
func testWrapsMultilineStatementConsistently3() {
|
||||
let input = """
|
||||
func aFunc(
|
||||
|
||||
Reference in New Issue
Block a user