Repository: swiftlang/swift-syntax (swift)

Issue:
Improvement for`IfStmt` in SyntaxBuilder
### Description

When creating an `IfStmt` it would be more convenient to write 

```swift
IfStmt("if [.init, .as].contains(token.tokenKind), let nextTokenKind = token.nextToken(viewMode: .sourceAccurate)?.tokenKind") {
}
```

instead of 


```swift
IfStmt(
  conditions: ConditionElementList {
    OptionalBindingCondition(
      letOrVarKeyword: .let,
      pattern: PatternSyntax("nextTokenKind"),
      initializer: InitializerClause(value: Expr("token.nextToken(viewMode: .sourceAccurate)?.tokenKind"))
    )
  }
)
```

Pull request:
Fixes #1143

Patch:
diff --git a/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift b/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift
index de810503565..90a10086440 100644
--- a/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift
+++ b/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift
@@ -63,7 +63,7 @@ public extension HasTrailingOptionalCodeBlock where Self: SyntaxExpressibleByStr
   }
 }
 
-extension AccessorDeclSyntax: HasTrailingOptionalCodeBlock {}
+extension AccessorDecl: HasTrailingOptionalCodeBlock {}
 extension DeinitializerDecl: HasTrailingOptionalCodeBlock {}
 extension FunctionDecl: HasTrailingOptionalCodeBlock {}
 extension InitializerDecl: HasTrailingOptionalCodeBlock {}
@@ -87,3 +87,23 @@ extension EnumDecl: HasTrailingMemberDeclBlock {}
 extension ExtensionDecl: HasTrailingMemberDeclBlock {}
 extension ProtocolDecl: HasTrailingMemberDeclBlock {}
 extension StructDecl: HasTrailingMemberDeclBlock {}
+
+// MARK: - IfStmt
+// IfStmtSyntax is a special scenario as we also have the `else` body or an if-else
+// So we cannot conform to `HasTrailingCodeBlock`
+
+public extension IfStmt {
+  init(_ signature: String, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemList, @CodeBlockItemListBuilder `else` elseBuilder: () -> CodeBlockItemList? = { nil }) {
+    self = "\(raw: signature) {}"
+    self.body = CodeBlock(statements: bodyBuilder())
+    self.elseBody = elseBuilder().map { .codeBlock(CodeBlock(statements: $0)) }
+    self.elseKeyword = elseBody != nil ? .elseKeyword() : nil
+  }
+
+  init(_ signature: String, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemList, elseIf: IfStmt) {
+    self = "\(raw: signature) {}"
+    self.body = CodeBlock(statements: bodyBuilder())
+    self.elseBody = .ifStmt(elseIf)
+    self.elseKeyword = elseBody != nil ? .elseKeyword() : nil
+  }
+}


Test patch:
diff --git a/Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift b/Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift
index 8a75048e9f8..1a5ea5601c0 100644
--- a/Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift
+++ b/Tests/SwiftSyntaxBuilderTest/IfStmtTests.swift
@@ -29,6 +29,62 @@ final class IfStmtTests: XCTestCase {
     )
   }
 
+  func testIfStmtyntax() {
+    let testCases: [UInt: (IfStmt, String)] = [
+      #line: (
+        IfStmt(
+          """
+          if foo == x {
+            return foo
+          }
+          """
+        ),
+        """
+        if foo == x {
+            return foo
+        }
+        """
+      ),
+      #line: (
+        IfStmt("if foo == x") { ReturnStmt("return foo") },
+        """
+        if foo == x {
+            return foo
+        }
+        """
+      ),
+      #line: (
+        IfStmt("if foo == x") {
+          ReturnStmt("return foo")
+        } else: {
+          ReturnStmt("return bar")
+        },
+        """
+        if foo == x {
+            return foo
+        }else {
+            return bar
+        }
+        """
+      ),
+      #line: (
+        IfStmt("if foo == x", bodyBuilder: { ReturnStmt("return foo") }, elseIf: IfStmtSyntax("if foo == z") { ReturnStmt("return baz") }),
+        """
+        if foo == x {
+            return foo
+        }else if foo == z {
+            return baz
+        }
+        """
+      ),
+    ]
+
+    for (line, testCase) in testCases {
+      let (builder, expected) = testCase
+      AssertBuildResult(builder, expected, line: line)
+    }
+  }
+
   func testIfElseStmt() {
     // Use the convenience initializer from IfStmtConvenienceInitializers
     // with an else branch expressed by a second trailing closure.