Skip to content

Commit ac4c0b0

Browse files
committed
Introduce type naming for Child nodes in CodeGeneration
1 parent f4dd574 commit ac4c0b0

File tree

12 files changed

+75
-59
lines changed

12 files changed

+75
-59
lines changed

CodeGeneration/Sources/SyntaxSupport/Child.swift

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,14 @@ public enum ChildKind {
5858
/// A child of a node, that may be declared optional or a token with a
5959
/// restricted subset of acceptable kinds or texts.
6060
public class Child {
61+
/// If the child has been renamed, its old, now deprecated, name.
62+
private let deprecatedName: String?
63+
6164
/// The name of the child.
6265
///
6366
/// The first character of the name is always uppercase.
6467
public let name: String
6568

66-
/// If the child has been renamed, its old, now deprecated, name.
67-
///
68-
/// This is used to generate deprecated compatibility layers.
69-
public let deprecatedName: String?
70-
7169
/// The kind of the child (node, token, collection, ...)
7270
public let kind: ChildKind
7371

@@ -103,6 +101,11 @@ public class Child {
103101
return .identifier(lowercaseFirstWord(name: name))
104102
}
105103

104+
/// A type of this child.
105+
public var type: TypeSyntax {
106+
return "\(raw: name.withFirstCharacterUppercased)"
107+
}
108+
106109
/// The deprecated name of this child that's suitable to be used for variable or enum case names.
107110
public var deprecatedVarName: TokenSyntax? {
108111
guard let deprecatedName = deprecatedName else {
@@ -111,6 +114,19 @@ public class Child {
111114
return .identifier(lowercaseFirstWord(name: deprecatedName))
112115
}
113116

117+
/// A deprecated type of this child.
118+
public var deprecatedType: TypeSyntax? {
119+
guard let deprecatedName = deprecatedName else {
120+
return nil
121+
}
122+
return "\(raw: deprecatedName)"
123+
}
124+
125+
/// Determines if this child has a deprecated name
126+
public var hasDeprecatedName: Bool {
127+
return deprecatedName != nil
128+
}
129+
114130
/// If the child ends with "token" in the kind, it's considered a token node.
115131
/// Grab the existing reference to that token from the global list.
116132
public var tokenKind: String? {

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ public class Node {
118118
let unexpectedDeprecatedName: String?
119119

120120
if i == 0 {
121-
unexpectedName = "UnexpectedBefore\(child.name)"
122-
unexpectedDeprecatedName = child.deprecatedName.map { "UnexpectedBefore\($0)" }
121+
unexpectedName = "UnexpectedBefore\(child.type)"
122+
unexpectedDeprecatedName = child.deprecatedType.map { "UnexpectedBefore\($0)" }
123123
} else {
124-
unexpectedName = "UnexpectedBetween\(children[i - 1].name)And\(child.name)"
125-
if let deprecatedName = children[i - 1].deprecatedName {
126-
unexpectedDeprecatedName = "UnexpectedBetween\(deprecatedName)And\(child.deprecatedName ?? child.name)"
127-
} else if let deprecatedName = child.deprecatedName {
128-
unexpectedDeprecatedName = "UnexpectedBetween\(children[i - 1].name)And\(deprecatedName)"
124+
unexpectedName = "UnexpectedBetween\(children[i - 1].type)And\(child.type)"
125+
if let deprecatedName = children[i - 1].deprecatedType {
126+
unexpectedDeprecatedName = "UnexpectedBetween\(deprecatedName)And\(child.deprecatedType ?? child.type)"
127+
} else if let deprecatedName = child.deprecatedType {
128+
unexpectedDeprecatedName = "UnexpectedBetween\(children[i - 1].type)And\(deprecatedName)"
129129
} else {
130130
unexpectedDeprecatedName = nil
131131
}
@@ -139,9 +139,9 @@ public class Node {
139139
return [unexpectedBefore, child]
140140
} + [
141141
Child(
142-
name: "UnexpectedAfter\(children.last!.name)",
143-
deprecatedName: children.last!.deprecatedName.map { "UnexpectedAfter\($0)" },
144-
kind: .collection(kind: .unexpectedNodes, collectionElementName: "UnexpectedAfter\(children.last!.name)"),
142+
name: "UnexpectedAfter\(children.last!.type)",
143+
deprecatedName: children.last!.deprecatedType.map { "UnexpectedAfter\($0)" },
144+
kind: .collection(kind: .unexpectedNodes, collectionElementName: "UnexpectedAfter\(children.last!.type)"),
145145
isOptional: true
146146
)
147147
]

CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public enum SyntaxOrTokenNodeKind: Hashable {
2626
public extension Child {
2727
/// The type of this child, represented by a ``SyntaxBuildableType``, which can
2828
/// be used to create the corresponding `Buildable` and `ExpressibleAs` types.
29-
var type: SyntaxBuildableType {
29+
var buildableType: SyntaxBuildableType {
3030
let buildableKind: SyntaxOrTokenNodeKind
3131
switch kind {
3232
case .node(kind: let kind):
@@ -44,29 +44,29 @@ public extension Child {
4444
)
4545
}
4646

47-
var parameterBaseType: String {
47+
var parameterBaseType: TypeSyntax {
4848
switch kind {
4949
case .nodeChoices:
50-
return self.name
50+
return self.type
5151
default:
52-
return type.parameterBaseType
52+
return buildableType.parameterBaseType
5353
}
5454
}
5555

5656
var parameterType: TypeSyntax {
57-
return self.type.optionalWrapped(type: IdentifierTypeSyntax(name: .identifier(parameterBaseType)))
57+
return self.buildableType.optionalWrapped(type: parameterBaseType)
5858
}
5959

6060
var defaultValue: ExprSyntax? {
6161
if isOptional || isUnexpectedNodes {
62-
if type.isBaseType && kind.isNodeChoicesEmpty {
63-
return ExprSyntax("\(type.buildable).none")
62+
if buildableType.isBaseType && kind.isNodeChoicesEmpty {
63+
return ExprSyntax("\(buildableType.buildable).none")
6464
} else {
6565
return ExprSyntax("nil")
6666
}
6767
}
6868
guard let token = token, isToken else {
69-
return type.defaultValue
69+
return buildableType.defaultValue
7070
}
7171
if token.text != nil {
7272
return ExprSyntax(".\(token.varOrCaseName)Token()")
@@ -127,7 +127,7 @@ public extension Child {
127127
}
128128

129129
var preconditionChoices: [ExprSyntax] = []
130-
if type.isOptional {
130+
if buildableType.isOptional {
131131
preconditionChoices.append(
132132
ExprSyntax(
133133
SequenceExprSyntax {
@@ -142,7 +142,7 @@ public extension Child {
142142
preconditionChoices.append(
143143
ExprSyntax(
144144
SequenceExprSyntax {
145-
MemberAccessExprSyntax(base: type.forceUnwrappedIfNeeded(expr: DeclReferenceExprSyntax(baseName: .identifier(varName))), name: "text")
145+
MemberAccessExprSyntax(base: buildableType.forceUnwrappedIfNeeded(expr: DeclReferenceExprSyntax(baseName: .identifier(varName))), name: "text")
146146
BinaryOperatorExprSyntax(text: "==")
147147
StringLiteralExprSyntax(content: textChoice)
148148
}

CodeGeneration/Sources/Utils/SyntaxBuildableType.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public struct SyntaxBuildableType: Hashable {
8585
/// - For token: ``TokenSyntax`` (tokens don't have a dedicated type in SwiftSyntaxBuilder)
8686
/// If the type is optional, the type is wrapped in an `OptionalType`.
8787
public var buildable: TypeSyntax {
88-
optionalWrapped(type: IdentifierTypeSyntax(name: .identifier(syntaxBaseName)))
88+
optionalWrapped(type: syntaxBaseName)
8989
}
9090

9191
/// Whether parameters of this type should be initializable by a result builder.
@@ -120,10 +120,10 @@ public struct SyntaxBuildableType: Hashable {
120120

121121
/// The corresponding `*Syntax` type defined in the `SwiftSyntax` module,
122122
/// without any question marks attached.
123-
public var syntaxBaseName: String {
123+
public var syntaxBaseName: TypeSyntax {
124124
switch kind {
125125
case .node(kind: let kind):
126-
return "\(kind.syntaxType)"
126+
return kind.syntaxType
127127
case .token:
128128
return "TokenSyntax"
129129
}
@@ -133,12 +133,12 @@ public struct SyntaxBuildableType: Hashable {
133133
/// which will eventually get built from `SwiftSyntaxBuilder`. If the type
134134
/// is optional, this terminates with a `?`.
135135
public var syntax: TypeSyntax {
136-
return optionalWrapped(type: IdentifierTypeSyntax(name: .identifier(syntaxBaseName)))
136+
return optionalWrapped(type: syntaxBaseName)
137137
}
138138

139139
/// The type that is used for parameters in SwiftSyntaxBuilder that take this
140140
/// type of syntax node.
141-
public var parameterBaseType: String {
141+
public var parameterBaseType: TypeSyntax {
142142
if isBaseType {
143143
return "\(syntaxBaseName)Protocol"
144144
} else {
@@ -147,7 +147,7 @@ public struct SyntaxBuildableType: Hashable {
147147
}
148148

149149
public var parameterType: TypeSyntax {
150-
return optionalWrapped(type: IdentifierTypeSyntax(name: .identifier(parameterBaseType)))
150+
return optionalWrapped(type: parameterBaseType)
151151
}
152152

153153
/// Assuming that this is a collection type, the non-optional type of the result builder

CodeGeneration/Sources/generate-swiftsyntax/LayoutNode+Extensions.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension LayoutNode {
2424
func createFunctionParameterSyntax(for child: Child) -> FunctionParameterSyntax {
2525
var paramType: TypeSyntax
2626
if !child.kind.isNodeChoicesEmpty {
27-
paramType = "\(raw: child.name)"
27+
paramType = "\(child.type)"
2828
} else if child.hasBaseType {
2929
paramType = "some \(raw: child.syntaxNodeKind.protocolType)"
3030
} else {
@@ -119,16 +119,16 @@ extension LayoutNode {
119119
childName = child.varOrCaseName
120120
}
121121

122-
if child.type.isBuilderInitializable {
122+
if child.buildableType.isBuilderInitializable {
123123
// Allow initializing certain syntax collections with result builders
124124
shouldCreateInitializer = true
125-
let builderInitializableType = child.type.builderInitializableType
126-
if child.type.builderInitializableType != child.type {
127-
let param = Node.from(type: child.type).layoutNode!.singleNonDefaultedChild
125+
let builderInitializableType = child.buildableType.builderInitializableType
126+
if child.buildableType.builderInitializableType != child.buildableType {
127+
let param = Node.from(type: child.buildableType).layoutNode!.singleNonDefaultedChild
128128
if child.isOptional {
129-
produceExpr = ExprSyntax("\(childName)Builder().map { \(raw: child.type.syntaxBaseName)(\(param.varOrCaseName): $0) }")
129+
produceExpr = ExprSyntax("\(childName)Builder().map { \(raw: child.buildableType.syntaxBaseName)(\(param.varOrCaseName): $0) }")
130130
} else {
131-
produceExpr = ExprSyntax("\(raw: child.type.syntaxBaseName)(\(param.varOrCaseName): \(childName)Builder())")
131+
produceExpr = ExprSyntax("\(raw: child.buildableType.syntaxBaseName)(\(param.varOrCaseName): \(childName)Builder())")
132132
}
133133
} else {
134134
produceExpr = ExprSyntax("\(childName)Builder()")
@@ -195,8 +195,8 @@ fileprivate func convertFromSyntaxProtocolToSyntaxType(child: Child, useDeprecat
195195
childName = child.varOrCaseName
196196
}
197197

198-
if child.type.isBaseType && !child.kind.isNodeChoices {
199-
return ExprSyntax("\(raw: child.type.syntaxBaseName)(fromProtocol: \(childName.backtickedIfNeeded))")
198+
if child.buildableType.isBaseType && !child.kind.isNodeChoices {
199+
return ExprSyntax("\(raw: child.buildableType.syntaxBaseName)(fromProtocol: \(childName.backtickedIfNeeded))")
200200
}
201201
return ExprSyntax("\(raw: childName.backtickedIfNeeded)")
202202
}

CodeGeneration/Sources/generate-swiftsyntax/templates/Array+Child.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ import SyntaxSupport
1414

1515
extension Array where Element == Child {
1616
var hasDeprecatedChild: Bool {
17-
return self.contains(where: { $0.deprecatedName != nil })
17+
return self.contains(where: { $0.hasDeprecatedName })
1818
}
1919
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserTokenSpecSetFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let parserTokenSpecSetFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2525
try EnumDeclSyntax(
2626
"""
2727
@_spi(Diagnostics)
28-
public enum \(raw: child.name)Options: TokenSpecSet
28+
public enum \(child.type)Options: TokenSpecSet
2929
"""
3030
) {
3131
for choice in choices {

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxNodesFile.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import SyntaxSupport
1616
import Utils
1717

1818
fileprivate extension Node {
19-
var childrenChoicesEnums: [(name: TokenSyntax, choices: [(caseName: TokenSyntax, kind: SyntaxNodeKind)])] {
19+
var childrenChoicesEnums: [(name: TypeSyntax, choices: [(caseName: TokenSyntax, kind: SyntaxNodeKind)])] {
2020
let node = self
2121
if let node = node.layoutNode {
22-
return node.children.compactMap { child -> (name: TokenSyntax, choices: [(caseName: TokenSyntax, kind: SyntaxNodeKind)])? in
22+
return node.children.compactMap { child -> (name: TypeSyntax, choices: [(caseName: TokenSyntax, kind: SyntaxNodeKind)])? in
2323
switch child.kind {
2424
case .nodeChoices(let choices):
25-
return (.identifier(child.name), choices.map { ($0.varOrCaseName, $0.syntaxNodeKind) })
25+
return (child.type, choices.map { ($0.varOrCaseName, $0.syntaxNodeKind) })
2626
default:
2727
return nil
2828
}
@@ -31,7 +31,7 @@ fileprivate extension Node {
3131
let choices = node.elementChoices.map { choice -> (TokenSyntax, SyntaxNodeKind) in
3232
(SYNTAX_NODE_MAP[choice]!.varOrCaseName, SYNTAX_NODE_MAP[choice]!.kind)
3333
}
34-
return [(.identifier("Element"), choices)]
34+
return [("Element", choices)]
3535
} else {
3636
return []
3737
}
@@ -238,7 +238,7 @@ let rawSyntaxNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
238238
}
239239

240240
for (index, child) in node.children.enumerated() {
241-
try VariableDeclSyntax("public var \(child.varOrCaseName.backtickedIfNeeded): Raw\(raw: child.type.buildable)") {
241+
try VariableDeclSyntax("public var \(child.varOrCaseName.backtickedIfNeeded): Raw\(raw: child.buildableType.buildable)") {
242242
let iuoMark = child.isOptional ? "" : "!"
243243

244244
if child.syntaxNodeKind == .syntax {
@@ -257,11 +257,11 @@ fileprivate extension Child {
257257
var rawParameterType: TypeSyntax {
258258
let paramType: TypeSyntax
259259
if case ChildKind.nodeChoices = kind {
260-
paramType = "\(raw: name)"
260+
paramType = type
261261
} else {
262262
paramType = syntaxNodeKind.rawType
263263
}
264264

265-
return type.optionalWrapped(type: paramType)
265+
return buildableType.optionalWrapped(type: paramType)
266266
}
267267
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RawSyntaxValidationFile.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
204204
let verifiedChoices = ArrayExprSyntax {
205205
ArrayElementSyntax(
206206
leadingTrivia: .newline,
207-
expression: ExprSyntax("verify(layout[\(raw: index)], as: Raw\(raw: child.type.buildable).self)")
207+
expression: ExprSyntax("verify(layout[\(raw: index)], as: Raw\(raw: child.buildableType.buildable).self)")
208208
)
209209
}
210210

@@ -220,10 +220,10 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
220220
}
221221
}
222222
}
223-
let verifyCall = ExprSyntax("verify(layout[\(raw: index)], as: Raw\(raw: child.type.buildable).self, tokenChoices: \(choices))")
223+
let verifyCall = ExprSyntax("verify(layout[\(raw: index)], as: Raw\(raw: child.buildableType.buildable).self, tokenChoices: \(choices))")
224224
ExprSyntax("assertNoError(kind, \(raw: index), \(verifyCall))")
225225
default:
226-
ExprSyntax("assertNoError(kind, \(raw: index), verify(layout[\(raw: index)], as: Raw\(raw: child.type.buildable).self))")
226+
ExprSyntax("assertNoError(kind, \(raw: index), verify(layout[\(raw: index)], as: Raw\(raw: child.buildableType.buildable).self))")
227227
}
228228
}
229229
} else if let node = node.collectionNode {

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ let renamedChildrenCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copy
2020
try ExtensionDeclSyntax("extension \(raw: layoutNode.type.syntaxBaseName)") {
2121
for child in layoutNode.children {
2222
if let deprecatedVarName = child.deprecatedVarName {
23-
let childType: TypeSyntax = child.kind.isNodeChoicesEmpty ? child.syntaxNodeKind.syntaxType : "\(raw: child.name)"
24-
let type = child.isOptional ? TypeSyntax("\(raw: childType)?") : TypeSyntax("\(raw: childType)")
23+
let childType: TypeSyntax = child.kind.isNodeChoicesEmpty ? child.syntaxNodeKind.syntaxType : "\(child.type)"
24+
let type = child.isOptional ? TypeSyntax("\(childType)?") : TypeSyntax("\(childType)")
2525

2626
DeclSyntax(
2727
"""
@@ -56,7 +56,7 @@ let renamedChildrenCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copy
5656
}
5757

5858
let deprecatedNames = layoutNode.children
59-
.filter { !$0.isUnexpectedNodes && $0.deprecatedName != nil }
59+
.filter { !$0.isUnexpectedNodes && $0.hasDeprecatedName }
6060
.map { $0.varOrCaseName.description }
6161
.joined(separator: ", ")
6262

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax {
102102
for child in node.children {
103103
ArrayElementSyntax(
104104
expression: MemberAccessExprSyntax(
105-
base: child.type.optionalChained(expr: ExprSyntax("\(child.varOrCaseName.backtickedIfNeeded)")),
105+
base: child.buildableType.optionalChained(expr: ExprSyntax("\(child.varOrCaseName.backtickedIfNeeded)")),
106106
period: .periodToken(),
107107
name: "raw"
108108
)
@@ -158,7 +158,7 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax {
158158
// Children properties
159159
// ===================
160160

161-
let childType: TypeSyntax = child.kind.isNodeChoicesEmpty ? child.syntaxNodeKind.syntaxType : "\(raw: child.name)"
161+
let childType: TypeSyntax = child.kind.isNodeChoicesEmpty ? child.syntaxNodeKind.syntaxType : "\(child.type)"
162162
let type = child.isOptional ? TypeSyntax("\(raw: childType)?") : TypeSyntax("\(raw: childType)")
163163

164164
try! VariableDeclSyntax(
@@ -242,7 +242,7 @@ private func generateSyntaxChildChoices(for child: Child) throws -> EnumDeclSynt
242242
return nil
243243
}
244244

245-
return try! EnumDeclSyntax("public enum \(raw: child.name): SyntaxChildChoices, SyntaxHashable") {
245+
return try! EnumDeclSyntax("public enum \(child.type): SyntaxChildChoices, SyntaxHashable") {
246246
for choice in choices {
247247
DeclSyntax("case `\(choice.varOrCaseName)`(\(raw: choice.syntaxNodeKind.syntaxType))")
248248
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/RenamedChildrenBuilderCompatibilityFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let renamedChildrenBuilderCompatibilityFile = try! SourceFileSyntax(leadingTrivi
2121
for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ $0.children.hasDeprecatedChild }) {
2222
if let convenienceInit = try layoutNode.createConvenienceBuilderInitializer(useDeprecatedChildName: true) {
2323
let deprecatedNames = layoutNode.children
24-
.filter { !$0.isUnexpectedNodes && $0.deprecatedName != nil }
24+
.filter { !$0.isUnexpectedNodes && $0.hasDeprecatedName }
2525
.compactMap { $0.varOrCaseName.description }
2626
.joined(separator: ", ")
2727

0 commit comments

Comments
 (0)