Skip to content

Commit 3191b8f

Browse files
authored
Merge pull request #704 from ahoppen/ahoppen/5.10/cherry-picks
[5.10] Add fixes that were present in 509.0.0 but not release/5.10
2 parents f14a625 + 317185c commit 3191b8f

23 files changed

+1003
-16
lines changed

Documentation/Configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ top-level keys and values:
8282
* `spacesAroundRangeFormationOperators` _(boolean)_: Determines whether whitespace should be forced
8383
before and after the range formation operators `...` and `..<`.
8484

85+
* `multiElementCollectionTrailingCommas` _(boolean)_: Determines whether multi-element collection literals should have trailing commas.
86+
Defaults to `true`.
87+
8588
> TODO: Add support for enabling/disabling specific syntax transformations in
8689
> the pipeline.
8790

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
154154
),
155155
.package(
156156
url: "https://github.com/apple/swift-syntax.git",
157-
branch: "main"
157+
branch: "release/5.10"
158158
),
159159
]
160160
} else {

Sources/SwiftFormat/API/Configuration+Default.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ extension Configuration {
3737
self.indentSwitchCaseLabels = false
3838
self.spacesAroundRangeFormationOperators = false
3939
self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
40+
self.multiElementCollectionTrailingCommas = true
4041
}
4142
}

Sources/SwiftFormat/API/Configuration.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public struct Configuration: Codable, Equatable {
4242
case rules
4343
case spacesAroundRangeFormationOperators
4444
case noAssignmentInExpressions
45+
case multiElementCollectionTrailingCommas
4546
}
4647

4748
/// A dictionary containing the default enabled/disabled states of rules, keyed by the rules'
@@ -162,6 +163,29 @@ public struct Configuration: Codable, Equatable {
162163
/// Contains exceptions for the `NoAssignmentInExpressions` rule.
163164
public var noAssignmentInExpressions: NoAssignmentInExpressionsConfiguration
164165

166+
/// Determines if multi-element collection literals should have trailing commas.
167+
///
168+
/// When `true` (default), the correct form is:
169+
/// ```swift
170+
/// let MyCollection = [1, 2,]
171+
/// ...
172+
/// let MyCollection = [
173+
/// "a": 1,
174+
/// "b": 2,
175+
/// ]
176+
/// ```
177+
///
178+
/// When `false`, the correct form is:
179+
/// ```swift
180+
/// let MyCollection = [1, 2]
181+
/// ...
182+
/// let MyCollection = [
183+
/// "a": 1,
184+
/// "b": 2
185+
/// ]
186+
/// ```
187+
public var multiElementCollectionTrailingCommas: Bool
188+
165189
/// Constructs a Configuration by loading it from a configuration file.
166190
public init(contentsOf url: URL) throws {
167191
let data = try Data(contentsOf: url)
@@ -239,6 +263,10 @@ public struct Configuration: Codable, Equatable {
239263
try container.decodeIfPresent(
240264
NoAssignmentInExpressionsConfiguration.self, forKey: .noAssignmentInExpressions)
241265
?? defaults.noAssignmentInExpressions
266+
self.multiElementCollectionTrailingCommas =
267+
try container.decodeIfPresent(
268+
Bool.self, forKey: .multiElementCollectionTrailingCommas)
269+
?? defaults.multiElementCollectionTrailingCommas
242270

243271
// If the `rules` key is not present at all, default it to the built-in set
244272
// so that the behavior is the same as if the configuration had been
@@ -271,6 +299,7 @@ public struct Configuration: Codable, Equatable {
271299
try container.encode(fileScopedDeclarationPrivacy, forKey: .fileScopedDeclarationPrivacy)
272300
try container.encode(indentSwitchCaseLabels, forKey: .indentSwitchCaseLabels)
273301
try container.encode(noAssignmentInExpressions, forKey: .noAssignmentInExpressions)
302+
try container.encode(multiElementCollectionTrailingCommas, forKey: .multiElementCollectionTrailingCommas)
274303
try container.encode(rules, forKey: .rules)
275304
}
276305

Sources/SwiftFormat/Core/ModifierListSyntax+Convenience.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ extension DeclModifierListSyntax {
1717
var accessLevelModifier: DeclModifierSyntax? {
1818
for modifier in self {
1919
switch modifier.name.tokenKind {
20-
case .keyword(.public), .keyword(.private), .keyword(.fileprivate), .keyword(.internal):
20+
case .keyword(.public), .keyword(.private), .keyword(.fileprivate), .keyword(.internal),
21+
.keyword(.package):
2122
return modifier
2223
default:
2324
continue

Sources/SwiftFormat/Core/Pipelines+Generated.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ class LintPipeline: SyntaxVisitor {
159159
}
160160

161161
override func visit(_ node: FunctionParameterSyntax) -> SyntaxVisitorContinueKind {
162+
visitIfEnabled(AlwaysUseLiteralForEmptyCollectionInit.visit, for: node)
162163
visitIfEnabled(NoLeadingUnderscores.visit, for: node)
163164
return .visitChildren
164165
}
@@ -243,6 +244,7 @@ class LintPipeline: SyntaxVisitor {
243244
}
244245

245246
override func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind {
247+
visitIfEnabled(AlwaysUseLiteralForEmptyCollectionInit.visit, for: node)
246248
visitIfEnabled(OmitExplicitReturns.visit, for: node)
247249
visitIfEnabled(UseSingleLinePropertyGetter.visit, for: node)
248250
return .visitChildren
@@ -356,6 +358,7 @@ extension FormatPipeline {
356358

357359
func rewrite(_ node: Syntax) -> Syntax {
358360
var node = node
361+
node = AlwaysUseLiteralForEmptyCollectionInit(context: context).rewrite(node)
359362
node = DoNotUseSemicolons(context: context).rewrite(node)
360363
node = FileScopedDeclarationPrivacy(context: context).rewrite(node)
361364
node = FullyIndirectEnum(context: context).rewrite(node)

Sources/SwiftFormat/Core/RuleNameCache+Generated.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
@_spi(Testing)
1717
public let ruleNameCache: [ObjectIdentifier: String] = [
1818
ObjectIdentifier(AllPublicDeclarationsHaveDocumentation.self): "AllPublicDeclarationsHaveDocumentation",
19+
ObjectIdentifier(AlwaysUseLiteralForEmptyCollectionInit.self): "AlwaysUseLiteralForEmptyCollectionInit",
1920
ObjectIdentifier(AlwaysUseLowerCamelCase.self): "AlwaysUseLowerCamelCase",
2021
ObjectIdentifier(AmbiguousTrailingClosureOverload.self): "AmbiguousTrailingClosureOverload",
2122
ObjectIdentifier(BeginDocumentationCommentWithOneLineSummary.self): "BeginDocumentationCommentWithOneLineSummary",

Sources/SwiftFormat/Core/RuleRegistry+Generated.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@_spi(Internal) public enum RuleRegistry {
1616
public static let rules: [String: Bool] = [
1717
"AllPublicDeclarationsHaveDocumentation": false,
18+
"AlwaysUseLiteralForEmptyCollectionInit": false,
1819
"AlwaysUseLowerCamelCase": true,
1920
"AmbiguousTrailingClosureOverload": true,
2021
"BeginDocumentationCommentWithOneLineSummary": false,

Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ public class PrettyPrinter {
557557
// We never want to add a trailing comma in an initializer so we disable trailing commas on
558558
// single element collections.
559559
let shouldHaveTrailingComma =
560-
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement
560+
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement && configuration.multiElementCollectionTrailingCommas
561561
if shouldHaveTrailingComma && !hasTrailingComma {
562562
diagnose(.addTrailingComma, category: .trailingComma)
563563
} else if !shouldHaveTrailingComma && hasTrailingComma {

Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,8 +1799,23 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
17991799

18001800
override func visit(_ node: AvailabilityLabeledArgumentSyntax) -> SyntaxVisitorContinueKind {
18011801
before(node.label, tokens: .open)
1802-
after(node.colon, tokens: .break(.continue, newlines: .elective(ignoresDiscretionary: true)))
1803-
after(node.value.lastToken(viewMode: .sourceAccurate), tokens: .close)
1802+
1803+
let tokensAfterColon: [Token]
1804+
let endTokens: [Token]
1805+
1806+
if case .string(let string) = node.value,
1807+
string.openingQuote.tokenKind == .multilineStringQuote
1808+
{
1809+
tokensAfterColon =
1810+
[.break(.open(kind: .block), newlines: .elective(ignoresDiscretionary: true))]
1811+
endTokens = [.break(.close(mustBreak: false), size: 0), .close]
1812+
} else {
1813+
tokensAfterColon = [.break(.continue, newlines: .elective(ignoresDiscretionary: true))]
1814+
endTokens = [.close]
1815+
}
1816+
1817+
after(node.colon, tokens: tokensAfterColon)
1818+
after(node.value.lastToken(viewMode: .sourceAccurate), tokens: endTokens)
18041819
return .visitChildren
18051820
}
18061821

@@ -2293,6 +2308,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
22932308

22942309
override func visit(_ node: GenericParameterSyntax) -> SyntaxVisitorContinueKind {
22952310
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
2311+
after(node.eachKeyword, tokens: .break)
22962312
after(node.colon, tokens: .break)
22972313
if let trailingComma = node.trailingComma {
22982314
after(trailingComma, tokens: .close, .break(.same))
@@ -2312,6 +2328,28 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
23122328
return .visitChildren
23132329
}
23142330

2331+
override func visit(_ node: PackElementExprSyntax) -> SyntaxVisitorContinueKind {
2332+
// `each` cannot be separated from the following token, or it is parsed as an identifier itself.
2333+
after(node.eachKeyword, tokens: .space)
2334+
return .visitChildren
2335+
}
2336+
2337+
override func visit(_ node: PackElementTypeSyntax) -> SyntaxVisitorContinueKind {
2338+
// `each` cannot be separated from the following token, or it is parsed as an identifier itself.
2339+
after(node.eachKeyword, tokens: .space)
2340+
return .visitChildren
2341+
}
2342+
2343+
override func visit(_ node: PackExpansionExprSyntax) -> SyntaxVisitorContinueKind {
2344+
after(node.repeatKeyword, tokens: .break)
2345+
return .visitChildren
2346+
}
2347+
2348+
override func visit(_ node: PackExpansionTypeSyntax) -> SyntaxVisitorContinueKind {
2349+
after(node.repeatKeyword, tokens: .break)
2350+
return .visitChildren
2351+
}
2352+
23152353
override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
23162354
return .visitChildren
23172355
}
@@ -2353,6 +2391,16 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
23532391
return .visitChildren
23542392
}
23552393

2394+
override func visit(_ node: SimpleStringLiteralExprSyntax) -> SyntaxVisitorContinueKind {
2395+
if node.openingQuote.tokenKind == .multilineStringQuote {
2396+
after(node.openingQuote, tokens: .break(.same, size: 0, newlines: .hard(count: 1)))
2397+
if !node.segments.isEmpty {
2398+
before(node.closingQuote, tokens: .break(.same, newlines: .hard(count: 1)))
2399+
}
2400+
}
2401+
return .visitChildren
2402+
}
2403+
23562404
override func visit(_ node: StringSegmentSyntax) -> SyntaxVisitorContinueKind {
23572405
// Looks up the correct break kind based on prior context.
23582406
func breakKind() -> BreakKind {
@@ -2481,6 +2529,27 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
24812529
return .visitChildren
24822530
}
24832531

2532+
override func visit(_ node: ConsumeExprSyntax) -> SyntaxVisitorContinueKind {
2533+
// The `consume` keyword cannot be separated from the following token or it will be parsed as
2534+
// an identifier.
2535+
after(node.consumeKeyword, tokens: .space)
2536+
return .visitChildren
2537+
}
2538+
2539+
override func visit(_ node: CopyExprSyntax) -> SyntaxVisitorContinueKind {
2540+
// The `copy` keyword cannot be separated from the following token or it will be parsed as an
2541+
// identifier.
2542+
after(node.copyKeyword, tokens: .space)
2543+
return .visitChildren
2544+
}
2545+
2546+
override func visit(_ node: DiscardStmtSyntax) -> SyntaxVisitorContinueKind {
2547+
// The `discard` keyword cannot be separated from the following token or it will be parsed as
2548+
// an identifier.
2549+
after(node.discardKeyword, tokens: .space)
2550+
return .visitChildren
2551+
}
2552+
24842553
override func visit(_ node: InheritanceClauseSyntax) -> SyntaxVisitorContinueKind {
24852554
// Normally, the open-break is placed before the open token. In this case, it's intentionally
24862555
// ordered differently so that the inheritance list can start on the current line and only

0 commit comments

Comments
 (0)