Skip to content

Commit c937cb7

Browse files
authored
Merge pull request #2168 from natikgadzhi/codegeneration/children-token-choices
List the token kinds in child documentation in SyntaxNodes
2 parents 5ff9344 + 4b76a66 commit c937cb7

18 files changed

+1956
-384
lines changed

CodeGeneration/Sources/SyntaxSupport/Child.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,25 @@ public class Child {
8787
/// This is used to e.g. describe the child if all of its tokens are missing in the source file.
8888
public let nameForDiagnostics: String?
8989

90-
/// A doc comment describing the child.
91-
public let documentation: SwiftSyntax.Trivia
90+
/// A summary of a doc comment describing the child. Full docc comment for the
91+
/// child is available in ``Child/documentation``, and includes detailed list
92+
/// of possible choices for the child if it's a token kind.
93+
public let documentationSummary: SwiftSyntax.Trivia
94+
95+
/// A docc comment describing the child, including the trivia provided when
96+
/// initializing the ``Child``, and the list of possible token choices inferred automatically.
97+
public var documentation: SwiftSyntax.Trivia {
98+
if case .token(let choices, _, _) = kind {
99+
let tokenChoicesTrivia = SwiftSyntax.Trivia.docCommentTrivia(
100+
from: GrammarGenerator.childTokenChoices(for: choices)
101+
)
102+
103+
return SwiftSyntax.Trivia(joining: [documentationSummary, tokenChoicesTrivia])
104+
}
105+
106+
// If this child is not a token kind, return documentation summary without the choices list.
107+
return documentationSummary
108+
}
92109

93110
/// The first line of the child's documentation
94111
public let documentationAbstract: String
@@ -220,7 +237,7 @@ public class Child {
220237
self.deprecatedName = deprecatedName
221238
self.kind = kind
222239
self.nameForDiagnostics = nameForDiagnostics
223-
self.documentation = docCommentTrivia(from: documentation)
240+
self.documentationSummary = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
224241
self.documentationAbstract = String(documentation?.split(whereSeparator: \.isNewline).first ?? "")
225242
self.isOptional = isOptional
226243
}

CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ import SwiftSyntax
1414

1515
/// Generates grammar doc comments for syntax nodes.
1616
struct GrammarGenerator {
17+
18+
/// Returns grammar for a ``TokenChoice``.
19+
///
20+
/// - parameters:
21+
/// - tokenChoice: ``TokenChoice`` to describe
1722
private func grammar(for tokenChoice: TokenChoice) -> String {
1823
switch tokenChoice {
1924
case .keyword(let keyword):
20-
return "`'\(keyword.spec.name)'`"
25+
return "`\(keyword.spec.name)`"
2126
case .token(let token):
2227
let tokenSpec = token.spec
2328
if let tokenText = tokenSpec.text {
24-
return "`'\(tokenText)'`"
29+
return "`\(tokenText)`"
2530
} else {
2631
return "`<\(tokenSpec.varOrCaseName)>`"
2732
}
@@ -60,4 +65,25 @@ struct GrammarGenerator {
6065
.map { " - `\($0.varOrCaseName)`: \(generator.grammar(for: $0))" }
6166
.joined(separator: "\n")
6267
}
68+
69+
/// Generates a markdown string describing possible choices for the given child
70+
/// token.
71+
static func childTokenChoices(for choices: [TokenChoice]) -> String {
72+
let grammar = GrammarGenerator()
73+
74+
if choices.count == 1 {
75+
return """
76+
### Tokens
77+
78+
For syntax trees generated by the parser, this is guaranteed to be \(grammar.grammar(for: choices.first!)).
79+
"""
80+
} else {
81+
return """
82+
### Tokens
83+
84+
For syntax trees generated by the parser, this is guaranteed to be one of the following kinds:
85+
\(choices.map { " - \(grammar.grammar(for: $0))" }.joined(separator: "\n"))
86+
"""
87+
}
88+
}
6389
}

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public class Node {
121121
self.base = base
122122
self.isExperimental = isExperimental
123123
self.nameForDiagnostics = nameForDiagnostics
124-
self.documentation = docCommentTrivia(from: documentation)
124+
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
125125
self.parserFunction = parserFunction
126126

127127
let childrenWithUnexpected: [Child]
@@ -211,7 +211,7 @@ public class Node {
211211
}
212212
.joined(separator: "\n")
213213

214-
return docCommentTrivia(
214+
return .docCommentTrivia(
215215
from: """
216216
### Contained in
217217
@@ -237,7 +237,7 @@ public class Node {
237237
self.base = base
238238
self.isExperimental = isExperimental
239239
self.nameForDiagnostics = nameForDiagnostics
240-
self.documentation = docCommentTrivia(from: documentation)
240+
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
241241
self.parserFunction = parserFunction
242242

243243
assert(!elementChoices.isEmpty)
@@ -299,7 +299,7 @@ public struct LayoutNode {
299299
return []
300300
}
301301

302-
return docCommentTrivia(
302+
return .docCommentTrivia(
303303
from: """
304304
### Children
305305
@@ -352,7 +352,7 @@ public struct CollectionNode {
352352
grammar = "(\(elementChoices.map { "``\($0.syntaxType)``" }.joined(separator: " | "))) `*`"
353353
}
354354

355-
return docCommentTrivia(
355+
return .docCommentTrivia(
356356
from: """
357357
### Children
358358

CodeGeneration/Sources/SyntaxSupport/Traits.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Trait {
2121
init(traitName: String, documentation: String? = nil, children: [Child]) {
2222
self.traitName = traitName
2323
self.protocolName = .identifier("\(traitName)Syntax")
24-
self.documentation = docCommentTrivia(from: documentation)
24+
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
2525
self.children = children
2626
}
2727
}

CodeGeneration/Sources/SyntaxSupport/Utils.swift

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,40 @@ public func lowercaseFirstWord(name: String) -> String {
3838
return name.prefix(wordIndex).lowercased() + name[name.index(name.startIndex, offsetBy: wordIndex)..<name.endIndex]
3939
}
4040

41-
/// Give a (possibly multi-line) string, prepends `///` to each line and creates
42-
/// a `.docLineComment` trivia piece for each line.
43-
public func docCommentTrivia(from string: String?) -> SwiftSyntax.Trivia {
44-
guard let string else {
45-
return []
46-
}
47-
let lines = string.split(separator: "\n", omittingEmptySubsequences: false)
48-
let pieces = lines.enumerated().map { (index, line) in
49-
var line = line
50-
if index != lines.count - 1 {
51-
line = "\(line)\n"
41+
// Helpers to create trivia pieces
42+
extension SwiftSyntax.Trivia {
43+
/// Make a new trivia from a (possibly multi-line) string, prepending `///`
44+
/// to each line and creating a `.docLineComment` trivia piece for each line.
45+
public static func docCommentTrivia(from string: String?) -> SwiftSyntax.Trivia {
46+
guard let string else {
47+
return []
48+
}
49+
50+
let lines = string.split(separator: "\n", omittingEmptySubsequences: false)
51+
let pieces = lines.enumerated().map { (index, line) in
52+
var line = line
53+
if index != lines.count - 1 {
54+
line = "\(line)\n"
55+
}
56+
return SwiftSyntax.TriviaPiece.docLineComment("/// \(line)")
5257
}
53-
return SwiftSyntax.TriviaPiece.docLineComment("/// \(line)")
58+
return SwiftSyntax.Trivia(pieces: pieces)
59+
}
60+
61+
/// Make a new trivia by joining together ``SwiftSyntax/TriviaPiece``s from `joining`,
62+
/// and gluing them together with pieces from `separator`.
63+
public init(
64+
joining items: [SwiftSyntax.Trivia],
65+
separator: SwiftSyntax.Trivia = SwiftSyntax.Trivia(pieces: [TriviaPiece.newlines(1), TriviaPiece.docLineComment("///"), TriviaPiece.newlines(1)])
66+
) {
67+
68+
self.init(
69+
pieces:
70+
items
71+
.filter { !$0.isEmpty }
72+
.joined(separator: separator)
73+
)
5474
}
55-
return SwiftSyntax.Trivia(pieces: pieces)
5675
}
5776

5877
public extension Collection {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ extension LayoutNode {
9292
If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
9393
""".removingEmptyLines
9494

95-
return docCommentTrivia(from: formattedParams)
95+
return SwiftSyntax.Trivia.docCommentTrivia(from: formattedParams)
9696
}
9797

9898
/// Create a builder-based convenience initializer, if needed.

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxCollectionsFile.swift

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,11 @@ import Utils
1717

1818
let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
for node in SYNTAX_NODES.compactMap(\.collectionNode) {
20-
let documentationSections = [
20+
let documentation = SwiftSyntax.Trivia(joining: [
2121
node.documentation,
2222
node.grammar,
2323
node.containedIn,
24-
]
25-
26-
let documentation =
27-
documentationSections
28-
.filter { !$0.isEmpty }
29-
.map { [$0] }
30-
.joined(separator: [Trivia.newline, Trivia.docLineComment("///"), Trivia.newline])
31-
.reduce(Trivia(), +)
24+
])
3225

3326
try! StructDeclSyntax(
3427
"""

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,12 @@ import Utils
2222
func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax {
2323
SourceFileSyntax(leadingTrivia: copyrightHeader) {
2424
for node in SYNTAX_NODES.compactMap(\.layoutNode) where nodesStartingWith.contains(node.kind.syntaxType.description.first!) {
25-
let documentationSections = [
26-
node.documentation,
27-
node.grammar,
28-
node.containedIn,
29-
]
30-
let documentation =
31-
documentationSections
32-
.filter { !$0.isEmpty }
33-
.map { [$0] }
34-
.joined(separator: [Trivia.newline, Trivia.docLineComment("///"), Trivia.newline])
35-
.reduce(Trivia(), +)
36-
3725
// We are actually handling this node now
3826
try! StructDeclSyntax(
3927
"""
4028
// MARK: - \(node.kind.syntaxType)
4129
42-
\(documentation)
30+
\(SwiftSyntax.Trivia(joining: [node.documentation, node.grammar, node.containedIn]))
4331
\(node.node.apiAttributes())\
4432
public struct \(node.kind.syntaxType): \(node.baseType.syntaxBaseName)Protocol, SyntaxHashable, \(node.base.leafProtocolType)
4533
"""

Sources/SwiftSyntax/generated/SyntaxTraits.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616

1717

1818
public protocol BracedSyntax: SyntaxProtocol {
19+
/// ### Tokens
20+
///
21+
/// For syntax trees generated by the parser, this is guaranteed to be `{`.
1922
var leftBrace: TokenSyntax {
2023
get
2124
set
2225
}
2326

27+
/// ### Tokens
28+
///
29+
/// For syntax trees generated by the parser, this is guaranteed to be `}`.
2430
var rightBrace: TokenSyntax {
2531
get
2632
set
@@ -121,6 +127,11 @@ public protocol EffectSpecifiersSyntax: SyntaxProtocol {
121127
set
122128
}
123129

130+
/// ### Tokens
131+
///
132+
/// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds:
133+
/// - `async`
134+
/// - `reasync`
124135
var asyncSpecifier: TokenSyntax? {
125136
get
126137
set
@@ -131,6 +142,11 @@ public protocol EffectSpecifiersSyntax: SyntaxProtocol {
131142
set
132143
}
133144

145+
/// ### Tokens
146+
///
147+
/// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds:
148+
/// - `throws`
149+
/// - `rethrows`
134150
var throwsSpecifier: TokenSyntax? {
135151
get
136152
set
@@ -173,11 +189,17 @@ public extension SyntaxProtocol {
173189

174190

175191
public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
192+
/// ### Tokens
193+
///
194+
/// For syntax trees generated by the parser, this is guaranteed to be `#`.
176195
var pound: TokenSyntax {
177196
get
178197
set
179198
}
180199

200+
/// ### Tokens
201+
///
202+
/// For syntax trees generated by the parser, this is guaranteed to be `<identifier>`.
181203
var macroName: TokenSyntax {
182204
get
183205
set
@@ -188,6 +210,9 @@ public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
188210
set
189211
}
190212

213+
/// ### Tokens
214+
///
215+
/// For syntax trees generated by the parser, this is guaranteed to be `(`.
191216
var leftParen: TokenSyntax? {
192217
get
193218
set
@@ -198,6 +223,9 @@ public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
198223
set
199224
}
200225

226+
/// ### Tokens
227+
///
228+
/// For syntax trees generated by the parser, this is guaranteed to be `)`.
201229
var rightParen: TokenSyntax? {
202230
get
203231
set
@@ -245,6 +273,9 @@ public extension SyntaxProtocol {
245273

246274

247275
public protocol NamedDeclSyntax: SyntaxProtocol {
276+
/// ### Tokens
277+
///
278+
/// For syntax trees generated by the parser, this is guaranteed to be `<identifier>`.
248279
var name: TokenSyntax {
249280
get
250281
set
@@ -285,6 +316,10 @@ public extension SyntaxProtocol {
285316
/// See the types conforming to this protocol for examples of where missing nodes can occur.
286317
public protocol MissingNodeSyntax: SyntaxProtocol {
287318
/// A placeholder, i.e. `<#placeholder#>`, that can be inserted into the source code to represent the missing node.
319+
///
320+
/// ### Tokens
321+
///
322+
/// For syntax trees generated by the parser, this is guaranteed to be `<identifier>`.
288323
var placeholder: TokenSyntax {
289324
get
290325
set
@@ -322,11 +357,17 @@ public extension SyntaxProtocol {
322357

323358

324359
public protocol ParenthesizedSyntax: SyntaxProtocol {
360+
/// ### Tokens
361+
///
362+
/// For syntax trees generated by the parser, this is guaranteed to be `(`.
325363
var leftParen: TokenSyntax {
326364
get
327365
set
328366
}
329367

368+
/// ### Tokens
369+
///
370+
/// For syntax trees generated by the parser, this is guaranteed to be `)`.
330371
var rightParen: TokenSyntax {
331372
get
332373
set
@@ -558,6 +599,9 @@ public extension SyntaxProtocol {
558599

559600

560601
public protocol WithTrailingCommaSyntax: SyntaxProtocol {
602+
/// ### Tokens
603+
///
604+
/// For syntax trees generated by the parser, this is guaranteed to be `,`.
561605
var trailingComma: TokenSyntax? {
562606
get
563607
set

0 commit comments

Comments
 (0)