diff --git a/Sources/SwiftFormat/CMakeLists.txt b/Sources/SwiftFormat/CMakeLists.txt index 9306c49f3..a846b8c59 100644 --- a/Sources/SwiftFormat/CMakeLists.txt +++ b/Sources/SwiftFormat/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(SwiftFormat Core/SyntaxFormatRule.swift Core/SyntaxLintRule.swift Core/SyntaxProtocol+Convenience.swift + Core/SyntaxTraits.swift Core/Trivia+Convenience.swift Core/WithAttributesSyntax+Convenience.swift Core/WithSemicolonSyntax.swift diff --git a/Sources/SwiftFormat/Core/SyntaxTraits.swift b/Sources/SwiftFormat/Core/SyntaxTraits.swift new file mode 100644 index 000000000..40f147936 --- /dev/null +++ b/Sources/SwiftFormat/Core/SyntaxTraits.swift @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// Common protocol implemented by expression syntax types that support calling another expression. +protocol CallingExprSyntaxProtocol: ExprSyntaxProtocol { + var calledExpression: ExprSyntax { get } +} + +extension FunctionCallExprSyntax: CallingExprSyntaxProtocol {} +extension SubscriptCallExprSyntax: CallingExprSyntaxProtocol {} + +extension Syntax { + func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { + return self.asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol + } + func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { + return self.asProtocol(CallingExprSyntaxProtocol.self) != nil + } +} + +extension ExprSyntax { + func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { + return Syntax(self).asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol + } + func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { + return self.asProtocol(CallingExprSyntaxProtocol.self) != nil + } +} + +/// Common protocol implemented by expression syntax types that are expressed as a modified +/// subexpression of the form ` `. +protocol KeywordModifiedExprSyntaxProtocol: ExprSyntaxProtocol { + var expression: ExprSyntax { get } +} + +extension AwaitExprSyntax: KeywordModifiedExprSyntaxProtocol {} +extension TryExprSyntax: KeywordModifiedExprSyntaxProtocol {} +extension UnsafeExprSyntax: KeywordModifiedExprSyntaxProtocol {} + +extension Syntax { + func asProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> KeywordModifiedExprSyntaxProtocol? { + return self.asProtocol(SyntaxProtocol.self) as? KeywordModifiedExprSyntaxProtocol + } + func isProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> Bool { + return self.asProtocol(KeywordModifiedExprSyntaxProtocol.self) != nil + } +} + +extension ExprSyntax { + func asProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> KeywordModifiedExprSyntaxProtocol? { + return Syntax(self).asProtocol(SyntaxProtocol.self) as? KeywordModifiedExprSyntaxProtocol + } + func isProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> Bool { + return self.asProtocol(KeywordModifiedExprSyntaxProtocol.self) != nil + } +} diff --git a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift index 03da7d4b4..5eb023fba 100644 --- a/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift @@ -1068,14 +1068,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if let calledMemberAccessExpr = node.calledExpression.as(MemberAccessExprSyntax.self) { if let base = calledMemberAccessExpr.base, base.is(DeclReferenceExprSyntax.self) { - // When this function call is wrapped by a try-expr or await-expr, the group applied when - // visiting that wrapping expression is sufficient. Adding another group here in that case - // can result in unnecessarily breaking after the try/await keyword. - if !(base.firstToken(viewMode: .sourceAccurate)?.previousToken(viewMode: .all)?.parent?.is(TryExprSyntax.self) - ?? false - || base.firstToken(viewMode: .sourceAccurate)?.previousToken(viewMode: .all)?.parent?.is(AwaitExprSyntax.self) - ?? false) - { + // When this function call is wrapped by a keyword-modified expression, the group applied + // when visiting that wrapping expression is sufficient. Adding another group here in that + // case can result in unnecessarily breaking after the modifier keyword. + if !(base.firstToken(viewMode: .sourceAccurate)?.previousToken(viewMode: .all)?.parent?.isProtocol( + KeywordModifiedExprSyntaxProtocol.self + ) ?? false) { before(base.firstToken(viewMode: .sourceAccurate), tokens: .open) after(calledMemberAccessExpr.declName.baseName.lastToken(viewMode: .sourceAccurate), tokens: .close) } @@ -1780,8 +1778,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { tokens: .break(.continue, newlines: .elective(ignoresDiscretionary: true)) ) - // Check for an anchor token inside of the expression to group with the try keyword. - if let anchorToken = findTryAwaitExprConnectingToken(inExpr: node.expression) { + // Check for an anchor token inside of the expression to end a group starting with the `try` + // keyword. + if !(node.parent?.isProtocol(KeywordModifiedExprSyntaxProtocol.self) ?? false), + let anchorToken = connectingTokenForKeywordModifiedExpr(inSubExpr: node.expression) + { before(node.tryKeyword, tokens: .open) after(anchorToken, tokens: .close) } @@ -1795,9 +1796,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { tokens: .break(.continue, newlines: .elective(ignoresDiscretionary: true)) ) - // Check for an anchor token inside of the expression to group with the await keyword. - if !(node.parent?.is(TryExprSyntax.self) ?? false), - let anchorToken = findTryAwaitExprConnectingToken(inExpr: node.expression) + // Check for an anchor token inside of the expression to end a group starting with the `await` + // keyword. + if !(node.parent?.isProtocol(KeywordModifiedExprSyntaxProtocol.self) ?? false), + let anchorToken = connectingTokenForKeywordModifiedExpr(inSubExpr: node.expression) { before(node.awaitKeyword, tokens: .open) after(anchorToken, tokens: .close) @@ -1806,18 +1808,37 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { return .visitChildren } - /// Searches the AST from `expr` to find a token that should be grouped with an enclosing - /// try-expr or await-expr. Returns that token, or nil when no such token is found. + override func visit(_ node: UnsafeExprSyntax) -> SyntaxVisitorContinueKind { + // Unlike `try` and `await`, `unsafe` is a contextual keyword that may not be separated from + // the following token by a line break. Keep them glued together with `.space`. + before(node.expression.firstToken(viewMode: .sourceAccurate), tokens: .space) + + // Check for an anchor token inside of the expression to end a group starting with the `unsafe` + // keyword. + if !(node.parent?.isProtocol(KeywordModifiedExprSyntaxProtocol.self) ?? false), + let anchorToken = connectingTokenForKeywordModifiedExpr(inSubExpr: node.expression) + { + before(node.unsafeKeyword, tokens: .open) + after(anchorToken, tokens: .close) + } + + return .visitChildren + } + + /// Searches within a subexpression of a keyword-modified expression to find the last token in a + /// range that should be grouped with the leading keyword modifier. /// - /// - Parameter expr: An expression that is wrapped by a try-expr or await-expr. - /// - Returns: A token that should be grouped with the try-expr or await-expr, or nil. - func findTryAwaitExprConnectingToken(inExpr expr: ExprSyntax) -> TokenSyntax? { - if let awaitExpr = expr.as(AwaitExprSyntax.self) { - // If we were called from the `try` of a `try await `, drill into the child expression. - return findTryAwaitExprConnectingToken(inExpr: awaitExpr.expression) + /// - Parameter expr: An expression that is wrapped by a keyword-modified expression. + /// - Returns: The token that should end the group that is started by the modifier keyword, or + /// nil if there should be no group. + func connectingTokenForKeywordModifiedExpr(inSubExpr expr: ExprSyntax) -> TokenSyntax? { + if let modifiedExpr = expr.asProtocol(KeywordModifiedExprSyntaxProtocol.self) { + // If we were called from a keyword-modified expression like `try`, `await`, or `unsafe`, + // recursively drill into the child expression. + return connectingTokenForKeywordModifiedExpr(inSubExpr: modifiedExpr.expression) } if let callingExpr = expr.asProtocol(CallingExprSyntaxProtocol.self) { - return findTryAwaitExprConnectingToken(inExpr: callingExpr.calledExpression) + return connectingTokenForKeywordModifiedExpr(inSubExpr: callingExpr.calledExpression) } if let memberAccessExpr = expr.as(MemberAccessExprSyntax.self), let base = memberAccessExpr.base { // When there's a simple base (i.e. identifier), group the entire `try/await .` @@ -1826,7 +1847,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if base.is(DeclReferenceExprSyntax.self) { return memberAccessExpr.declName.baseName.lastToken(viewMode: .sourceAccurate) } - return findTryAwaitExprConnectingToken(inExpr: base) + return connectingTokenForKeywordModifiedExpr(inSubExpr: base) } if expr.is(DeclReferenceExprSyntax.self) { return expr.lastToken(viewMode: .sourceAccurate) @@ -3825,13 +3846,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { /// that are known to wrap an expression, e.g. try expressions, are handled by checking the /// expression that they contain. private func isCompoundExpression(_ expr: ExprSyntax) -> Bool { + if let modifiedExpr = expr.asProtocol(KeywordModifiedExprSyntaxProtocol.self) { + return isCompoundExpression(modifiedExpr.expression) + } switch Syntax(expr).as(SyntaxEnum.self) { - case .awaitExpr(let awaitExpr): - return isCompoundExpression(awaitExpr.expression) case .infixOperatorExpr, .ternaryExpr, .isExpr, .asExpr: return true - case .tryExpr(let tryExpr): - return isCompoundExpression(tryExpr.expression) case .tupleExpr(let tupleExpr) where tupleExpr.elements.count == 1: return isCompoundExpression(tupleExpr.elements.first!.expression) default: @@ -4505,29 +4525,3 @@ extension NewlineBehavior { } } } - -/// Common protocol implemented by expression syntax types that support calling another expression. -protocol CallingExprSyntaxProtocol: ExprSyntaxProtocol { - var calledExpression: ExprSyntax { get } -} - -extension FunctionCallExprSyntax: CallingExprSyntaxProtocol {} -extension SubscriptCallExprSyntax: CallingExprSyntaxProtocol {} - -extension Syntax { - func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { - return self.asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol - } - func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { - return self.asProtocol(CallingExprSyntaxProtocol.self) != nil - } -} - -extension ExprSyntax { - func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { - return Syntax(self).asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol - } - func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { - return self.asProtocol(CallingExprSyntaxProtocol.self) != nil - } -} diff --git a/Tests/SwiftFormatTests/PrettyPrint/DoStmtTests.swift b/Tests/SwiftFormatTests/PrettyPrint/DoStmtTests.swift index 46b775f5a..9d013c559 100644 --- a/Tests/SwiftFormatTests/PrettyPrint/DoStmtTests.swift +++ b/Tests/SwiftFormatTests/PrettyPrint/DoStmtTests.swift @@ -43,6 +43,19 @@ final class DoStmtTests: PrettyPrintTestCase { assertPrettyPrintEqual(input: input, expected: expected, linelength: 25) } + func testNestedDo() { + // Avoid regressions in the case where a nested `do` block was getting shifted all the way left. + let input = """ + func foo() { + do { + bar() + baz() + } + } + """ + assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 45) + } + func testLabeledDoStmt() { let input = """ someLabel:do { @@ -102,4 +115,203 @@ final class DoStmtTests: PrettyPrintTestCase { linelength: 25 ) } + + func testDoCatch_noBreakBeforeCatch() { + let input = + """ + do { try thisFuncMightFail() } catch error1 { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2 { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2(let someVar) { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } + catch error2(let someVar) { + print(someVar) + print("Don't do it!") + } + do { try thisFuncMightFail() } catch is ABadError{ print("Nope") } + """ + + let expected = + """ + do { + try thisFuncMightFail() + } catch error1 { print("Nope") } + do { + try thisFuncMightFail() + } catch error1 { + print("Nope") + } catch error2 { print("Nope") } + do { + try thisFuncMightFail() + } catch error1 { + print("Nope") + } catch error2(let someVar) { + print("Nope") + } + do { + try thisFuncMightFail() + } catch error1 { + print("Nope") + } catch error2(let someVar) { + print(someVar) + print("Don't do it!") + } + do { + try thisFuncMightFail() + } catch is ABadError { print("Nope") } + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) + } + + func testDoCatch_breakBeforeCatch() { + let input = + """ + do { try thisFuncMightFail() } catch error1 { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2 { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2(let someVar) { print("Nope") } + do { try thisFuncMightFail() } catch error1 { print("Nope") } + catch error2(let someVar) { + print(someVar) + print("Don't do it!") + } + do { try thisFuncMightFail() } catch is ABadError{ print("Nope") } + """ + + let expected = + """ + do { try thisFuncMightFail() } + catch error1 { print("Nope") } + do { try thisFuncMightFail() } + catch error1 { print("Nope") } + catch error2 { print("Nope") } + do { try thisFuncMightFail() } + catch error1 { print("Nope") } + catch error2(let someVar) { + print("Nope") + } + do { try thisFuncMightFail() } + catch error1 { print("Nope") } + catch error2(let someVar) { + print(someVar) + print("Don't do it!") + } + do { try thisFuncMightFail() } + catch is ABadError { print("Nope") } + + """ + + var config = Configuration.forTesting + config.lineBreakBeforeControlFlowKeywords = true + assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: config) + } + + func testCatchWhere_noBreakBeforeCatch() { + let input = + """ + do { try thisFuncMightFail() } catch error1 where error1 is ErrorType { print("Nope") } + do { try thisFuncMightFail() } catch error1 where error1 is LongerErrorType { print("Nope") } + """ + + let expected = + """ + do { + try thisFuncMightFail() + } catch error1 where error1 is ErrorType { + print("Nope") + } + do { + try thisFuncMightFail() + } catch error1 + where error1 is LongerErrorType + { print("Nope") } + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 42) + } + + func testCatchWhere_breakBeforeCatch() { + let input = + """ + do { try thisFuncMightFail() } catch error1 where error1 is ErrorType { print("Nope") } + do { try thisFuncMightFail() } catch error1 where error1 is LongerErrorType { print("Nope") } + """ + + let expected = + """ + do { try thisFuncMightFail() } + catch error1 where error1 is ErrorType { + print("Nope") + } + do { try thisFuncMightFail() } + catch error1 + where error1 is LongerErrorType { + print("Nope") + } + + """ + + var config = Configuration.forTesting + config.lineBreakBeforeControlFlowKeywords = true + assertPrettyPrintEqual(input: input, expected: expected, linelength: 42, configuration: config) + } + + func testMultipleCatchItems() { + let input = + """ + do { try thisMightFail() } catch error1, error2 { print("Nope") } + do { try thisMightFail() } catch longErrorType, error2 { print("Nope") } + do { try thisMightFail() } catch longErrorTypeName, longErrorType2(let someLongVariable) { print("Nope") } + do { try thisMightFail() } catch longErrorTypeName, longErrorType2 as SomeLongErrorType { print("Nope") } + do { try thisMightFail() } catch longErrorName where someCondition, longErrorType2 { print("Nope") } + do { try thisMightFail() } catch longErrorTypeName, longErrorType2 as SomeLongErrorType where someCondition, longErrorType3 { print("Nope") } + """ + + let expected = + """ + do { + try thisMightFail() + } catch error1, error2 { + print("Nope") + } + do { + try thisMightFail() + } catch longErrorType, + error2 + { print("Nope") } + do { + try thisMightFail() + } catch + longErrorTypeName, + longErrorType2( + let someLongVariable) + { print("Nope") } + do { + try thisMightFail() + } catch + longErrorTypeName, + longErrorType2 + as SomeLongErrorType + { print("Nope") } + do { + try thisMightFail() + } catch longErrorName + where someCondition, + longErrorType2 + { print("Nope") } + do { + try thisMightFail() + } catch + longErrorTypeName, + longErrorType2 + as SomeLongErrorType + where someCondition, + longErrorType3 + { print("Nope") } + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 25) + } } diff --git a/Tests/SwiftFormatTests/PrettyPrint/AwaitExprTests.swift b/Tests/SwiftFormatTests/PrettyPrint/ExpressionModifierTests.swift similarity index 51% rename from Tests/SwiftFormatTests/PrettyPrint/AwaitExprTests.swift rename to Tests/SwiftFormatTests/PrettyPrint/ExpressionModifierTests.swift index bb95befb4..3f1e3b1a6 100644 --- a/Tests/SwiftFormatTests/PrettyPrint/AwaitExprTests.swift +++ b/Tests/SwiftFormatTests/PrettyPrint/ExpressionModifierTests.swift @@ -12,7 +12,80 @@ import SwiftFormat -final class AwaitExprTests: PrettyPrintTestCase { +/// These tests verify the breaking and grouping behavior of expression modifiers like `try`, +/// `await`, and `unsafe`. +final class ExpressionModifierTests: PrettyPrintTestCase { + func testBasicTries() { + let input = + """ + let a = try possiblyFailingFunc() + let a = try? possiblyFailingFunc() + let a = try! possiblyFailingFunc() + """ + + let expected = + """ + let a = try possiblyFailingFunc() + let a = try? possiblyFailingFunc() + let a = try! possiblyFailingFunc() + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) + } + + func testTryKeywordBreaking() { + let input = + """ + let aVeryLongArgumentName = try foo.bar() + let aVeryLongArgumentName = try + foo.bar() + let aVeryLongArgumentName = try? foo.bar() + let abc = try foo.baz().quxxe(a, b, c).bar() + let abc = try foo + .baz().quxxe(a, b, c).bar() + let abc = try [1, 2, 3, 4, 5, 6, 7].baz().quxxe(a, b, c).bar() + let abc = try [1, 2, 3, 4, 5, 6, 7] + .baz().quxxe(a, b, c).bar() + let abc = try foo.baz().quxxe(a, b, c).bar[0] + let abc = try foo + .baz().quxxe(a, b, c).bar[0] + let abc = try + foo + .baz().quxxe(a, b, c).bar[0] + """ + + let expected = + """ + let aVeryLongArgumentName = + try foo.bar() + let aVeryLongArgumentName = + try foo.bar() + let aVeryLongArgumentName = + try? foo.bar() + let abc = try foo.baz().quxxe(a, b, c) + .bar() + let abc = + try foo + .baz().quxxe(a, b, c).bar() + let abc = try [1, 2, 3, 4, 5, 6, 7] + .baz().quxxe(a, b, c).bar() + let abc = try [1, 2, 3, 4, 5, 6, 7] + .baz().quxxe(a, b, c).bar() + let abc = try foo.baz().quxxe(a, b, c) + .bar[0] + let abc = + try foo + .baz().quxxe(a, b, c).bar[0] + let abc = + try foo + .baz().quxxe(a, b, c).bar[0] + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) + } + func testBasicAwaits() { let input = """ @@ -136,4 +209,80 @@ final class AwaitExprTests: PrettyPrintTestCase { assertPrettyPrintEqual(input: input, expected: expected, linelength: 46) } + + func testBasicUnsafe() { + // NOTE: Even though the third line will be overlong, it cannot be broken after `unsafe` + // because the result would fail to parse. + let input = + """ + let a = unsafe unsafeFunction() + let b = unsafe longerUnsafeFunction() + let c = unsafe evenLongerAndLongerUnsafeFunction() + """ + + let expected = + """ + let a = unsafe unsafeFunction() + let b = + unsafe longerUnsafeFunction() + let c = + unsafe evenLongerAndLongerUnsafeFunction() + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 32) + } + + func testMultipleModifierBreaking() { + assertPrettyPrintEqual( + input: """ + let a = unsafe try await + modifiedFunction() + let b = unsafe try await + longerModifiedFunction() + let c = unsafe try await + evenLongerAndLongerModifiedFunction(arg: 1) + """, + expected: """ + let a = + unsafe try await modifiedFunction() + let b = + unsafe try await + longerModifiedFunction() + let c = + unsafe try await + evenLongerAndLongerModifiedFunction( + arg: 1) + + """, + linelength: 35 + ) + + assertPrettyPrintEqual( + input: """ + let a = unsafe try await smol() + """, + expected: """ + let a = + unsafe try + await smol() + + """, + linelength: 13 + ) + + assertPrettyPrintEqual( + input: """ + let a = unsafe try await smol() + """, + expected: """ + let a = + unsafe try + await + smol() + + """, + linelength: 10 + ) + } } diff --git a/Tests/SwiftFormatTests/PrettyPrint/TryCatchTests.swift b/Tests/SwiftFormatTests/PrettyPrint/TryCatchTests.swift deleted file mode 100644 index 71073e233..000000000 --- a/Tests/SwiftFormatTests/PrettyPrint/TryCatchTests.swift +++ /dev/null @@ -1,298 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftFormat - -final class TryCatchTests: PrettyPrintTestCase { - func testBasicTries() { - let input = - """ - let a = try possiblyFailingFunc() - let a = try? possiblyFailingFunc() - let a = try! possiblyFailingFunc() - """ - - let expected = - """ - let a = try possiblyFailingFunc() - let a = try? possiblyFailingFunc() - let a = try! possiblyFailingFunc() - - """ - - assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) - } - - func testDoTryCatch_noBreakBeforeCatch() { - let input = - """ - do { try thisFuncMightFail() } catch error1 { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2 { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2(let someVar) { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } - catch error2(let someVar) { - print(someVar) - print("Don't do it!") - } - do { try thisFuncMightFail() } catch is ABadError{ print("Nope") } - """ - - let expected = - """ - do { - try thisFuncMightFail() - } catch error1 { print("Nope") } - do { - try thisFuncMightFail() - } catch error1 { - print("Nope") - } catch error2 { print("Nope") } - do { - try thisFuncMightFail() - } catch error1 { - print("Nope") - } catch error2(let someVar) { - print("Nope") - } - do { - try thisFuncMightFail() - } catch error1 { - print("Nope") - } catch error2(let someVar) { - print(someVar) - print("Don't do it!") - } - do { - try thisFuncMightFail() - } catch is ABadError { print("Nope") } - - """ - - assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) - } - - func testDoTryCatch_breakBeforeCatch() { - let input = - """ - do { try thisFuncMightFail() } catch error1 { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2 { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } catch error2(let someVar) { print("Nope") } - do { try thisFuncMightFail() } catch error1 { print("Nope") } - catch error2(let someVar) { - print(someVar) - print("Don't do it!") - } - do { try thisFuncMightFail() } catch is ABadError{ print("Nope") } - """ - - let expected = - """ - do { try thisFuncMightFail() } - catch error1 { print("Nope") } - do { try thisFuncMightFail() } - catch error1 { print("Nope") } - catch error2 { print("Nope") } - do { try thisFuncMightFail() } - catch error1 { print("Nope") } - catch error2(let someVar) { - print("Nope") - } - do { try thisFuncMightFail() } - catch error1 { print("Nope") } - catch error2(let someVar) { - print(someVar) - print("Don't do it!") - } - do { try thisFuncMightFail() } - catch is ABadError { print("Nope") } - - """ - - var config = Configuration.forTesting - config.lineBreakBeforeControlFlowKeywords = true - assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: config) - } - - func testCatchWhere_noBreakBeforeCatch() { - let input = - """ - do { try thisFuncMightFail() } catch error1 where error1 is ErrorType { print("Nope") } - do { try thisFuncMightFail() } catch error1 where error1 is LongerErrorType { print("Nope") } - """ - - let expected = - """ - do { - try thisFuncMightFail() - } catch error1 where error1 is ErrorType { - print("Nope") - } - do { - try thisFuncMightFail() - } catch error1 - where error1 is LongerErrorType - { print("Nope") } - - """ - - assertPrettyPrintEqual(input: input, expected: expected, linelength: 42) - } - - func testCatchWhere_breakBeforeCatch() { - let input = - """ - do { try thisFuncMightFail() } catch error1 where error1 is ErrorType { print("Nope") } - do { try thisFuncMightFail() } catch error1 where error1 is LongerErrorType { print("Nope") } - """ - - let expected = - """ - do { try thisFuncMightFail() } - catch error1 where error1 is ErrorType { - print("Nope") - } - do { try thisFuncMightFail() } - catch error1 - where error1 is LongerErrorType { - print("Nope") - } - - """ - - var config = Configuration.forTesting - config.lineBreakBeforeControlFlowKeywords = true - assertPrettyPrintEqual(input: input, expected: expected, linelength: 42, configuration: config) - } - - func testNestedDo() { - // Avoid regressions in the case where a nested `do` block was getting shifted all the way left. - let input = """ - func foo() { - do { - bar() - baz() - } - } - """ - assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 45) - } - - func testMultipleCatchItems() { - let input = - """ - do { try thisMightFail() } catch error1, error2 { print("Nope") } - do { try thisMightFail() } catch longErrorType, error2 { print("Nope") } - do { try thisMightFail() } catch longErrorTypeName, longErrorType2(let someLongVariable) { print("Nope") } - do { try thisMightFail() } catch longErrorTypeName, longErrorType2 as SomeLongErrorType { print("Nope") } - do { try thisMightFail() } catch longErrorName where someCondition, longErrorType2 { print("Nope") } - do { try thisMightFail() } catch longErrorTypeName, longErrorType2 as SomeLongErrorType where someCondition, longErrorType3 { print("Nope") } - """ - - let expected = - """ - do { - try thisMightFail() - } catch error1, error2 { - print("Nope") - } - do { - try thisMightFail() - } catch longErrorType, - error2 - { print("Nope") } - do { - try thisMightFail() - } catch - longErrorTypeName, - longErrorType2( - let someLongVariable) - { print("Nope") } - do { - try thisMightFail() - } catch - longErrorTypeName, - longErrorType2 - as SomeLongErrorType - { print("Nope") } - do { - try thisMightFail() - } catch longErrorName - where someCondition, - longErrorType2 - { print("Nope") } - do { - try thisMightFail() - } catch - longErrorTypeName, - longErrorType2 - as SomeLongErrorType - where someCondition, - longErrorType3 - { print("Nope") } - - """ - - assertPrettyPrintEqual(input: input, expected: expected, linelength: 25) - } - - func testTryKeywordBreaking() { - let input = - """ - let aVeryLongArgumentName = try foo.bar() - let aVeryLongArgumentName = try - foo.bar() - let aVeryLongArgumentName = try? foo.bar() - let abc = try foo.baz().quxxe(a, b, c).bar() - let abc = try foo - .baz().quxxe(a, b, c).bar() - let abc = try [1, 2, 3, 4, 5, 6, 7].baz().quxxe(a, b, c).bar() - let abc = try [1, 2, 3, 4, 5, 6, 7] - .baz().quxxe(a, b, c).bar() - let abc = try foo.baz().quxxe(a, b, c).bar[0] - let abc = try foo - .baz().quxxe(a, b, c).bar[0] - let abc = try - foo - .baz().quxxe(a, b, c).bar[0] - """ - - let expected = - """ - let aVeryLongArgumentName = - try foo.bar() - let aVeryLongArgumentName = - try foo.bar() - let aVeryLongArgumentName = - try? foo.bar() - let abc = try foo.baz().quxxe(a, b, c) - .bar() - let abc = - try foo - .baz().quxxe(a, b, c).bar() - let abc = try [1, 2, 3, 4, 5, 6, 7] - .baz().quxxe(a, b, c).bar() - let abc = try [1, 2, 3, 4, 5, 6, 7] - .baz().quxxe(a, b, c).bar() - let abc = try foo.baz().quxxe(a, b, c) - .bar[0] - let abc = - try foo - .baz().quxxe(a, b, c).bar[0] - let abc = - try foo - .baz().quxxe(a, b, c).bar[0] - - """ - - assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) - } -}