diff --git a/Sources/SwiftBasicFormat/BasicFormat.swift b/Sources/SwiftBasicFormat/BasicFormat.swift index d8f872e86b9..2e50a4e9c82 100644 --- a/Sources/SwiftBasicFormat/BasicFormat.swift +++ b/Sources/SwiftBasicFormat/BasicFormat.swift @@ -310,27 +310,18 @@ open class BasicFormat: SyntaxRewriter { (.backtick, _), (.dollarIdentifier, .period), // a.b (.endOfFile, _), - (.exclamationMark, .leftParen), // myOptionalClosure!() (.exclamationMark, .period), // myOptionalBar!.foo() - (.regexPoundDelimiter, .leftParen), // opening extended regex delimiter should never be separate by a space (.regexPoundDelimiter, .regexSlash), // opening extended regex delimiter should never be separate by a space (.identifier, .leftAngle), // MyType - (.identifier, .leftParen), // foo() (.identifier, .leftSquare), // myArray[1] (.identifier, .period), // a.b (.integerLiteral, .period), // macOS 11.2.1 (.keyword(.Any), .period), // Any.Type (.keyword(.`init`), .leftAngle), // init() - (.keyword(.`init`), .leftParen), // init() - (.keyword(.private), .leftParen), // private(set) (.keyword(.self), .period), // self.someProperty - (.keyword(.self), .leftParen), // self() (.keyword(.self), .leftSquare), // self[] (.keyword(.Self), .period), // Self.someProperty - (.keyword(.Self), .leftParen), // Self() (.keyword(.Self), .leftSquare), // Self[] - (.keyword(.set), .leftParen), // var mVar: Int { set(value) {} } - (.keyword(.subscript), .leftParen), // subscript(x: Int) (.keyword(.super), .period), // super.someProperty (.leftBrace, .rightBrace), // {} (.leftParen, _), @@ -339,13 +330,10 @@ open class BasicFormat: SyntaxRewriter { (.multilineStringQuote, .rawStringPoundDelimiter), (.period, _), (.postfixQuestionMark, .leftAngle), // init?() - (.postfixQuestionMark, .leftParen), // init?() or myOptionalClosure?() (.postfixQuestionMark, .period), // someOptional?.someProperty (.pound, _), - (.poundUnavailable, .leftParen), // #unavailable(...) (.prefixAmpersand, _), (.prefixOperator, _), - (.rawStringPoundDelimiter, .leftParen), // opening raw string delimiter should never be separate by a space // opening raw string delimiter should never be separate by a space (.rawStringPoundDelimiter, .multilineStringQuote), (.rawStringPoundDelimiter, .singleQuote), // opening raw string delimiter should never be separate by a space @@ -355,10 +343,7 @@ open class BasicFormat: SyntaxRewriter { (.regexPoundDelimiter, .period), // #/myRegex/#.someMember (.regexSlash, .regexPoundDelimiter), // closing extended regex delimiter should never be separate by a space (.regexSlash, .period), // /myRegex/.someMember - (.rightAngle, .leftParen), // func foo(x: T) (.rightAngle, .period), // Foo.bar - (.rightBrace, .leftParen), // { return 1 }() - (.rightParen, .leftParen), // returnsClosure()() (.rightParen, .period), // foo().bar (.rightSquare, .period), // myArray[1].someProperty (.singleQuote, .rawStringPoundDelimiter), // closing raw string delimiter should never be separate by a space @@ -378,9 +363,11 @@ open class BasicFormat: SyntaxRewriter { (nil, _): return false case (_, .colon): - if second?.keyPathInParent != \TernaryExprSyntax.colon - && second?.keyPathInParent != \UnresolvedTernaryExprSyntax.colon - { + switch second?.keyPathInParent { + case \TernaryExprSyntax.colon, + \UnresolvedTernaryExprSyntax.colon: + break + default: return false } case (.leftAngle, _) where second?.tokenKind != .rightAngle: @@ -389,6 +376,18 @@ open class BasicFormat: SyntaxRewriter { case (_, .rightAngle) where first?.tokenKind != .leftAngle: // `<` and `>` need to be separated by a space because otherwise they become an operator return false + case (_, .leftParen): + switch second?.keyPathInParent { + case \ClosureParameterClauseSyntax.leftParen, + \FunctionTypeSyntax.leftParen, + \TupleExprSyntax.leftParen, + \TuplePatternSyntax.leftParen, + \TupleTypeSyntax.leftParen: + break + default: + return false + } + default: break } diff --git a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift index 39ea7f1f8e2..801c5e5e7ff 100644 --- a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift +++ b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift @@ -43,6 +43,15 @@ fileprivate func assertFormatted( ) } +fileprivate func assertFormattingRoundTrips( + _ source: String, + using format: BasicFormat = BasicFormat(indentationWidth: .spaces(4)), + file: StaticString = #filePath, + line: UInt = #line +) { + assertFormatted(source: source, expected: source, using: format, file: file, line: line) +} + final class BasicFormatTest: XCTestCase { func testNotIndented() { assertFormatted( @@ -100,26 +109,26 @@ final class BasicFormatTest: XCTestCase { } func testAlreadyIndented() { - let source = """ + assertFormattingRoundTrips( + """ func foo() { someFunc(a: 1, b: 1) } """ - - assertFormatted(source: source, expected: source) + ) } func testAlreadyIndentedWithComment() { - let source = """ + assertFormattingRoundTrips( + """ func foo() { // ABC someFunc(a: 1, b: 1) } """ - - assertFormatted(source: source, expected: source) + ) } func testAlreadyIndentedWithComment2() { @@ -278,7 +287,8 @@ final class BasicFormatTest: XCTestCase { } func testMultilineStringLiteralWithBlankLines() { - let source = #""" + assertFormattingRoundTrips( + #""" assertionFailure(""" First line @@ -287,20 +297,22 @@ final class BasicFormatTest: XCTestCase { """) """# - assertFormatted(source: source, expected: source) + ) } func testMultilineStringLiteralWithFirstLineBlank() { - let source = #""" + assertFormattingRoundTrips( + #""" assertionFailure(""" """) """# - assertFormatted(source: source, expected: source) + ) } func testNestedMultilineStringLiterals() { - let source = #""" + assertFormattingRoundTrips( + #""" assertionFailure(""" \(""" @@ -308,8 +320,7 @@ final class BasicFormatTest: XCTestCase { """) """) """# - - assertFormatted(source: source, expected: source) + ) } func testIndentNestedMultilineStringLiterals() throws { @@ -375,7 +386,8 @@ final class BasicFormatTest: XCTestCase { } func testClosureInStringInterpolation() { - let source = #""" + assertFormattingRoundTrips( + #""" """ \(gen { (x) in return """ @@ -384,8 +396,7 @@ final class BasicFormatTest: XCTestCase { }) """ """# - - assertFormatted(source: source, expected: source) + ) } func testNestedUserDefinedIndentation() { @@ -540,31 +551,27 @@ final class BasicFormatTest: XCTestCase { } func testPeriodAfterStringLiteral() { - let source = """ + assertFormattingRoundTrips( + """ "test".lowercased() """ - assertFormatted(source: source, expected: source) + ) } func testPeriodAfterRawStringLiteral() { - let source = """ + assertFormattingRoundTrips( + """ #"test"#.lowercased() """ - assertFormatted(source: source, expected: source) + ) } func testPeriodAfterRegexLiteral() { - let source = """ - /test/.something - """ - assertFormatted(source: source, expected: source) + assertFormattingRoundTrips("/test/.something") } func testPeriodAfterRawRegexLiteral() { - let source = """ - /test/.something - """ - assertFormatted(source: source, expected: source) + assertFormattingRoundTrips("/test/.something") } func testNewlineInTrailingTriviaAtEndOfIndentationScope() throws { @@ -627,30 +634,65 @@ final class BasicFormatTest: XCTestCase { } func testIndentedStandaloneClosureRoundTrips() throws { - let source = """ + assertFormattingRoundTrips( + """ foo { "abc" } """ - assertFormatted(source: source, expected: source) + ) } func testIndentedStandaloneClosureRoundTrips2() throws { - let source = """ + assertFormattingRoundTrips( + """ foo { if true { print("test") } } """ - assertFormatted(source: source, expected: source) + ) } func testPrivateSetVar() { - let source = """ - private(set) var x = 1 + assertFormattingRoundTrips("private(set) var x = 1") + + assertFormattingRoundTrips("internal(set) var x = 1") + } + + func testSpiAttribute() { + assertFormattingRoundTrips( + """ + @_spi(MySPI) struct Foo { + } """ + ) + } - assertFormatted(source: source, expected: source) + func testTypedThrows() { + assertFormattingRoundTrips( + """ + func foo() throws(MyError) { + } + """ + ) + } + + func testClosureParameterClause() { + assertFormatted( + source: "{(x: Int) in}", + expected: """ + { (x: Int) in + } + """ + ) + } + + func testFunctionType() { + assertFormatted( + source: "let x:(Int)->Void", + expected: "let x: (Int) -> Void" + ) } } diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index 63bf01fc3c3..496b65264a0 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -469,7 +469,7 @@ final class TypeTests: ParserTestCase { assertParse("func foo() -> dependsOn(scoped x) X", experimentalFeatures: [.nonescapableTypes]) assertParse( - "func foo() -> dependsOn 1️⃣X", + "func foo() -> dependsOn1️⃣ X", diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", @@ -477,7 +477,7 @@ final class TypeTests: ParserTestCase { fixIts: ["insert '(', parameter reference, and ')'"] ) ], - fixedSource: "func foo() -> dependsOn (<#identifier#>) X", + fixedSource: "func foo() -> dependsOn(<#identifier#>) X", experimentalFeatures: [.nonescapableTypes] ) diff --git a/Tests/SwiftParserTest/translated/AvailabilityQueryTests.swift b/Tests/SwiftParserTest/translated/AvailabilityQueryTests.swift index 051a6ad7521..4425e329fd5 100644 --- a/Tests/SwiftParserTest/translated/AvailabilityQueryTests.swift +++ b/Tests/SwiftParserTest/translated/AvailabilityQueryTests.swift @@ -124,7 +124,7 @@ final class AvailabilityQueryTests: ParserTestCase { func testAvailabilityQuery7() { assertParse( """ - if #available 1️⃣{ + if #available1️⃣ { } """, diagnostics: [ @@ -135,7 +135,7 @@ final class AvailabilityQueryTests: ParserTestCase { ) ], fixedSource: """ - if #available (<#identifier#>) { + if #available(<#identifier#>) { } """ )