Skip to content

Commit a48a3d4

Browse files
committed
Refactored file generation logic to be string-based and added validation tests
1 parent 883cb8a commit a48a3d4

12 files changed

+145
-126
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ var targets: [Target] = [
113113
dependencies: [
114114
"SwiftFormat",
115115
"_SwiftFormatTestSupport",
116+
"generate-swift-format",
116117
.product(name: "Markdown", package: "swift-markdown"),
117118
] + swiftSyntaxDependencies(["SwiftOperators", "SwiftParser", "SwiftSyntax", "SwiftSyntaxBuilder"])
118119
),

Sources/SwiftFormat/Core/Pipelines+Generated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information

Sources/SwiftFormat/Core/RuleNameCache+Generated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information

Sources/SwiftFormat/Core/RuleRegistry+Generated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information

Sources/generate-swift-format/FileGenerator.swift

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import Foundation
1414

1515
/// Common behavior used to generate source files.
1616
protocol FileGenerator {
17-
/// Types conforming to this protocol must implement this method to write their content into the
18-
/// given file handle.
19-
func write(into handle: FileHandle) throws
17+
/// Generates the file content as a String.
18+
func generateString() -> String
2019
}
2120

2221
private struct FailedToCreateFileError: Error {
@@ -30,21 +29,7 @@ extension FileGenerator {
3029
if fm.fileExists(atPath: url.path) {
3130
try fm.removeItem(at: url)
3231
}
33-
34-
if !fm.createFile(atPath: url.path, contents: nil, attributes: nil) {
35-
throw FailedToCreateFileError(url: url)
36-
}
37-
let handle = try FileHandle(forWritingTo: url)
38-
defer { handle.closeFile() }
39-
40-
try write(into: handle)
41-
}
42-
}
43-
44-
extension FileHandle {
45-
/// Writes the provided string as data to a file output stream.
46-
public func write(_ string: String) {
47-
guard let data = string.data(using: .utf8) else { return }
48-
write(data)
32+
let content = generateString()
33+
try content.write(to: url, atomically: true, encoding: .utf8)
4934
}
5035
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
15+
/// A namespace that provides paths for files generated by the `generate-swift-format` tool.
16+
enum GenerateSwiftFormatPaths {
17+
private static let sourcesDirectory =
18+
URL(fileURLWithPath: #file)
19+
.deletingLastPathComponent()
20+
.deletingLastPathComponent()
21+
static let rulesDirectory =
22+
sourcesDirectory
23+
.appendingPathComponent("SwiftFormat")
24+
.appendingPathComponent("Rules")
25+
static let pipelineFile =
26+
sourcesDirectory
27+
.appendingPathComponent("SwiftFormat")
28+
.appendingPathComponent("Core")
29+
.appendingPathComponent("Pipelines+Generated.swift")
30+
static let ruleRegistryFile =
31+
sourcesDirectory
32+
.appendingPathComponent("SwiftFormat")
33+
.appendingPathComponent("Core")
34+
.appendingPathComponent("RuleRegistry+Generated.swift")
35+
static let ruleNameCacheFile =
36+
sourcesDirectory
37+
.appendingPathComponent("SwiftFormat")
38+
.appendingPathComponent("Core")
39+
.appendingPathComponent("RuleNameCache+Generated.swift")
40+
static let ruleDocumentationFile =
41+
sourcesDirectory
42+
.appendingPathComponent("..")
43+
.appendingPathComponent("Documentation")
44+
.appendingPathComponent("RuleDocumentation.md")
45+
}

Sources/generate-swift-format/PipelineGenerator.swift

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ final class PipelineGenerator: FileGenerator {
2323
self.ruleCollector = ruleCollector
2424
}
2525

26-
func write(into handle: FileHandle) throws {
27-
handle.write(
28-
"""
26+
func generateString() -> String {
27+
var result = ""
28+
result += """
2929
//===----------------------------------------------------------------------===//
3030
//
3131
// This source file is part of the Swift.org open source project
3232
//
33-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
33+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
3434
// Licensed under Apache License v2.0 with Runtime Library Exception
3535
//
3636
// See https://swift.org/LICENSE.txt for license information
@@ -65,58 +65,41 @@ final class PipelineGenerator: FileGenerator {
6565
}
6666
6767
"""
68-
)
6968

7069
for (nodeType, lintRules) in ruleCollector.syntaxNodeLinters.sorted(by: { $0.key < $1.key }) {
71-
handle.write(
72-
"""
70+
result += """
7371
7472
override func visit(_ node: \(nodeType)) -> SyntaxVisitorContinueKind {
7573
7674
"""
77-
)
78-
7975
for ruleName in lintRules.sorted() {
80-
handle.write(
81-
"""
76+
result += """
8277
visitIfEnabled(\(ruleName).visit, for: node)
8378
8479
"""
85-
)
8680
}
87-
88-
handle.write(
89-
"""
81+
result += """
9082
return .visitChildren
9183
}
9284
9385
"""
94-
)
95-
96-
handle.write(
97-
"""
86+
result += """
9887
override func visitPost(_ node: \(nodeType)) {
9988
10089
"""
101-
)
10290
for ruleName in lintRules.sorted() {
103-
handle.write(
104-
"""
91+
result += """
10592
onVisitPost(rule: \(ruleName).self, for: node)
10693
10794
"""
108-
)
10995
}
110-
handle.write(
111-
"""
96+
result += """
11297
}
11398
11499
"""
115-
)
116100
}
117101

118-
handle.write(
119-
"""
102+
result += """
120103
}
121104
122105
extension FormatPipeline {
@@ -125,24 +108,18 @@ final class PipelineGenerator: FileGenerator {
125108
var node = node
126109
127110
"""
128-
)
129-
130111
for ruleName in ruleCollector.allFormatters.map({ $0.typeName }).sorted() {
131-
handle.write(
132-
"""
112+
result += """
133113
node = \(ruleName)(context: context).rewrite(node)
134114
135115
"""
136-
)
137116
}
138-
139-
handle.write(
140-
"""
117+
result += """
141118
return node
142119
}
143120
}
144121
145122
"""
146-
)
123+
return result
147124
}
148125
}

Sources/generate-swift-format/RuleDocumentationGenerator.swift

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ final class RuleDocumentationGenerator: FileGenerator {
2424
self.ruleCollector = ruleCollector
2525
}
2626

27-
func write(into handle: FileHandle) throws {
28-
handle.write(
29-
"""
27+
func generateString() -> String {
28+
var result = ""
29+
result += """
3030
<!-- This file is automatically generated with generate-swift-format. Do not edit! -->
3131
3232
# `swift-format` Lint and Format Rules
@@ -41,29 +41,20 @@ final class RuleDocumentationGenerator: FileGenerator {
4141
4242
4343
"""
44-
)
45-
4644
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
47-
handle.write(
48-
"""
49-
- [\(detectedRule.typeName)](#\(detectedRule.typeName))
50-
51-
"""
52-
)
45+
result += "- [\(detectedRule.typeName)](#\(detectedRule.typeName))\n"
5346
}
54-
5547
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
56-
handle.write(
57-
"""
48+
result += """
5849
5950
### \(detectedRule.typeName)
6051
6152
\(detectedRule.description ?? "")
6253
\(ruleFormatSupportDescription(for: detectedRule))
6354
6455
"""
65-
)
6656
}
57+
return result
6758
}
6859

6960
private func ruleFormatSupportDescription(for rule: RuleCollector.DetectedRule) -> String {

Sources/generate-swift-format/RuleNameCacheGenerator.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ final class RuleNameCacheGenerator: FileGenerator {
2323
self.ruleCollector = ruleCollector
2424
}
2525

26-
func write(into handle: FileHandle) throws {
27-
handle.write(
28-
"""
26+
func generateString() -> String {
27+
var result = ""
28+
result += """
2929
//===----------------------------------------------------------------------===//
3030
//
3131
// This source file is part of the Swift.org open source project
3232
//
33-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
33+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
3434
// Licensed under Apache License v2.0 with Runtime Library Exception
3535
//
3636
// See https://swift.org/LICENSE.txt for license information
@@ -45,11 +45,10 @@ final class RuleNameCacheGenerator: FileGenerator {
4545
public let ruleNameCache: [ObjectIdentifier: String] = [
4646
4747
"""
48-
)
49-
5048
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
51-
handle.write(" ObjectIdentifier(\(detectedRule.typeName).self): \"\(detectedRule.typeName)\",\n")
49+
result += " ObjectIdentifier(\(detectedRule.typeName).self): \"\(detectedRule.typeName)\",\n"
5250
}
53-
handle.write("]\n")
51+
result += "]\n"
52+
return result
5453
}
5554
}

Sources/generate-swift-format/RuleRegistryGenerator.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ final class RuleRegistryGenerator: FileGenerator {
2323
self.ruleCollector = ruleCollector
2424
}
2525

26-
func write(into handle: FileHandle) throws {
27-
handle.write(
28-
"""
26+
func generateString() -> String {
27+
var result = ""
28+
result += """
2929
//===----------------------------------------------------------------------===//
3030
//
3131
// This source file is part of the Swift.org open source project
3232
//
33-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
33+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
3434
// Licensed under Apache License v2.0 with Runtime Library Exception
3535
//
3636
// See https://swift.org/LICENSE.txt for license information
@@ -44,11 +44,10 @@ final class RuleRegistryGenerator: FileGenerator {
4444
public static let rules: [String: Bool] = [
4545
4646
"""
47-
)
48-
4947
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
50-
handle.write(" \"\(detectedRule.typeName)\": \(!detectedRule.isOptIn),\n")
48+
result += " \"\(detectedRule.typeName)\": \(!detectedRule.isOptIn),\n"
5149
}
52-
handle.write(" ]\n}\n")
50+
result += " ]\n}\n"
51+
return result
5352
}
5453
}

0 commit comments

Comments
 (0)