Skip to content

Commit 3bf5a7f

Browse files
committed
List the token kinds in child documentation
For children of syntax nodes that are TokenSyntax, list the kinds of tokens it can contain. Fixes #1987.
1 parent 669eb75 commit 3bf5a7f

12 files changed

+680
-97
lines changed

CodeGeneration/Sources/SyntaxSupport/Child.swift

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,27 @@ 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+
var tokenKindsComment = choices.count == 1 ?
100+
"For syntax trees generated by the parser, this is guaranteed to be the following kind:" :
101+
"For syntax trees generated by the parser, this is guaranteed to be one of the following token kinds:\n"
102+
tokenKindsComment += GrammarGenerator.childTokenChoices(for: self)
103+
104+
let trivia = docCommentTrivia(from: tokenKindsComment)
105+
return documentationSummary.appending(trivia)
106+
}
107+
108+
// If this child is not a token kind, return documentation summary without the choices list.
109+
return documentationSummary
110+
}
92111

93112
/// The first line of the child's documentation
94113
public let documentationAbstract: String
@@ -220,7 +239,7 @@ public class Child {
220239
self.deprecatedName = deprecatedName
221240
self.kind = kind
222241
self.nameForDiagnostics = nameForDiagnostics
223-
self.documentation = docCommentTrivia(from: documentation)
242+
self.documentationSummary = docCommentTrivia(from: documentation)
224243
self.documentationAbstract = String(documentation?.split(whereSeparator: \.isNewline).first ?? "")
225244
self.isOptional = isOptional
226245
}

CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,27 @@ import SwiftSyntax
1414

1515
/// Generates grammar doc comments for syntax nodes.
1616
struct GrammarGenerator {
17-
private func grammar(for tokenChoice: TokenChoice) -> String {
17+
18+
/// Returns grammar for a ``TokenChoice``.
19+
///
20+
/// - parameters:
21+
/// - tokenChoice: ``TokenChoice`` to describe
22+
/// - backticks: Whether to wrap the token choice in backticks
23+
private func grammar(for tokenChoice: TokenChoice, backticked: Bool = true) -> String {
24+
var description: String
1825
switch tokenChoice {
1926
case .keyword(let keyword):
20-
return "`'\(keyword.spec.name)'`"
27+
description = "'\(keyword.spec.name)'"
2128
case .token(let token):
2229
let tokenSpec = token.spec
2330
if let tokenText = tokenSpec.text {
24-
return "`'\(tokenText)'`"
31+
description = "'\(tokenText)'"
2532
} else {
26-
return "`<\(tokenSpec.varOrCaseName)>`"
33+
description = "<\(tokenSpec.varOrCaseName)>"
2734
}
2835
}
36+
37+
return backticked ? "`\(description)`" : description
2938
}
3039

3140
private func grammar(for child: Child) -> String {
@@ -60,4 +69,20 @@ struct GrammarGenerator {
6069
.map { " - `\($0.varOrCaseName)`: \(generator.grammar(for: $0))" }
6170
.joined(separator: "\n")
6271
}
72+
73+
/// Generates the list of possible token kinds for this particular ``Child``.
74+
/// The child must be of kind ``ChildKind/token``. Otherwise, an empty string will be returned.
75+
static func childTokenChoices(for child: Child) -> String {
76+
let generator = GrammarGenerator()
77+
78+
if case .token(let choices, _, _) = child.kind {
79+
if choices.count == 1 {
80+
return " \(generator.grammar(for: choices.first!, backticked: false))"
81+
} else {
82+
return choices.map { " - \(generator.grammar(for: $0, backticked: false))" }.joined(separator: "\n")
83+
}
84+
} else {
85+
return ""
86+
}
87+
}
6388
}

Sources/SwiftSyntax/generated/SyntaxTraits.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717

1818
public protocol BracedSyntax: SyntaxProtocol {
19+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: '{'
1920
var leftBrace: TokenSyntax {
2021
get
2122
set
2223
}
2324

25+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: '}'
2426
var rightBrace: TokenSyntax {
2527
get
2628
set
@@ -121,6 +123,9 @@ public protocol EffectSpecifiersSyntax: SyntaxProtocol {
121123
set
122124
}
123125

126+
/// For syntax trees generated by the parser, this is guaranteed to be one of the following token kinds:
127+
/// - 'async'
128+
/// - 'reasync'
124129
var asyncSpecifier: TokenSyntax? {
125130
get
126131
set
@@ -131,6 +136,9 @@ public protocol EffectSpecifiersSyntax: SyntaxProtocol {
131136
set
132137
}
133138

139+
/// For syntax trees generated by the parser, this is guaranteed to be one of the following token kinds:
140+
/// - 'throws'
141+
/// - 'rethrows'
134142
var throwsSpecifier: TokenSyntax? {
135143
get
136144
set
@@ -173,11 +181,13 @@ public extension SyntaxProtocol {
173181

174182

175183
public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
184+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: '#'
176185
var pound: TokenSyntax {
177186
get
178187
set
179188
}
180189

190+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: <identifier>
181191
var macroName: TokenSyntax {
182192
get
183193
set
@@ -188,6 +198,7 @@ public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
188198
set
189199
}
190200

201+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: '('
191202
var leftParen: TokenSyntax? {
192203
get
193204
set
@@ -198,6 +209,7 @@ public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol {
198209
set
199210
}
200211

212+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: ')'
201213
var rightParen: TokenSyntax? {
202214
get
203215
set
@@ -245,6 +257,7 @@ public extension SyntaxProtocol {
245257

246258

247259
public protocol NamedDeclSyntax: SyntaxProtocol {
260+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: <identifier>
248261
var name: TokenSyntax {
249262
get
250263
set
@@ -284,7 +297,7 @@ public extension SyntaxProtocol {
284297
///
285298
/// See the types conforming to this protocol for examples of where missing nodes can occur.
286299
public protocol MissingNodeSyntax: SyntaxProtocol {
287-
/// A placeholder, i.e. `<#placeholder#>`, that can be inserted into the source code to represent the missing node.
300+
/// A placeholder, i.e. `<#placeholder#>`, that can be inserted into the source code to represent the missing node./// For syntax trees generated by the parser, this is guaranteed to be the following kind: <identifier>
288301
var placeholder: TokenSyntax {
289302
get
290303
set
@@ -322,11 +335,13 @@ public extension SyntaxProtocol {
322335

323336

324337
public protocol ParenthesizedSyntax: SyntaxProtocol {
338+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: '('
325339
var leftParen: TokenSyntax {
326340
get
327341
set
328342
}
329343

344+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: ')'
330345
var rightParen: TokenSyntax {
331346
get
332347
set
@@ -558,6 +573,7 @@ public extension SyntaxProtocol {
558573

559574

560575
public protocol WithTrailingCommaSyntax: SyntaxProtocol {
576+
/// For syntax trees generated by the parser, this is guaranteed to be the following kind: ','
561577
var trailingComma: TokenSyntax? {
562578
get
563579
set

0 commit comments

Comments
 (0)