Skip to content

Commit c3f46ad

Browse files
authored
Merge pull request #2041 from hamishknight/experimental
2 parents 777520e + eef462c commit c3f46ad

File tree

144 files changed

+655
-375
lines changed

Some content is hidden

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

144 files changed

+655
-375
lines changed

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import SwiftSyntax
14+
1315
public struct KeywordSpec {
1416
public var name: String
17+
18+
/// If `true`, this is for an experimental language feature, and any public
19+
/// API generated should be SPI.
20+
public var isExperimental: Bool
21+
1522
public var isLexerClassified: Bool
1623
public var requiresLeadingSpace: Bool
1724
public var requiresTrailingSpace: Bool
@@ -24,10 +31,18 @@ public struct KeywordSpec {
2431
}
2532
}
2633

34+
/// Retrieve the attributes that should be printed on any API for the
35+
/// generated keyword.
36+
public var apiAttributes: AttributeListSyntax {
37+
guard isExperimental else { return "" }
38+
return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline)
39+
}
40+
2741
/// `isLexerClassified` determines whether the token kind is switched from being an identifier to a keyword in the lexer.
2842
/// This is true for keywords that used to be considered non-contextual.
29-
init(_ name: String, isLexerClassified: Bool = false, requiresLeadingSpace: Bool = false, requiresTrailingSpace: Bool = false) {
43+
init(_ name: String, isExperimental: Bool = false, isLexerClassified: Bool = false, requiresLeadingSpace: Bool = false, requiresTrailingSpace: Bool = false) {
3044
self.name = name
45+
self.isExperimental = isExperimental
3146
self.isLexerClassified = isLexerClassified
3247
self.requiresLeadingSpace = requiresLeadingSpace
3348
self.requiresTrailingSpace = requiresTrailingSpace

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public class Node {
4040
/// The kind of node’s supertype. This kind must have `isBase == true`
4141
public let base: SyntaxNodeKind
4242

43+
/// If `true`, this is for an experimental language feature, and any public
44+
/// API generated should be SPI.
45+
public let isExperimental: Bool
46+
4347
/// When the node name is printed for diagnostics, this name is used.
4448
/// If `nil`, `nameForDiagnostics` will print the parent node’s name.
4549
public let nameForDiagnostics: String?
@@ -85,10 +89,25 @@ public class Node {
8589
}
8690
}
8791

92+
/// Retrieve the attributes that should be printed on any API for the
93+
/// generated node. If `forRaw` is true, this is for the raw syntax node.
94+
public func apiAttributes(forRaw: Bool = false) -> AttributeListSyntax {
95+
let attrList = AttributeListSyntax {
96+
if isExperimental {
97+
"@_spi(ExperimentalLanguageFeatures)"
98+
}
99+
if forRaw {
100+
"@_spi(RawSyntax)"
101+
}
102+
}
103+
return attrList.with(\.trailingTrivia, attrList.isEmpty ? [] : .newline)
104+
}
105+
88106
/// Construct the specification for a layout syntax node.
89107
init(
90108
kind: SyntaxNodeKind,
91109
base: SyntaxNodeKind,
110+
isExperimental: Bool = false,
92111
nameForDiagnostics: String?,
93112
documentation: String? = nil,
94113
parserFunction: String? = nil,
@@ -100,6 +119,7 @@ public class Node {
100119

101120
self.kind = kind
102121
self.base = base
122+
self.isExperimental = isExperimental
103123
self.nameForDiagnostics = nameForDiagnostics
104124
self.documentation = docCommentTrivia(from: documentation)
105125
self.parserFunction = parserFunction
@@ -204,6 +224,7 @@ public class Node {
204224
init(
205225
kind: SyntaxNodeKind,
206226
base: SyntaxNodeKind,
227+
isExperimental: Bool = false,
207228
nameForDiagnostics: String?,
208229
documentation: String? = nil,
209230
parserFunction: String? = nil,
@@ -212,6 +233,7 @@ public class Node {
212233
self.kind = kind
213234
precondition(base == .syntaxCollection)
214235
self.base = base
236+
self.isExperimental = isExperimental
215237
self.nameForDiagnostics = nameForDiagnostics
216238
self.documentation = docCommentTrivia(from: documentation)
217239
self.parserFunction = parserFunction

CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,36 @@ public struct TokenSpec {
2222
}
2323

2424
public let varOrCaseName: TokenSyntax
25+
26+
/// If `true`, this is for an experimental language feature, and any public
27+
/// API generated should be SPI.
28+
public let isExperimental: Bool
29+
2530
public let nameForDiagnostics: String
2631
public let text: String?
2732
public let kind: Kind
2833

2934
fileprivate init(
3035
name: String,
36+
isExperimental: Bool = false,
3137
nameForDiagnostics: String,
3238
text: String? = nil,
3339
kind: Kind
3440
) {
3541
self.varOrCaseName = .identifier(name)
42+
self.isExperimental = isExperimental
3643
self.nameForDiagnostics = nameForDiagnostics
3744
self.text = text
3845
self.kind = kind
3946
}
4047

48+
/// Retrieve the attributes that should be printed on any API for the
49+
/// generated token.
50+
public var apiAttributes: AttributeListSyntax {
51+
guard isExperimental else { return "" }
52+
return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline)
53+
}
54+
4155
static func punctuator(name: String, text: String) -> TokenSpec {
4256
return TokenSpec(
4357
name: name,

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparserdiagnostics/SyntaxKindNameForDiagnosticsFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let syntaxKindNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax("import SwiftSyntax")
19+
DeclSyntax("@_spi(ExperimentalLanguageFeatures) import SwiftSyntax")
2020

2121
try! ExtensionDeclSyntax("extension SyntaxKind") {
2222
try VariableDeclSyntax("var nameForDiagnostics: String?") {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ let keywordFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2828
"""
2929
) {
3030
for (index, keyword) in KEYWORDS.enumerated() {
31-
DeclSyntax("case \(raw: keyword.escapedName)")
31+
DeclSyntax(
32+
"""
33+
\(keyword.apiAttributes)\
34+
case \(raw: keyword.escapedName)
35+
"""
36+
)
3237
}
3338

3439
try! InitializerDeclSyntax("@_spi(RawSyntax) public init?(_ text: SyntaxText)") {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ let rawSyntaxNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
4242
for node in SYNTAX_NODES where node.kind.isBase {
4343
DeclSyntax(
4444
"""
45-
@_spi(RawSyntax)
45+
\(node.apiAttributes(forRaw: true))\
4646
public protocol \(node.kind.rawType)NodeProtocol: \(raw: node.base.rawProtocolType) {}
4747
"""
4848
)
@@ -51,7 +51,7 @@ let rawSyntaxNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
5151
for node in SYNTAX_NODES {
5252
try! StructDeclSyntax(
5353
"""
54-
@_spi(RawSyntax)
54+
\(node.apiAttributes(forRaw: true))\
5555
public struct \(node.kind.rawType): \(node.kind.isBase ? node.kind.rawProtocolType : node.base.rawProtocolType)
5656
"""
5757
) {

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let nodesSections: String = {
3737

3838
for (baseKind, heading) in nodeKinds {
3939
let baseTypes = ["\(baseKind.syntaxType)", "\(baseKind.syntaxType)Protocol", "Missing\(baseKind.syntaxType)"]
40-
let leafTypes = SYNTAX_NODES.filter({ $0.base == baseKind && !$0.kind.isMissing }).map(\.kind.syntaxType.description)
40+
let leafTypes = SYNTAX_NODES.filter({ $0.base == baseKind && !$0.kind.isMissing && !$0.isExperimental }).map(\.kind.syntaxType.description)
4141
addSection(heading: heading, types: baseTypes + leafTypes)
4242
}
4343

@@ -48,7 +48,7 @@ let nodesSections: String = {
4848
"SyntaxChildrenIndex",
4949
]
5050
+ SYNTAX_NODES.flatMap({ (node: Node) -> [String] in
51-
guard let node = node.collectionNode else {
51+
guard let node = node.collectionNode, !node.isExperimental else {
5252
return []
5353
}
5454
return [node.kind.syntaxType.description]
@@ -59,9 +59,12 @@ let nodesSections: String = {
5959
})
6060
)
6161

62-
addSection(heading: "Attributes", types: ATTRIBUTE_NODES.map(\.kind.syntaxType.description).sorted())
62+
addSection(heading: "Attributes", types: ATTRIBUTE_NODES.filter({ !$0.isExperimental }).map(\.kind.syntaxType.description).sorted())
6363

64-
addSection(heading: "Miscellaneous Syntax", types: SYNTAX_NODES.map(\.kind.syntaxType.description).filter({ !handledSyntaxTypes.contains($0) }))
64+
addSection(
65+
heading: "Miscellaneous Syntax",
66+
types: SYNTAX_NODES.filter({ !$0.isExperimental }).map(\.kind.syntaxType.description).filter({ !handledSyntaxTypes.contains($0) })
67+
)
6568

6669
addSection(heading: "Traits", types: TRAITS.map { "\($0.protocolName)" })
6770

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ let syntaxAnyVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
7979
for node in SYNTAX_NODES where !node.kind.isBase {
8080
DeclSyntax(
8181
"""
82+
\(node.apiAttributes())\
8283
override open func visit(_ node: \(node.kind.syntaxType)) -> SyntaxVisitorContinueKind {
8384
return visitAny(node._syntaxNode)
8485
}
@@ -87,6 +88,7 @@ let syntaxAnyVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
8788

8889
DeclSyntax(
8990
"""
91+
\(node.apiAttributes())\
9092
override open func visitPost(_ node: \(node.kind.syntaxType)) {
9193
visitAnyPost(node._syntaxNode)
9294
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let syntaxBaseNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2626
/// Extension point to add common methods to all ``\(node.kind.syntaxType)`` nodes.
2727
///
2828
/// - Warning: Do not conform to this protocol yourself.
29+
\(node.apiAttributes())\
2930
public protocol \(node.kind.protocolType): \(raw: node.base.protocolType) {}
3031
"""
3132
)
@@ -59,6 +60,7 @@ let syntaxBaseNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
5960
try! StructDeclSyntax(
6061
"""
6162
\(node.documentation)
63+
\(node.apiAttributes())\
6264
public struct \(node.kind.syntaxType): \(node.kind.protocolType), SyntaxHashable
6365
"""
6466
) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3333
try! StructDeclSyntax(
3434
"""
3535
\(documentation)
36+
\(node.node.apiAttributes())\
3637
public struct \(node.kind.syntaxType): SyntaxCollection, SyntaxHashable
3738
"""
3839
) {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ let syntaxEnumFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2424
) {
2525
DeclSyntax("case token(TokenSyntax)")
2626
for node in NON_BASE_SYNTAX_NODES {
27-
DeclSyntax("case \(node.varOrCaseName)(\(node.kind.syntaxType))")
27+
DeclSyntax(
28+
"""
29+
\(node.apiAttributes())\
30+
case \(node.varOrCaseName)(\(node.kind.syntaxType))
31+
"""
32+
)
2833
}
2934
}
3035

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ let syntaxKindFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2424
) {
2525
DeclSyntax("case token")
2626
for node in NON_BASE_SYNTAX_NODES {
27-
DeclSyntax("case \(node.varOrCaseName)")
27+
DeclSyntax(
28+
"""
29+
\(node.apiAttributes())\
30+
case \(node.varOrCaseName)
31+
"""
32+
)
2833
}
2934

3035
try VariableDeclSyntax("public var isSyntaxCollection: Bool") {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax {
4040
// MARK: - \(raw: node.kind.syntaxType)
4141
4242
\(documentation)
43+
\(node.node.apiAttributes())\
4344
public struct \(raw: node.kind.syntaxType): \(raw: node.baseType.syntaxBaseName)Protocol, SyntaxHashable
4445
"""
4546
) {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
125125
/// Visit a ``\(node.kind.syntaxType)``.
126126
/// - Parameter node: the node that is being visited
127127
/// - Returns: the rewritten node
128+
\(node.apiAttributes())\
128129
open func visit(_ node: \(node.kind.syntaxType)) -> \(node.kind.syntaxType) {
129130
return Syntax(visitChildren(node)).cast(\(node.kind.syntaxType).self)
130131
}
@@ -136,6 +137,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
136137
/// Visit a ``\(node.kind.syntaxType)``.
137138
/// - Parameter node: the node that is being visited
138139
/// - Returns: the rewritten node
140+
\(node.apiAttributes())\
139141
open func visit(_ node: \(node.kind.syntaxType)) -> \(raw: node.baseType.syntaxBaseName) {
140142
return \(raw: node.baseType.syntaxBaseName)(visitChildren(node))
141143
}
@@ -144,12 +146,14 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
144146
}
145147
}
146148

147-
for baseKind in SyntaxNodeKind.allCases where baseKind.isBase && baseKind != .syntax && baseKind != .syntaxCollection {
149+
for baseNode in SYNTAX_NODES where baseNode.kind.isBase && baseNode.kind != .syntax && baseNode.kind != .syntaxCollection {
150+
let baseKind = baseNode.kind
148151
DeclSyntax(
149152
"""
150153
/// Visit any \(raw: baseKind.syntaxType) node.
151154
/// - Parameter node: the node that is being visited
152155
/// - Returns: the rewritten node
156+
\(baseNode.apiAttributes())\
153157
public func visit(_ node: \(raw: baseKind.syntaxType)) -> \(raw: baseKind.syntaxType) {
154158
return visit(node.data).cast(\(raw: baseKind.syntaxType).self)
155159
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,21 @@ import SyntaxSupport
1616
import Utils
1717

1818
let syntaxTransformFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
try! ProtocolDeclSyntax("public protocol SyntaxTransformVisitor") {
19+
try! ProtocolDeclSyntax(
20+
"""
21+
@_spi(SyntaxTransformVisitor)
22+
public protocol SyntaxTransformVisitor
23+
"""
24+
) {
2025
DeclSyntax("associatedtype ResultType = Void")
2126

2227
DeclSyntax("func visitAny(_ node: Syntax) -> ResultType")
2328

2429
DeclSyntax("func visit(_ token: TokenSyntax) -> ResultType")
2530

26-
for node in SYNTAX_NODES where !node.kind.isBase {
31+
// Don't bother including experimental nodes here since we want to remove
32+
// SyntaxTransformVisitor anyway.
33+
for node in SYNTAX_NODES where !node.kind.isBase && !node.isExperimental {
2734
DeclSyntax(
2835
"""
2936
/// Visiting ``\(node.kind.syntaxType)`` specifically.
@@ -50,6 +57,7 @@ let syntaxTransformFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
5057
/// Visiting ``\(node.kind.syntaxType)`` specifically.
5158
/// - Parameter node: the node we are visiting.
5259
/// - Returns: nil by default.
60+
\(node.apiAttributes())\
5361
public func visit(_ node: \(node.kind.syntaxType)) -> ResultType {
5462
visitAny(Syntax(node))
5563
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
5656
/// Visiting ``\(node.kind.syntaxType)`` specifically.
5757
/// - Parameter node: the node we are visiting.
5858
/// - Returns: how should we continue visiting.
59+
\(node.apiAttributes())\
5960
open func visit(_ node: \(node.kind.syntaxType)) -> SyntaxVisitorContinueKind {
6061
return .visitChildren
6162
}
@@ -66,6 +67,7 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
6667
"""
6768
/// The function called after visiting ``\(node.kind.syntaxType)`` and its descendants.
6869
/// - node: the node we just finished visiting.
70+
\(node.apiAttributes())\
6971
open func visitPost(_ node: \(node.kind.syntaxType)) {}
7072
"""
7173
)

0 commit comments

Comments
 (0)