From 2c8a8c905b211ad8a5d3080941a4f0df6ddd64a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ba=CC=A8k?= <44930823+Matejkob@users.noreply.github.com> Date: Sun, 20 Aug 2023 22:07:43 +0200 Subject: [PATCH] Add appendInterpolation with optional syntax nodes Introduces a new method, appendInterpolation, to the SyntaxStringInterpolation struct, which takes an optional syntax node parameter. If the provided node is nil, no interpolation is added. Otherwise, interpolation is performed as usual. Additionally, this new method has been integrated into the CodeGeneration package to eliminate the usage of interpolation. --- .../swiftsyntax/RawSyntaxNodesFile.swift | 11 +++++----- .../swiftsyntax/SyntaxNodesFile.swift | 4 +++- .../swiftsyntax/SyntaxTraitsFile.swift | 4 +++- .../Syntax+StringInterpolation.swift | 20 +++++++++++++++++++ .../StringInterpolationTests.swift | 16 +++++++++++++++ 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxNodesFile.swift index 4a8f948bc5c..b442f9d7cd4 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxNodesFile.swift @@ -217,8 +217,9 @@ let rawSyntaxNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { let list = ExprListSyntax { ExprSyntax("layout.initialize(repeating: nil)") for (index, child) in node.children.enumerated() { - let optionalMark = child.isOptional ? "?" : "" - ExprSyntax("layout[\(raw: index)] = \(child.varOrCaseName.backtickedIfNeeded)\(raw: optionalMark).raw") + let optionalMark = child.isOptional ? TokenSyntax.postfixQuestionMarkToken() : nil + + ExprSyntax("layout[\(raw: index)] = \(child.varOrCaseName.backtickedIfNeeded)\(optionalMark).raw") .with(\.leadingTrivia, .newline) } } @@ -239,12 +240,12 @@ let rawSyntaxNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { for (index, child) in node.children.enumerated() { try VariableDeclSyntax("public var \(child.varOrCaseName.backtickedIfNeeded): Raw\(child.buildableType.buildable)") { - let iuoMark = child.isOptional ? "" : "!" + let exclamationMark = child.isOptional ? nil : TokenSyntax.exclamationMarkToken() if child.syntaxNodeKind == .syntax { - ExprSyntax("layoutView.children[\(raw: index)]\(raw: iuoMark)") + ExprSyntax("layoutView.children[\(raw: index)]\(exclamationMark)") } else { - ExprSyntax("layoutView.children[\(raw: index)].map(\(child.syntaxNodeKind.rawType).init(raw:))\(raw: iuoMark)") + ExprSyntax("layoutView.children[\(raw: index)].map(\(child.syntaxNodeKind.rawType).init(raw:))\(exclamationMark)") } } } diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift index 0648832d9c5..bfcc73f00fe 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -177,10 +177,12 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax { } } + let questionMark = child.isOptional ? TokenSyntax.postfixQuestionMarkToken() : nil + AccessorDeclSyntax( """ set(value) { - self = \(node.kind.syntaxType)(data.replacingChild(at: \(raw: index), with: value\(raw: child.isOptional ? "?" : "").data, arena: SyntaxArena())) + self = \(node.kind.syntaxType)(data.replacingChild(at: \(raw: index), with: value\(questionMark).data, arena: SyntaxArena())) } """ ) diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxTraitsFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxTraitsFile.swift index c32815a9e23..3ffe8b62623 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxTraitsFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxTraitsFile.swift @@ -26,10 +26,12 @@ let syntaxTraitsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { """ ) { for child in trait.children { + let questionMark = child.isOptional ? TokenSyntax.postfixQuestionMarkToken() : nil + DeclSyntax( """ \(child.documentation) - var \(child.varOrCaseName): \(child.syntaxNodeKind.syntaxType)\(raw: child.isOptional ? "?" : "") { get set } + var \(child.varOrCaseName): \(child.syntaxNodeKind.syntaxType)\(questionMark) { get set } """ ) } diff --git a/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift b/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift index 29b15cf4399..9a8436b0b9f 100644 --- a/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift +++ b/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift @@ -63,6 +63,12 @@ extension SyntaxStringInterpolation: StringInterpolationProtocol { } /// Append a syntax node to the interpolation. + /// + /// This method accepts a syntax node and appends it to the interpolation. + /// If there was a previous indentation value, the method will indent the + /// syntax node with that value. If not, it will use the syntax node as-is. + /// + /// - Parameter node: A syntax node that conforms to `SyntaxProtocol`. public mutating func appendInterpolation( _ node: Node ) { @@ -84,6 +90,20 @@ extension SyntaxStringInterpolation: StringInterpolationProtocol { self.lastIndentation = nil } + /// Append an optional syntax node to the interpolation. + /// + /// This method accepts an optional syntax node and appends it to the interpolation + /// if it exists. If the syntax node is nil, this method does nothing. + /// + /// - Parameter node: An optional syntax node that conforms to `SyntaxProtocol`. + public mutating func appendInterpolation( + _ node: Node? + ) { + if let node { + appendInterpolation(node) + } + } + public mutating func appendInterpolation(raw value: T) { sourceText.append(contentsOf: String(describing: value).utf8) self.lastIndentation = nil diff --git a/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift b/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift index bb2fd7e5f53..5ec8234107c 100644 --- a/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift +++ b/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift @@ -72,6 +72,22 @@ final class StringInterpolationTests: XCTestCase { ) } + func testOptionalInterpolationWithNil() { + let tokenSyntax: TokenSyntax? = nil + + let funcSyntax: DeclSyntax = "func foo\(tokenSyntax)()" + XCTAssertTrue(funcSyntax.is(FunctionDeclSyntax.self)) + XCTAssertEqual(funcSyntax.description, "func foo()") + } + + func testOptionalInterpolationWithValue() { + let tokenSyntax: TokenSyntax? = .identifier("Bar") + + let funcSyntax: DeclSyntax = "func foo\(tokenSyntax)()" + XCTAssertTrue(funcSyntax.is(FunctionDeclSyntax.self)) + XCTAssertEqual(funcSyntax.description, "func fooBar()") + } + func testPatternInterpolation() { let letPattern: PatternSyntax = "let x" XCTAssertTrue(letPattern.is(ValueBindingPatternSyntax.self))