Skip to content

Commit 429b742

Browse files
committed
Add SwiftSyntaxBuilder convenience initializers that mix parser integration and result builders
1 parent 07aa98a commit 429b742

File tree

8 files changed

+149
-44
lines changed

8 files changed

+149
-44
lines changed

Sources/SwiftSyntaxBuilder/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ add_library(SwiftSyntaxBuilder STATIC
2222
ConvenienceInitializers/IfStmtConvenienceInitializers.swift
2323
ConvenienceInitializers/IntegerLiteralExprConvenienceInitializers.swift
2424
ConvenienceInitializers/MemberAccessExprConvenienceInitializers.swift
25+
ConvenienceInitializers/SwitchCaseConvenienceInitializers.swift
26+
ConvenienceInitializers/SyntaxNodeWithBody.swift
2527
ConvenienceInitializers/StringLiteralExprConvenienceInitializers.swift
2628
ConvenienceInitializers/TernaryExprConvenienceInitializers.swift
2729
ConvenienceInitializers/TupleExprElementConvenienceInitializers.swift
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 SwiftSyntax
14+
15+
public extension SwitchCase {
16+
init(_ label: String, @CodeBlockItemListBuilder statementsBuilder: () -> CodeBlockItemListSyntax) {
17+
self.init("\(label)")
18+
self.statements = statementsBuilder()
19+
}
20+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 SwiftSyntax
14+
15+
// MARK: - HasCodeBlock
16+
17+
public protocol HasTrailingCodeBlock {
18+
var body: CodeBlock { get set }
19+
}
20+
21+
public extension HasTrailingCodeBlock where Self: SyntaxExpressibleByStringInterpolation {
22+
init(_ signature: String, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) {
23+
self.init("\(signature) {}")
24+
self.body = CodeBlock(statements: bodyBuilder())
25+
}
26+
}
27+
28+
extension CatchClause: HasTrailingCodeBlock {}
29+
extension DeferStmt: HasTrailingCodeBlock {}
30+
extension DoStmt: HasTrailingCodeBlock {}
31+
extension ForInStmt: HasTrailingCodeBlock {}
32+
extension GuardStmt: HasTrailingCodeBlock {}
33+
extension WhileStmt: HasTrailingCodeBlock {}
34+
35+
// MARK: - HasOptionalCodeBlock
36+
37+
public protocol HasTrailingOptionalCodeBlock {
38+
var body: CodeBlockSyntax? { get set }
39+
}
40+
41+
public extension HasTrailingOptionalCodeBlock where Self: SyntaxExpressibleByStringInterpolation {
42+
init(_ signature: String, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) {
43+
self.init("\(signature) {}")
44+
self.body = CodeBlock(statements: bodyBuilder())
45+
}
46+
}
47+
48+
extension AccessorDeclSyntax: HasTrailingOptionalCodeBlock {}
49+
extension DeinitializerDecl: HasTrailingOptionalCodeBlock {}
50+
extension FunctionDecl: HasTrailingOptionalCodeBlock {}
51+
extension InitializerDecl: HasTrailingOptionalCodeBlock {}
52+
53+
54+
// MARK: HasTrailingMemberDeclBlock
55+
56+
public protocol HasTrailingMemberDeclBlock {
57+
var members: MemberDeclBlock { get set }
58+
}
59+
60+
public extension HasTrailingMemberDeclBlock where Self: SyntaxExpressibleByStringInterpolation {
61+
init(_ signature: String, @MemberDeclListBuilder membersBuilder: () -> MemberDeclListSyntax) {
62+
self.init("\(signature) {}")
63+
self.members = MemberDeclBlock(members: membersBuilder())
64+
}
65+
}
66+
67+
extension ActorDecl: HasTrailingMemberDeclBlock {}
68+
extension ClassDecl: HasTrailingMemberDeclBlock {}
69+
extension EnumDecl: HasTrailingMemberDeclBlock {}
70+
extension ExtensionDecl: HasTrailingMemberDeclBlock {}
71+
extension ProtocolDecl: HasTrailingMemberDeclBlock {}
72+
extension StructDecl: HasTrailingMemberDeclBlock {}
73+

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ enum SyntaxStringInterpolationError: Error, CustomStringConvertible {
121121
case .producedInvalidNodeType(expectedType: let expectedType, actualType: let actualType):
122122
return "Parsing the code snippet was expected to produce a \(expectedType) but produced a \(actualType)"
123123
case .diagnostics(let diagnostics, let tree):
124-
return DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics)
124+
// Start the diagnostc on a new line so it isn't prefixed with the file, which messes up the
125+
// column-aligned message from `DiagnosticsFormatter`.
126+
return "\n" + DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics)
125127
}
126128
}
127129
}

Tests/SwiftSyntaxBuilderTest/FunctionTests.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,14 @@ import _SwiftSyntaxTestSupport
1818

1919
final class FunctionTests: XCTestCase {
2020
func testFibonacci() {
21-
let leadingTrivia = Trivia.unexpectedText("")
22-
23-
let input = ParameterClause {
24-
FunctionParameter(firstName: .wildcard, secondName: .identifier("n"), colon: .colon, type: Type("Int"))
25-
}
26-
27-
let signature = FunctionSignature(input: input, output: ReturnClause(returnType: Type("Int")))
28-
29-
let buildable = FunctionDecl(leadingTrivia: leadingTrivia, identifier: .identifier("fibonacci"), signature: signature) {
21+
let buildable = FunctionDecl("func fibonacci(_ n: Int) -> Int") {
3022
IfStmt("if n <= 1 { return n }")
3123

3224
ReturnStmt("return fibonacci(n - 1) + self.fibonacci(n - 2)")
3325
}
3426

3527
AssertBuildResult(buildable, """
36-
func fibonacci(_ n: Int) -> Int {
28+
func fibonacci(_ n: Int) -> Int {
3729
if n <= 1 {
3830
return n
3931
}

Tests/SwiftSyntaxBuilderTest/ProtocolDeclTests.swift

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,8 @@ import SwiftSyntaxBuilder
1616

1717
final class ProtocolDeclTests: XCTestCase {
1818
func testProtocolDecl() {
19-
let returnType = ArrayType(elementType: Type("DeclSyntax"))
20-
let input = ParameterClause {
21-
FunctionParameter(firstName: .identifier("format"), colon: .colon, type: Type("Format"))
22-
FunctionParameter(firstName: .identifier("leadingTrivia"), colon: .colon, type: OptionalType(wrappedType: Type("Trivia")))
23-
}
24-
let functionSignature = FunctionSignature(input: input, output: ReturnClause(returnType: returnType))
25-
26-
let buildable = ProtocolDecl(modifiers: [DeclModifier(name: .public)], identifier: "DeclListBuildable") {
27-
FunctionDecl(identifier: .identifier("buildDeclList"), signature: functionSignature, body: nil)
19+
let buildable = ProtocolDecl("public protocol DeclListBuildable") {
20+
FunctionDecl("func buildDeclList(format: Format, leadingTrivia: Trivia?) -> [DeclSyntax]")
2821
}
2922

3023
AssertBuildResult(buildable, """

Tests/SwiftSyntaxBuilderTest/StructTests.swift

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,11 @@ final class StructTests: XCTestCase {
2626
}
2727

2828
func testNestedStruct() {
29-
let nestedStruct = StructDecl(
30-
leadingTrivia: [
31-
.docLineComment("/// A nested struct"),
32-
.newlines(1),
33-
.docLineComment("/// with multi line comment"),
34-
.newlines(1)
35-
],
36-
structKeyword: .struct,
37-
identifier: "NestedStruct",
38-
genericParameterClause: GenericParameterClause(rightAngleBracket: .rightAngle.withTrailingTrivia([])) {
39-
GenericParameter(name: "A")
40-
GenericParameter(name: "B", colon: .colon, inheritedType: Type("C"))
41-
GenericParameter(name: "D")
42-
},
43-
genericWhereClause: GenericWhereClause {
44-
GenericRequirement(body: .conformanceRequirement(ConformanceRequirement(leftTypeIdentifier: Type("A"), rightTypeIdentifier: SimpleTypeIdentifier("X"))))
45-
GenericRequirement(body: .sameTypeRequirement(SameTypeRequirement(
46-
leftTypeIdentifier: "A.P", equalityToken: .spacedBinaryOperator("=="), rightTypeIdentifier: "D")))
47-
}
48-
) {}
29+
let nestedStruct = StructDecl("""
30+
/// A nested struct
31+
/// with multi line comment
32+
struct NestedStruct<A, B: C, D> where A: X, A.P == D
33+
""") {}
4934

5035
let carriateReturnsStruct = StructDecl(
5136
leadingTrivia: [
@@ -67,10 +52,7 @@ final class StructTests: XCTestCase {
6752
structKeyword: .struct,
6853
identifier: "CarriageReturnFormFeedsStruct"
6954
)
70-
let testStruct = StructDecl(
71-
modifiers: [DeclModifier(name: .public)],
72-
identifier: "TestStruct"
73-
) {
55+
let testStruct = StructDecl("public struct TestStruct") {
7456
nestedStruct
7557
carriateReturnsStruct
7658
carriageReturnFormFeedsStruct
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 XCTest
14+
import SwiftSyntax
15+
import SwiftSyntaxBuilder
16+
17+
final class SwitchTests: XCTestCase {
18+
func testSwitch() {
19+
let syntax = SwitchStmt(expression: Expr("count")) {
20+
for num in 1..<3 {
21+
SwitchCase("case \(num):") {
22+
Expr("print(count)")
23+
}
24+
}
25+
SwitchCase("default:") {
26+
BreakStmt("break")
27+
}
28+
}
29+
30+
AssertBuildResult(syntax, """
31+
switch count {
32+
case 1:
33+
print(count)
34+
case 2:
35+
print(count)
36+
default:
37+
break
38+
}
39+
""")
40+
}
41+
}

0 commit comments

Comments
 (0)