Skip to content

Commit d3bea2a

Browse files
authored
Merge pull request swiftlang#106 from allevato/no-break-empty-parens
Don't break inside empty parens of a no-argument func/init decl.
2 parents 9c21818 + 8321151 commit d3bea2a

File tree

4 files changed

+69
-23
lines changed

4 files changed

+69
-23
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -246,18 +246,29 @@ private final class TokenStreamCreator: SyntaxVisitor {
246246
// MARK: - Function and function-like declaration nodes (initializers, deinitializers, subscripts)
247247

248248
func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
249-
// Prioritize keeping "<modifiers> func <name>" together.
250-
let firstTokenAfterAttributes = node.modifiers?.firstToken ?? node.funcKeyword
251-
before(firstTokenAfterAttributes, tokens: .open)
252-
after(node.funcKeyword, tokens: .break)
253-
after(node.identifier, tokens: .close)
249+
let hasArguments = !node.signature.input.parameterList.isEmpty
254250

255-
// Prioritize keeping ") throws -> <return_type>" together.
256-
if config.prioritizeKeepingFunctionOutputTogether {
251+
// Prioritize keeping ") throws -> <return_type>" together. We can only do this if the function
252+
// has arguments.
253+
if hasArguments && config.prioritizeKeepingFunctionOutputTogether {
257254
// Due to visitation order, the matching .open break is added in ParameterClauseSyntax.
258255
after(node.signature.lastToken, tokens: .close)
259256
}
260257

258+
let mustBreak = node.body != nil || node.signature.output != nil
259+
arrangeParameterClause(node.signature.input, forcesBreakBeforeRightParen: mustBreak)
260+
261+
// Prioritize keeping "<modifiers> func <name>(" together. Also include the ")" if the parameter
262+
// list is empty.
263+
let firstTokenAfterAttributes = node.modifiers?.firstToken ?? node.funcKeyword
264+
before(firstTokenAfterAttributes, tokens: .open)
265+
after(node.funcKeyword, tokens: .break)
266+
if hasArguments || node.genericParameterClause != nil {
267+
after(node.signature.input.leftParen, tokens: .close)
268+
} else {
269+
after(node.signature.input.rightParen, tokens: .close)
270+
}
271+
261272
// Add a non-breaking space after the identifier if it's an operator, to separate it visually
262273
// from the following parenthesis or generic argument list. Note that even if the function is
263274
// defining a prefix or postfix operator, or even if the operator isn't originally followed by a
@@ -266,9 +277,6 @@ private final class TokenStreamCreator: SyntaxVisitor {
266277
after(node.identifier.lastToken, tokens: .space)
267278
}
268279

269-
let mustBreak = node.body != nil || node.signature.output != nil
270-
arrangeParameterClause(node.signature.input, forcesBreakBeforeRightParen: mustBreak)
271-
272280
arrangeFunctionLikeDecl(
273281
node,
274282
attributes: node.attributes,
@@ -280,18 +288,22 @@ private final class TokenStreamCreator: SyntaxVisitor {
280288
}
281289

282290
func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
291+
let hasArguments = !node.parameters.parameterList.isEmpty
292+
293+
arrangeParameterClause(node.parameters, forcesBreakBeforeRightParen: node.body != nil)
294+
283295
// Prioritize keeping "<modifiers> init<punctuation>" together.
284296
let firstTokenAfterAttributes = node.modifiers?.firstToken ?? node.initKeyword
285-
let lastTokenOfName = node.optionalMark ?? node.initKeyword
286-
if firstTokenAfterAttributes != lastTokenOfName {
287-
before(firstTokenAfterAttributes, tokens: .open)
288-
after(lastTokenOfName, tokens: .close)
297+
before(firstTokenAfterAttributes, tokens: .open)
298+
299+
if hasArguments || node.genericParameterClause != nil {
300+
after(node.parameters.leftParen, tokens: .close)
301+
} else {
302+
after(node.parameters.rightParen, tokens: .close)
289303
}
290304

291305
before(node.throwsOrRethrowsKeyword, tokens: .break)
292306

293-
arrangeParameterClause(node.parameters, forcesBreakBeforeRightParen: node.body != nil)
294-
295307
arrangeFunctionLikeDecl(
296308
node,
297309
attributes: node.attributes,
@@ -313,16 +325,24 @@ private final class TokenStreamCreator: SyntaxVisitor {
313325
}
314326

315327
func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind {
328+
let hasArguments = !node.indices.parameterList.isEmpty
329+
316330
before(node.firstToken, tokens: .open)
317331

318332
// Prioritize keeping "<modifiers> subscript" together.
319333
if let firstModifierToken = node.modifiers?.firstToken {
320334
before(firstModifierToken, tokens: .open)
321-
after(node.subscriptKeyword, tokens: .close)
335+
336+
if hasArguments || node.genericParameterClause != nil {
337+
after(node.indices.leftParen, tokens: .close)
338+
} else {
339+
after(node.indices.rightParen, tokens: .close)
340+
}
322341
}
323342

324-
// Prioritize keeping ") -> <return_type>" together.
325-
if config.prioritizeKeepingFunctionOutputTogether {
343+
// Prioritize keeping ") -> <return_type>" together. We can only do this if the subscript has
344+
// arguments.
345+
if hasArguments && config.prioritizeKeepingFunctionOutputTogether {
326346
// Due to visitation order, the matching .open break is added in ParameterClauseSyntax.
327347
after(node.result.lastToken, tokens: .close)
328348
}
@@ -905,8 +925,9 @@ private final class TokenStreamCreator: SyntaxVisitor {
905925
}
906926

907927
func visit(_ node: ParameterClauseSyntax) -> SyntaxVisitorContinueKind {
908-
// Prioritize keeping ") throws -> <return_type>" together.
909-
if config.prioritizeKeepingFunctionOutputTogether {
928+
// Prioritize keeping ") throws -> <return_type>" together. We can only do this if the function
929+
// has arguments.
930+
if !node.parameterList.isEmpty && config.prioritizeKeepingFunctionOutputTogether {
910931
// Due to visitation order, this .open corresponds to a .close added in FunctionDeclSyntax
911932
// or SubscriptDeclSyntax.
912933
before(node.rightParen, tokens: .open)
@@ -2072,6 +2093,8 @@ private final class TokenStreamCreator: SyntaxVisitor {
20722093
private func arrangeParameterClause(
20732094
_ parameters: ParameterClauseSyntax, forcesBreakBeforeRightParen: Bool
20742095
) {
2096+
guard !parameters.parameterList.isEmpty else { return }
2097+
20752098
after(parameters.leftParen, tokens: .break(.open, size: 0), .open(argumentListConsistency()))
20762099
before(
20772100
parameters.rightParen,

Tests/SwiftFormatPrettyPrintTests/FunctionDeclTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,4 +1019,24 @@ public class FunctionDeclTests: PrettyPrintTestCase {
10191019

10201020
assertPrettyPrintEqual(input: input, expected: input, linelength: 80)
10211021
}
1022+
1023+
func testDoesNotBreakInsideEmptyParens() {
1024+
// If the function name is so long that the parentheses of a no-argument parameter list would
1025+
// be pushed past the margin, don't break inside them.
1026+
let input =
1027+
"""
1028+
func fooBarBaz() {}
1029+
1030+
"""
1031+
1032+
let expected =
1033+
"""
1034+
func
1035+
fooBarBaz()
1036+
{}
1037+
1038+
"""
1039+
1040+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 14)
1041+
}
10221042
}

Tests/SwiftFormatPrettyPrintTests/InitializerDeclTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,8 @@ public class InitializerDeclTests: PrettyPrintTestCase {
352352
let expected =
353353
"""
354354
struct Struct {
355-
@objc @inlinable public init<
355+
@objc @inlinable
356+
public init<
356357
Elements: Collection, Element
357358
>(
358359
element: Element,
@@ -388,7 +389,8 @@ public class InitializerDeclTests: PrettyPrintTestCase {
388389
let expected =
389390
"""
390391
struct Struct {
391-
@objc @inlinable public init<
392+
@objc @inlinable
393+
public init<
392394
Elements: Collection, Element
393395
>(
394396
element: Element,

Tests/SwiftFormatPrettyPrintTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ extension FunctionDeclTests {
264264
("testBreaksBeforeOrInsideOutputWithAttributes_prioritizingKeepingOutputTogether", testBreaksBeforeOrInsideOutputWithAttributes_prioritizingKeepingOutputTogether),
265265
("testBreaksBeforeOrInsideOutputWithWhereClause", testBreaksBeforeOrInsideOutputWithWhereClause),
266266
("testBreaksBeforeOrInsideOutputWithWhereClause_prioritizingKeepingOutputTogether", testBreaksBeforeOrInsideOutputWithWhereClause_prioritizingKeepingOutputTogether),
267+
("testDoesNotBreakInsideEmptyParens", testDoesNotBreakInsideEmptyParens),
267268
("testDoesNotCollapseFunctionParameterAttributes", testDoesNotCollapseFunctionParameterAttributes),
268269
("testDoesNotCollapseStackedFunctionParameterAttributes", testDoesNotCollapseStackedFunctionParameterAttributes),
269270
("testEmptyFunction", testEmptyFunction),

0 commit comments

Comments
 (0)