Skip to content

Commit 92dbcbc

Browse files
authored
Merge pull request #2451 from RoBo-Inc/list_builder_protocol
protocol ListBuilder
2 parents 3ec17e0 + c1dc8b3 commit 92dbcbc

File tree

8 files changed

+343
-3987
lines changed

8 files changed

+343
-3987
lines changed

CodeGeneration/Sources/Utils/SyntaxBuildableType.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,6 @@ public struct SyntaxBuildableType: Hashable {
146146
}
147147
}
148148

149-
public var parameterType: TypeSyntax {
150-
return optionalWrapped(type: parameterBaseType)
151-
}
152-
153149
/// Assuming that this is a collection type, the non-optional type of the result builder
154150
/// that can be used to build the collection.
155151
public var resultBuilderType: TypeSyntax {
@@ -161,11 +157,6 @@ public struct SyntaxBuildableType: Hashable {
161157
}
162158
}
163159

164-
/// Whether this type has the `WithTrailingComma` trait.
165-
public var hasWithTrailingCommaTrait: Bool {
166-
SYNTAX_NODES.compactMap(\.layoutNode).contains { $0.type == self && $0.traits.contains("WithTrailingComma") }
167-
}
168-
169160
/// If this type is not a base kind, its base type (see `SyntaxBuildableNode.base_type()`),
170161
/// otherwise `nil`.
171162
public var baseType: Self? {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift

Lines changed: 7 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -20,161 +20,34 @@ let resultBuildersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2020

2121
for node in SYNTAX_NODES.compactMap(\.collectionNode) {
2222
let type = SyntaxBuildableType(kind: .node(kind: node.kind))
23-
let elementType = node.collectionElementType
24-
let expressionType: TypeSyntax = node.elementChoices.count == 1 ? elementType.parameterType : TypeSyntax("\(type.buildable).Element")
2523

2624
try! StructDeclSyntax(
2725
"""
26+
27+
// MARK: - \(type.resultBuilderType)
28+
2829
@resultBuilder
2930
\(node.node.apiAttributes())\
30-
public struct \(type.resultBuilderType)
31+
public struct \(type.resultBuilderType): ListBuilder
3132
"""
3233
) {
3334
DeclSyntax(
3435
"""
35-
/// The type of individual statement expressions in the transformed function,
36-
/// which defaults to Component if buildExpression() is not provided.
37-
public typealias Expression = \(expressionType)
38-
"""
39-
)
40-
41-
DeclSyntax(
42-
"""
43-
/// The type of a partial result, which will be carried through all of the
44-
/// build methods.
45-
public typealias Component = [Expression]
46-
"""
47-
)
48-
49-
DeclSyntax(
50-
"""
51-
/// The type of the final returned result, which defaults to Component if
52-
/// buildFinalResult() is not provided.
53-
public typealias FinalResult = \(type.buildable)
54-
"""
55-
)
56-
57-
DeclSyntax(
58-
"""
59-
/// Required by every result builder to build combined results from
60-
/// statement blocks.
61-
public static func buildBlock(_ components: Self.Component...) -> Self.Component {
62-
return components.flatMap { $0 }
63-
}
64-
"""
65-
)
66-
67-
DeclSyntax(
68-
"""
69-
/// If declared, provides contextual type information for statement
70-
/// expressions to translate them into partial results.
71-
public static func buildExpression(_ expression: Self.Expression) -> Self.Component {
72-
return [expression]
73-
}
36+
public typealias FinalResult = \(type.syntaxBaseName)
7437
"""
7538
)
7639

7740
if node.elementChoices.count > 1 {
7841
for elementChoice in node.elementChoices {
7942
DeclSyntax(
8043
"""
81-
/// If declared, provides contextual type information for statement
82-
/// expressions to translate them into partial results.
83-
public static func buildExpression(_ expression: \(elementChoice.syntaxType)) -> Self.Component {
84-
return buildExpression(.init(expression))
44+
public static func buildExpression(_ expression: \(elementChoice.syntaxType)) -> Component {
45+
buildExpression(.init(expression))
8546
}
8647
"""
8748
)
8849
}
8950
}
90-
91-
DeclSyntax(
92-
"""
93-
/// Add all the elements of `expression` to this result builder, effectively flattening them.
94-
///
95-
/// - Note: This overload is disfavored to resolve an ambiguity when both the final result and
96-
/// the elements are expressible by string interpolation. In that case we favor creating a
97-
/// single element from the string literal.
98-
@_disfavoredOverload
99-
public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component {
100-
return expression.map { $0 }
101-
}
102-
"""
103-
)
104-
105-
DeclSyntax(
106-
"""
107-
/// Enables support for `if` statements that do not have an `else`.
108-
public static func buildOptional(_ component: Self.Component?) -> Self.Component {
109-
return component ?? []
110-
}
111-
"""
112-
)
113-
114-
DeclSyntax(
115-
"""
116-
/// With buildEither(second:), enables support for 'if-else' and 'switch'
117-
/// statements by folding conditional results into a single result.
118-
public static func buildEither(first component: Self.Component) -> Self.Component {
119-
return component
120-
}
121-
"""
122-
)
123-
124-
DeclSyntax(
125-
"""
126-
/// With buildEither(first:), enables support for 'if-else' and 'switch'
127-
/// statements by folding conditional results into a single result.
128-
public static func buildEither(second component: Self.Component) -> Self.Component {
129-
return component
130-
}
131-
"""
132-
)
133-
134-
DeclSyntax(
135-
"""
136-
/// Enables support for 'for..in' loops by combining the
137-
/// results of all iterations into a single result.
138-
public static func buildArray(_ components: [Self.Component]) -> Self.Component {
139-
return components.flatMap { $0 }
140-
}
141-
"""
142-
)
143-
144-
DeclSyntax(
145-
"""
146-
/// If declared, this will be called on the partial result of an 'if'
147-
/// #available' block to allow the result builder to erase type
148-
/// information.
149-
public static func buildLimitedAvailability(_ component: Self.Component) -> Self.Component {
150-
return component
151-
}
152-
"""
153-
)
154-
155-
try FunctionDeclSyntax(
156-
"""
157-
/// If declared, this will be called on the partial result from the outermost
158-
/// block statement to produce the final returned result.
159-
public static func buildFinalResult(_ component: Component) -> FinalResult
160-
"""
161-
) {
162-
if elementType.isToken {
163-
StmtSyntax("return .init(component)")
164-
} else if elementType.hasWithTrailingCommaTrait {
165-
DeclSyntax("let lastIndex = component.count - 1")
166-
167-
StmtSyntax(
168-
"""
169-
return .init(component.enumerated().map { index, source in
170-
return index < lastIndex ? source.ensuringTrailingComma() : source
171-
})
172-
"""
173-
)
174-
} else {
175-
StmtSyntax("return .init(component)")
176-
}
177-
}
17851
}
17952

18053
DeclSyntax(

Sources/SwiftSyntaxBuilder/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_swift_syntax_library(SwiftSyntaxBuilder
1010
ConvenienceInitializers.swift
1111
DeclSyntaxParseable.swift
1212
Indenter.swift
13+
ListBuilder.swift
1314
ResultBuilderExtensions.swift
1415
SwiftSyntaxBuilderCompatibility.swift
1516
Syntax+StringInterpolation.swift

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@_spi(RawSyntax) import SwiftParser
1414
@_spi(RawSyntax) import SwiftSyntax
1515

16-
// MARK: - ArrayElementList
16+
// MARK: - ArrayElementListSyntax
1717

1818
extension ArrayElementListSyntax {
1919
public init(expressions: [ExprSyntax]) {
@@ -30,15 +30,15 @@ extension ArrayElementListSyntax {
3030
}
3131
}
3232

33-
// MARK: - ArrayExpr
33+
// MARK: - ArrayExprSyntax
3434

3535
extension ArrayExprSyntax {
3636
public init(expressions: [ExprSyntax]) {
3737
self.init(elements: ArrayElementListSyntax(expressions: expressions))
3838
}
3939
}
4040

41-
// MARK: - CustomAttribute
41+
// MARK: - AttributeSyntax
4242

4343
extension AttributeSyntax {
4444
/// A convenience initializer that allows passing in arguments using a result builder
@@ -58,15 +58,15 @@ extension AttributeSyntax {
5858
}
5959
}
6060

61-
// MARK: - BinaryOperatorExpr
61+
// MARK: - BinaryOperatorExprSyntax
6262

6363
extension BinaryOperatorExprSyntax {
6464
public init(text: String) {
6565
self.init(operator: .binaryOperator(text))
6666
}
6767
}
6868

69-
// MARK: - BooleanLiteralExpr
69+
// MARK: - BooleanLiteralExprSyntax
7070

7171
extension BooleanLiteralExprSyntax: ExpressibleByBooleanLiteral {
7272
public init(_ value: Bool) {
@@ -78,7 +78,7 @@ extension BooleanLiteralExprSyntax: ExpressibleByBooleanLiteral {
7878
}
7979
}
8080

81-
// MARK: - CatchClause
81+
// MARK: - CatchClauseSyntax
8282

8383
extension CatchClauseSyntax {
8484
/// A convenience initializer that calculates spacing around the `catch` keyword.
@@ -96,7 +96,7 @@ extension CatchClauseSyntax {
9696
}
9797
}
9898

99-
// MARK: - DictionaryExpr
99+
// MARK: - DictionaryExprSyntax
100100

101101
extension DictionaryExprSyntax {
102102
/// A convenience initializer that allows passing in members using a result builder
@@ -115,7 +115,15 @@ extension DictionaryExprSyntax {
115115
}
116116
}
117117

118-
// MARK: - Expr
118+
// MARK: - ExprListSyntax
119+
120+
extension ExprListSyntax {
121+
public init(_ elements: [ExprSyntaxProtocol]) {
122+
self.init(elements.map { ExprSyntax(fromProtocol: $0) } as [ExprSyntax])
123+
}
124+
}
125+
126+
// MARK: - ExprSyntax
119127

120128
extension ExprSyntax {
121129
/// Returns a syntax tree for an expression that represents the value of the
@@ -163,7 +171,7 @@ extension FloatLiteralExprSyntax: ExpressibleByFloatLiteral {
163171
}
164172
}
165173

166-
// MARK: - FunctionCallExpr
174+
// MARK: - FunctionCallExprSyntax
167175

168176
extension FunctionCallExprSyntax {
169177
/// A convenience initializer that allows passing in arguments using a result builder
@@ -188,7 +196,7 @@ extension FunctionCallExprSyntax {
188196
}
189197
}
190198

191-
// MARK: - IntegerLiteralExpr
199+
// MARK: - IntegerLiteralExprSyntax
192200

193201
extension IntegerLiteralExprSyntax: ExpressibleByIntegerLiteral {
194202
public init(_ value: Int) {
@@ -200,7 +208,21 @@ extension IntegerLiteralExprSyntax: ExpressibleByIntegerLiteral {
200208
}
201209
}
202210

203-
// MARK: - StringLiteralExpr
211+
// MARK: - LabeledExprSyntax
212+
213+
extension LabeledExprSyntax {
214+
/// A convenience initializer that allows passing in label as an optional string.
215+
/// The presence of the colon will be inferred based on the presence of the label.
216+
public init(label: String? = nil, expression: some ExprSyntaxProtocol) {
217+
self.init(
218+
label: label.map { .identifier($0) },
219+
colon: label == nil ? nil : .colonToken(trailingTrivia: .space),
220+
expression: expression
221+
)
222+
}
223+
}
224+
225+
// MARK: - StringLiteralExprSyntax
204226

205227
extension String {
206228
/// Replace literal newlines with "\r", "\n", "\u{2028}", and ASCII control characters with "\0", "\u{7}"
@@ -338,21 +360,15 @@ extension StringLiteralExprSyntax {
338360
}
339361
}
340362

341-
// MARK: - TupleExprElement
363+
// MARK: - UnexpectedNodesSyntax
342364

343-
extension LabeledExprSyntax {
344-
/// A convenience initializer that allows passing in label as an optional string.
345-
/// The presence of the colon will be inferred based on the presence of the label.
346-
public init(label: String? = nil, expression: some ExprSyntaxProtocol) {
347-
self.init(
348-
label: label.map { .identifier($0) },
349-
colon: label == nil ? nil : .colonToken(trailingTrivia: .space),
350-
expression: expression
351-
)
365+
extension UnexpectedNodesSyntax {
366+
public init(_ elements: [SyntaxProtocol]) {
367+
self.init(elements.map { Syntax(fromProtocol: $0) } as [Syntax])
352368
}
353369
}
354370

355-
// MARK: - VariableDecl
371+
// MARK: - VariableDeclSyntax
356372

357373
extension VariableDeclSyntax {
358374
/// Creates an optionally initialized property.

0 commit comments

Comments
 (0)