Skip to content

Commit a991bdf

Browse files
authored
Merge pull request #1628 from ahoppen/ahoppen/5.9/header-doc
[5.9] Add documentation for initializers that build a node from a header an
2 parents f2b149e + d12f590 commit a991bdf

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,25 @@ extension SyntaxStringInterpolation {
4040
public protocol HasTrailingCodeBlock {
4141
var body: CodeBlockSyntax { get set }
4242

43+
/// Constructs a syntax node where `header` builds the text of the node before the body in braces and `bodyBuilder` is used to build the node’s body.
44+
///
45+
/// For example, you can construct
46+
///
47+
/// ```swift
48+
/// while x < 5 {
49+
/// x += 1
50+
/// }
51+
/// ```
52+
///
53+
/// using this call
54+
///
55+
/// ```swift
56+
/// try WhileStmtSyntax("while x < 5") {
57+
/// ExprSyntax("x += 1")
58+
/// }
59+
/// ```
60+
///
61+
/// Throws an error if `header` defines a different node type than the type the initializer is called on. E.g. if calling `try IfStmtSyntax("while x < 5") {}`
4362
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () throws -> CodeBlockItemListSyntax) rethrows
4463
}
4564

@@ -71,6 +90,25 @@ extension WhileStmtSyntax: HasTrailingCodeBlock {}
7190
public protocol HasTrailingOptionalCodeBlock {
7291
var body: CodeBlockSyntax? { get set }
7392

93+
/// Constructs a syntax node where `header` builds the text of the node before the body in braces and `bodyBuilder` is used to build the node’s body.
94+
///
95+
/// For example, you can construct
96+
///
97+
/// ```swift
98+
/// func addOne(_ base: Int) -> Int {
99+
/// return base + 1
100+
/// }
101+
/// ```
102+
///
103+
/// using this call
104+
///
105+
/// ```swift
106+
/// try FunctionDeclSyntax("func addOne(_ base: Int) -> Int") {
107+
/// ExprSyntax("return base + 1")
108+
/// }
109+
/// ```
110+
///
111+
/// Throws an error if `header` defines a different node type than the type the initializer is called on. E.g. if calling `try FunctionDeclSyntax("init") {}`
74112
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () throws -> CodeBlockItemListSyntax) throws
75113
}
76114

@@ -95,6 +133,27 @@ extension InitializerDeclSyntax: HasTrailingOptionalCodeBlock {}
95133
public protocol HasTrailingMemberDeclBlock {
96134
var memberBlock: MemberDeclBlockSyntax { get set }
97135

136+
/// Constructs a syntax node where `header` builds the text of the node before the members in braces and `membersBuilder` is used to list the node’s members.
137+
///
138+
/// For example, you can construct
139+
///
140+
/// ```swift
141+
/// struct Point {
142+
/// var x: Int
143+
/// var y: Int
144+
/// }
145+
/// ```
146+
///
147+
/// using this call
148+
///
149+
/// ```swift
150+
/// try StructDeclSyntax("struct Point") {
151+
/// DeclSyntax("var x: Int")
152+
/// DeclSyntax("var y: Int")
153+
/// }
154+
/// ```
155+
///
156+
/// Throws an error if `header` defines a different node type than the type the initializer is called on. E.g. if calling `try StructDeclSyntax("class MyClass") {}`
98157
init(_ header: PartialSyntaxNodeString, @MemberDeclListBuilder membersBuilder: () throws -> MemberDeclListSyntax) throws
99158
}
100159

@@ -121,6 +180,23 @@ extension StructDeclSyntax: HasTrailingMemberDeclBlock {}
121180
// So we cannot conform to `HasTrailingCodeBlock`
122181

123182
public extension IfExprSyntax {
183+
/// Constructs an `if` expression with an optional `else` block.
184+
///
185+
/// `header` specifies the part of the `if` expression before the body’s first brace.
186+
///
187+
/// For example, the following `if` expression has the header `if sunny`
188+
///
189+
/// ```swift
190+
/// if sunny {
191+
/// sunbath()
192+
/// }
193+
/// ```
194+
///
195+
/// If `elseBuilder` is not `nil`, an `else` keyword will automatically be inserted.
196+
///
197+
/// This function takes care of inserting the braces as well.
198+
///
199+
/// Throws an error if `header` does not start an `if` expression. E.g. if calling `try IfExprSyntax("while true") {}`
124200
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () throws -> CodeBlockItemListSyntax, @CodeBlockItemListBuilder `else` elseBuilder: () throws -> CodeBlockItemListSyntax? = { nil }) throws {
125201
let expr = ExprSyntax("\(header) {}")
126202
guard let ifExpr = expr.as(Self.self) else {
@@ -132,6 +208,40 @@ public extension IfExprSyntax {
132208
self.elseKeyword = elseBody != nil ? .keyword(.else) : nil
133209
}
134210

211+
/// Constructs an `if` expression with a following `else if` clause.
212+
/// This can be used to create longer chains of `if`, `else if` expressions.
213+
///
214+
/// For example, you can construct
215+
///
216+
/// ```swift
217+
/// if x == 1 {
218+
/// return "one
219+
/// } else if x == 2 {
220+
/// return "two
221+
/// } else {
222+
/// return "many
223+
/// }
224+
/// ```
225+
///
226+
/// using this call
227+
///
228+
/// ```swift
229+
/// try IfExprSyntax(
230+
/// "if x == 1",
231+
/// bodyBuilder: {
232+
/// StmtSyntax(#"return "one""#)
233+
/// }, elseIf: IfExprSyntax(
234+
/// "if x == 2",
235+
/// bodyBuilder: {
236+
/// StmtSyntax(#"return "two""#)
237+
/// }, else: {
238+
/// StmtSyntax(#"return "many""#)
239+
/// }
240+
/// )
241+
/// )
242+
/// ```
243+
///
244+
/// Throws an error if `header` does not start an `if` expression. E.g. if calling `try IfExprSyntax("while true", bodyBuilder: {}, elseIf: {})`
135245
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () throws -> CodeBlockItemListSyntax, elseIf: IfExprSyntax) throws {
136246
let expr = ExprSyntax("\(header) {}")
137247
guard let ifExpr = expr.as(Self.self) else {
@@ -147,6 +257,24 @@ public extension IfExprSyntax {
147257
// MARK: - SwitchCase
148258

149259
extension SwitchCaseSyntax {
260+
/// Constructs a case item where `header` includes the text between the `case` keyword and the `:` (both inclusive) and `statementsBuilder` can be used to build the statements inside the case item.
261+
///
262+
/// For example, you can construct
263+
///
264+
/// ```swift
265+
/// default:
266+
/// return nil
267+
/// ```
268+
///
269+
/// using this call
270+
///
271+
/// ```swift
272+
/// try SwitchCaseSyntax("default:") {
273+
/// StmtSyntax("return")
274+
/// }
275+
/// ```
276+
///
277+
/// Throws an error if `header` does not start a switch case item. E.g. if calling `try SwitchCaseSyntax("func foo") {}`
150278
public init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder statementsBuilder: () throws -> CodeBlockItemListSyntax) rethrows {
151279
self = SwitchCaseSyntax("\(header)")
152280
self.statements = try statementsBuilder()
@@ -158,6 +286,29 @@ extension SwitchCaseSyntax {
158286
// So we cannot conform to `HasTrailingCodeBlock` or `HasTrailingMemberDeclBlock`
159287

160288
public extension SwitchExprSyntax {
289+
/// Constructs a `switch` expression where `header` builds the text before the opening `{` and `casesBuilder` can be used to build the case items.
290+
///
291+
/// For example, to construct
292+
///
293+
/// ```swift
294+
/// switch direction {
295+
/// case .up:
296+
/// goUp()
297+
/// case .down:
298+
/// goDown()
299+
/// }
300+
/// ```
301+
///
302+
/// using this call
303+
///
304+
/// ```swift
305+
/// try SwitchExprSyntax("switch direction") {
306+
/// SwitchCaseSyntax("case .up: goUp()")
307+
/// SwitchCaseSyntax("case .down: goDown()")
308+
/// }
309+
/// ```
310+
///
311+
/// Throws an error if `header` does not start a switch expression. E.g. if calling `try SwitchExprSyntax("if x < 42") {}`
161312
init(_ header: PartialSyntaxNodeString, @SwitchCaseListBuilder casesBuilder: () throws -> SwitchCaseListSyntax = { SwitchCaseListSyntax([]) }) throws {
162313
let expr = ExprSyntax("\(header) {}")
163314
guard let switchExpr = expr.as(Self.self) else {
@@ -173,6 +324,25 @@ public extension SwitchExprSyntax {
173324
// So we cannot conform to `HasTrailingCodeBlock` or `HasTrailingMemberDeclBlock`
174325

175326
public extension VariableDeclSyntax {
327+
/// Construct a variable with a single `get` accessor where `header` builds the text beofre the opening `{` and `accessor` builds the accessor body.
328+
///
329+
/// For example, to construt
330+
///
331+
/// ```swift
332+
/// var x: Int {
333+
/// return origin.x
334+
/// }
335+
/// ```
336+
///
337+
/// using this call
338+
///
339+
/// ```swift
340+
/// try VariableDeclSyntax("var x: Int") {
341+
/// StmtSyntax("return origin.x")
342+
/// }
343+
/// ```
344+
///
345+
/// Throws an error if `header` does not start a variable declaration. E.g. if calling `try VariableDeclSyntax("func foo") {}`
176346
init(_ header: PartialSyntaxNodeString, @CodeBlockItemListBuilder accessor: () throws -> CodeBlockItemListSyntax) throws {
177347
let decl = DeclSyntax("\(header) {}")
178348
guard let castedDecl = decl.as(Self.self) else {

Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,32 @@ final class IfStmtTests: XCTestCase {
9797
}
9898
"""
9999
),
100+
#line: (
101+
try IfExprSyntax(
102+
"if x == 1",
103+
bodyBuilder: {
104+
StmtSyntax(#"return "one""#)
105+
},
106+
elseIf: IfExprSyntax(
107+
"if x == 2",
108+
bodyBuilder: {
109+
StmtSyntax(#"return "two""#)
110+
},
111+
else: {
112+
StmtSyntax(#"return "many""#)
113+
}
114+
)
115+
),
116+
"""
117+
if x == 1 {
118+
return "one"
119+
} else if x == 2 {
120+
return "two"
121+
} else {
122+
return "many"
123+
}
124+
"""
125+
),
100126
]
101127

102128
for (line, testCase) in testCases {

0 commit comments

Comments
 (0)