Skip to content

Commit 2d8a3cd

Browse files
committed
[CodeGeneration] improved creation of syntax nodes across raw and non-raw
- introduced `TypeConvertible`, `ParameterConvertible` and `SyntaxNodeConvertible` - introduced raw representations of `SyntaxNodeKind`, `Node` and `Child` for raw - removed `SyntaxBuildableType` - fixed the typing of raw node choices
1 parent a7b91e6 commit 2d8a3cd

File tree

53 files changed

+814
-806
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+814
-806
lines changed

CodeGeneration/Sources/SyntaxSupport/Child.swift

+85-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public enum ChildKind {
8080

8181
/// A child of a node, that may be declared optional or a token with a
8282
/// restricted subset of acceptable kinds or texts.
83-
public class Child: NodeChoiceConvertible {
83+
public class Child: SyntaxNodeConvertible, NodeChoiceConvertible, ParameterConvertible {
8484
/// The name of the child.
8585
///
8686
/// The first character of the name is always uppercase.
@@ -227,9 +227,9 @@ public class Child: NodeChoiceConvertible {
227227
case .nodeChoices(let choices):
228228
return choices.isEmpty
229229
case .node(let kind):
230-
return kind.isBase
230+
return kind.isBaseType
231231
case .collection(kind: let kind, _, _, _):
232-
return kind.isBase
232+
return kind.isBaseType
233233
case .token:
234234
return false
235235
}
@@ -244,6 +244,11 @@ public class Child: NodeChoiceConvertible {
244244
return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline)
245245
}
246246

247+
/// The ``Node`` representation of this child, if any.
248+
public var node: Node? {
249+
self.syntaxNodeKind.node
250+
}
251+
247252
/// If a classification is passed, it specifies the color identifiers in
248253
/// that subtree should inherit for syntax coloring. Must be a member of
249254
/// ``SyntaxClassification``.
@@ -273,3 +278,80 @@ public class Child: NodeChoiceConvertible {
273278
self.isOptional = isOptional
274279
}
275280
}
281+
282+
// MARK: SyntaxNodeConvertible
283+
public extension Child {
284+
var isNode: Bool {
285+
switch self.kind {
286+
case .node, .collection:
287+
return true
288+
default:
289+
return false
290+
}
291+
}
292+
293+
var syntaxType: TypeSyntax {
294+
switch self.kind {
295+
case .node(let kind), .collection(let kind, _, _, _):
296+
return kind.syntaxType
297+
case .nodeChoices:
298+
return self.syntaxChoicesType
299+
case .token:
300+
return "TokenSyntax"
301+
}
302+
}
303+
}
304+
305+
// MARK: ParameterConvertible
306+
extension Child {
307+
public var parameterAnyType: TypeSyntax {
308+
self.parameterType(specifier: "any")
309+
}
310+
311+
public var parameterSomeType: TypeSyntax {
312+
self.parameterType(specifier: "some")
313+
}
314+
315+
func parameterType(
316+
specifier: TokenSyntax,
317+
protocolType: TypeSyntax? = nil,
318+
syntaxType: TypeSyntax? = nil
319+
) -> TypeSyntax {
320+
let type: TypeSyntax
321+
if self.isBaseNode {
322+
type = "\(specifier) \(protocolType ?? self.protocolType)"
323+
} else {
324+
type = syntaxType ?? self.syntaxType
325+
}
326+
return self.isOptional ? type.optionalWrapped : type
327+
}
328+
329+
func defaultValue(syntaxType: TypeSyntax) -> ExprSyntax? {
330+
guard !self.isOptional else {
331+
if self.isBaseNode {
332+
return "\(syntaxType.optionalWrapped).none"
333+
} else {
334+
return "nil"
335+
}
336+
}
337+
if case .collection(_, _, defaultsToEmpty: true, _) = self.kind {
338+
return "[]"
339+
}
340+
guard let token else {
341+
return self.isOptional ? "nil" : nil
342+
}
343+
guard token.text == nil else {
344+
return ".\(token.identifier)Token()"
345+
}
346+
guard case .token(let choices, _, _) = self.kind,
347+
case .keyword(let keyword) = choices.only
348+
else {
349+
return nil
350+
}
351+
return ".\(token.memberCallName)(.\(keyword.spec.memberCallName))"
352+
}
353+
354+
public var defaultValue: ExprSyntax? {
355+
self.defaultValue(syntaxType: self.syntaxType)
356+
}
357+
}

CodeGeneration/Sources/SyntaxSupport/Node.swift

+14-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import SwiftSyntax
2121
/// but fixed types.
2222
/// - Collection nodes contains an arbitrary number of children but all those
2323
/// children are of the same type.
24-
public class Node: NodeChoiceConvertible {
24+
public class Node: SyntaxNodeConvertible, NodeChoiceConvertible {
2525
fileprivate enum Data {
2626
case layout(children: [Child], traits: [String])
2727
case collection(choices: [SyntaxNodeKind])
@@ -38,7 +38,7 @@ public class Node: NodeChoiceConvertible {
3838
public let kind: SyntaxNodeKind
3939

4040
/// The kind of node’s supertype. This kind must have `isBase == true`
41-
public var base: SyntaxNodeKind {
41+
public var baseKind: SyntaxNodeKind {
4242
self.kind.base
4343
}
4444

@@ -57,6 +57,10 @@ public class Node: NodeChoiceConvertible {
5757
/// function that should be invoked to create this node.
5858
public let parserFunction: TokenSyntax?
5959

60+
public let isOptional = false
61+
62+
public let isNode = true
63+
6064
public var syntaxNodeKind: SyntaxNodeKind {
6165
self.kind
6266
}
@@ -71,7 +75,7 @@ public class Node: NodeChoiceConvertible {
7175
public var layoutNode: LayoutNode? {
7276
switch data {
7377
case .layout:
74-
if kind.isBase {
78+
if kind.isBaseType {
7579
return nil
7680
} else {
7781
return LayoutNode(node: self)
@@ -127,7 +131,7 @@ public class Node: NodeChoiceConvertible {
127131
children: [Child] = []
128132
) {
129133
precondition(kind.base != .syntaxCollection)
130-
precondition(kind.base.isBase, "unknown base kind '\(kind.base)' for node '\(kind)'")
134+
precondition(kind.base.isBaseType, "unknown base kind '\(kind.base)' for node '\(kind)'")
131135

132136
self.kind = kind
133137
self.experimentalFeature = experimentalFeature
@@ -219,18 +223,14 @@ public class Node: NodeChoiceConvertible {
219223

220224
let list =
221225
childIn
222-
.map {
223-
if let childName = $0.child?.identifier {
226+
.map { (node, child) in
227+
if let childName = child?.identifier {
224228
// This will repeat the syntax type before and after the dot, which is
225229
// a little unfortunate, but it's the only way I found to get docc to
226230
// generate a fully-qualified type + member.
227-
if $0.node.isAvailableInDocc {
228-
return " - \($0.node.doccLink).``\($0.node.syntaxType)/\(childName)``"
229-
} else {
230-
return " - \($0.node.doccLink).`\($0.node.syntaxType)/\(childName)`"
231-
}
231+
return " - \(node.doccLink).\(node.doccLink(content: "\(node.syntaxType)/\(childName)"))"
232232
} else {
233-
return " - \($0.node.doccLink)"
233+
return " - \(node.doccLink)"
234234
}
235235
}
236236
.joined(separator: "\n")
@@ -252,8 +252,8 @@ public class Node: NodeChoiceConvertible {
252252

253253
let list =
254254
SYNTAX_NODES
255-
.filter { $0.base == self.kind && !$0.isExperimental && !$0.kind.isDeprecated }
256-
.map { "- \($0.kind.doccLink)" }
255+
.filter { $0.baseKind == self.kind && !$0.isExperimental && !$0.isDeprecated }
256+
.map { "- \($0.doccLink)" }
257257
.joined(separator: "\n")
258258

259259
guard !list.isEmpty else {

CodeGeneration/Sources/SyntaxSupport/NodeChoiceConvertible.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import SwiftSyntax
1414

1515
/// Instances of a conforming type should provide necessary information for generating code of a node choice.
1616
public protocol NodeChoiceConvertible: IdentifierConvertible {
17+
associatedtype Kind: SyntaxNodeKindProtocol
18+
1719
/// A docc comment describing the syntax node convertible, including the trivia provided when
1820
/// initializing the syntax node convertible, and the list of possible token choices inferred automatically.
1921
var documentation: SwiftSyntax.Trivia { get }
@@ -29,7 +31,7 @@ public protocol NodeChoiceConvertible: IdentifierConvertible {
2931
var apiAttributes: AttributeListSyntax { get }
3032

3133
/// The kind of the syntax node convertible.
32-
var syntaxNodeKind: SyntaxNodeKind { get }
34+
var syntaxNodeKind: Kind { get }
3335
}
3436

3537
public extension NodeChoiceConvertible {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
/// Implementations should provide necessary information for generating code of a parameter.
16+
public protocol ParameterConvertible {
17+
/// The type that is used for parameters in SwiftSyntaxBuilder that take this
18+
/// type of syntax node and expect an existential type if the parameter type is a protocol.
19+
var parameterAnyType: TypeSyntax {
20+
get
21+
}
22+
23+
/// The type that is used for parameters in SwiftSyntaxBuilder that take this
24+
/// type of syntax node and expect a generic type if the parameter type is a protocol.
25+
var parameterSomeType: TypeSyntax {
26+
get
27+
}
28+
29+
/// If the type has a default value (because it is optional or a token
30+
/// with fixed test), return an expression that can be used as the
31+
/// default value for a function parameter. Otherwise, return `nil`.
32+
var defaultValue: ExprSyntax? {
33+
get
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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 Child {
16+
/// The raw representation of ``Child``.
17+
struct Raw: SyntaxNodeConvertible, NodeChoiceConvertible, ParameterConvertible {
18+
var child: Child
19+
20+
public var isOptional: Bool {
21+
self.child.isOptional
22+
}
23+
24+
public var isNode: Bool {
25+
self.child.isNode
26+
}
27+
28+
public var syntaxType: TypeSyntax {
29+
switch self.child.kind {
30+
case .node(let kind), .collection(let kind, _, _, _):
31+
return kind.raw.syntaxType
32+
case .nodeChoices:
33+
return self.child.syntaxChoicesType
34+
case .token:
35+
return "RawTokenSyntax"
36+
}
37+
}
38+
39+
public var syntaxNodeKind: SyntaxNodeKind.Raw {
40+
self.child.syntaxNodeKind.raw
41+
}
42+
43+
public var documentation: SwiftSyntax.Trivia {
44+
self.child.documentation
45+
}
46+
47+
public var experimentalFeature: ExperimentalFeature? {
48+
self.child.experimentalFeature
49+
}
50+
51+
public var apiAttributes: AttributeListSyntax {
52+
self.child.apiAttributes
53+
}
54+
55+
public var identifier: TokenSyntax {
56+
self.child.identifier
57+
}
58+
59+
public var parameterAnyType: TypeSyntax {
60+
self.child.parameterType(specifier: "any", protocolType: self.protocolType, syntaxType: self.syntaxType)
61+
}
62+
63+
public var parameterSomeType: TypeSyntax {
64+
if self.child.isBaseNode && !self.child.isOptional {
65+
// we restrict the use of generic type to non-optional parameter types, otherwise call sites would no longer be
66+
// able to just pass `nil` to this parameter without specializing `(some Raw<Kind>SyntaxNodeProtocol)?`
67+
//
68+
// we've opted out of providing a default value to the parameter (e.g. `RawExprSyntax?.none`) as a workaround,
69+
// as passing an explicit `nil` would prompt developers to think clearly whether this parameter should be parsed
70+
return "some \(self.protocolType)"
71+
} else {
72+
return self.actualType
73+
}
74+
}
75+
76+
public var defaultValue: ExprSyntax? {
77+
self.child.defaultValue(syntaxType: self.syntaxType)
78+
}
79+
}
80+
81+
/// The raw representation of this child.
82+
var raw: Raw {
83+
Raw(child: self)
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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 Node {
16+
/// The raw representation of ``Node``. The definition of a syntax node that, when generated, conforms to
17+
/// `RawSyntaxNodeProtocol`.
18+
struct Raw: SyntaxNodeConvertible, NodeChoiceConvertible {
19+
var node: Node
20+
21+
public var isOptional: Bool {
22+
self.node.isOptional
23+
}
24+
25+
public var syntaxNodeKind: SyntaxNodeKind.Raw {
26+
self.node.syntaxNodeKind.raw
27+
}
28+
29+
public var isNode: Bool {
30+
self.node.isNode
31+
}
32+
33+
public var documentation: SwiftSyntax.Trivia {
34+
self.node.documentation
35+
}
36+
37+
public var experimentalFeature: ExperimentalFeature? {
38+
self.node.experimentalFeature
39+
}
40+
41+
public var apiAttributes: AttributeListSyntax {
42+
self.node.apiAttributes
43+
}
44+
45+
public var identifier: TokenSyntax {
46+
self.node.identifier
47+
}
48+
}
49+
50+
/// The raw representation of this node.
51+
var raw: Raw {
52+
Raw(node: self)
53+
}
54+
}

0 commit comments

Comments
 (0)