Skip to content

Commit 7a37286

Browse files
authored
Merge pull request #2954 from rintaro/availability-macro-definition
Syntax and parsing for '-define-availability' argument
2 parents 749a09a + a1c17fe commit 7a37286

Some content is hidden

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

42 files changed

+556
-129
lines changed

CodeGeneration/Sources/SyntaxSupport/AvailabilityNodes.swift

+25
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,29 @@ public let AVAILABILITY_NODES: [Node] = [
182182
]
183183
),
184184

185+
Node(
186+
kind: .availabilityMacroDefinition,
187+
base: .syntax,
188+
spi: "Compiler",
189+
nameForDiagnostics: "availability macro definition",
190+
documentation: "Syntax for '-define-availability' compiler arguments, never appear in Swift source code",
191+
parserFunction: "parseAvailabilityMacroDefinition",
192+
children: [
193+
Child(
194+
name: "platformVersion",
195+
kind: .node(kind: .platformVersion)
196+
),
197+
Child(
198+
name: "colon",
199+
kind: .token(choices: [.token(.colon)])
200+
),
201+
Child(
202+
name: "specs",
203+
kind: .collection(
204+
kind: .availabilityArgumentList,
205+
collectionElementName: "AvailabilityArgument"
206+
)
207+
),
208+
]
209+
),
185210
]

CodeGeneration/Sources/SyntaxSupport/Node.swift

+24-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class Node: NodeChoiceConvertible {
4242

4343
public let experimentalFeature: ExperimentalFeature?
4444

45+
/// SPI name if this node is only available for the SPI.
46+
public let spi: TokenSyntax?
47+
4548
/// When the node name is printed for diagnostics, this name is used.
4649
/// If `nil`, `nameForDiagnostics` will print the parent node’s name.
4750
public let nameForDiagnostics: String?
@@ -103,6 +106,14 @@ public class Node: NodeChoiceConvertible {
103106
"""
104107
experimentalSPI.with(\.trailingTrivia, .newline)
105108
}
109+
if let spi = self.spi {
110+
let spiAttr: AttributeListSyntax = """
111+
#if compiler(>=5.8)
112+
@_spi(\(spi))
113+
#endif
114+
"""
115+
spiAttr.with(\.trailingTrivia, .newline)
116+
}
106117
if forRaw {
107118
"@_spi(RawSyntax)"
108119
}
@@ -119,6 +130,7 @@ public class Node: NodeChoiceConvertible {
119130
kind: SyntaxNodeKind,
120131
base: SyntaxNodeKind,
121132
experimentalFeature: ExperimentalFeature? = nil,
133+
spi: TokenSyntax? = nil,
122134
nameForDiagnostics: String?,
123135
documentation: String? = nil,
124136
parserFunction: TokenSyntax? = nil,
@@ -132,6 +144,7 @@ public class Node: NodeChoiceConvertible {
132144
self.kind = kind
133145
self.base = base
134146
self.experimentalFeature = experimentalFeature
147+
self.spi = spi
135148
self.nameForDiagnostics = nameForDiagnostics
136149
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
137150
self.parserFunction = parserFunction
@@ -141,6 +154,10 @@ public class Node: NodeChoiceConvertible {
141154
self.data = .layout(children: childrenWithUnexpected, childHistory: childHistory, traits: traits)
142155
}
143156

157+
public var hiddenInDocumentation: Bool {
158+
self.isExperimental || self.spi != nil || self.kind.isDeprecated
159+
}
160+
144161
/// A doc comment that lists all the nodes in which this node occurs as a child in.
145162
public var containedIn: SwiftSyntax.Trivia {
146163
if kind == .unexpectedNodes {
@@ -149,7 +166,10 @@ public class Node: NodeChoiceConvertible {
149166
return []
150167
}
151168
var childIn: [(node: SyntaxNodeKind, child: Child?)] = []
152-
for node in SYNTAX_NODES where !node.isExperimental {
169+
for node in SYNTAX_NODES {
170+
if !self.hiddenInDocumentation && node.hiddenInDocumentation {
171+
continue
172+
}
153173
if let layout = node.layoutNode {
154174
for child in layout.children {
155175
if child.kinds.contains(self.kind) {
@@ -202,7 +222,7 @@ public class Node: NodeChoiceConvertible {
202222

203223
let list =
204224
SYNTAX_NODES
205-
.filter { $0.base == self.kind && !$0.isExperimental && !$0.kind.isDeprecated }
225+
.filter { $0.base == self.kind && (!$0.hiddenInDocumentation || self.hiddenInDocumentation) }
206226
.map { "- \($0.kind.doccLink)" }
207227
.joined(separator: "\n")
208228

@@ -226,6 +246,7 @@ public class Node: NodeChoiceConvertible {
226246
kind: SyntaxNodeKind,
227247
base: SyntaxNodeKind,
228248
experimentalFeature: ExperimentalFeature? = nil,
249+
spi: TokenSyntax? = nil,
229250
nameForDiagnostics: String?,
230251
documentation: String? = nil,
231252
parserFunction: TokenSyntax? = nil,
@@ -235,6 +256,7 @@ public class Node: NodeChoiceConvertible {
235256
precondition(base == .syntaxCollection)
236257
self.base = base
237258
self.experimentalFeature = experimentalFeature
259+
self.spi = spi
238260
self.nameForDiagnostics = nameForDiagnostics
239261
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
240262
self.parserFunction = parserFunction

CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon
4444
case availabilityArgumentList
4545
case availabilityCondition
4646
case availabilityLabeledArgument
47+
case availabilityMacroDefinition
4748
case awaitExpr
4849
case backDeployedAttributeArguments
4950
case binaryOperatorExpr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 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+
import SwiftSyntaxBuilder
15+
import SyntaxSupport
16+
17+
enum ImportAccessLevel {
18+
case `public`
19+
case `internal`
20+
}
21+
22+
func importSwiftSyntax(accessLevel: ImportAccessLevel = .internal) -> DeclSyntax {
23+
// Import all '@_spi'.
24+
let importingAttrs = AttributeListSyntax {
25+
var seen: Set<String> = []
26+
AttributeSyntax("@_spi(RawSyntax)").with(\.trailingTrivia, .space)
27+
AttributeSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .space)
28+
for node in NON_BASE_SYNTAX_NODES {
29+
if let spi = node.spi, seen.insert(spi.text).inserted {
30+
AttributeSyntax("@_spi(\(spi))").with(\.trailingTrivia, .space)
31+
}
32+
}
33+
}
34+
let visibilityKeyword: TokenSyntax
35+
switch accessLevel {
36+
case .internal:
37+
visibilityKeyword = "internal"
38+
case .public:
39+
visibilityKeyword = "public"
40+
}
41+
42+
return DeclSyntax(
43+
"""
44+
#if compiler(>=6)
45+
\(importingAttrs)\(visibilityKeyword) import SwiftSyntax
46+
#else
47+
\(importingAttrs)import SwiftSyntax
48+
#endif
49+
"""
50+
)
51+
}

CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/IsLexerClassifiedFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let isLexerClassifiedFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
public import SwiftSyntax
23-
#else
24-
import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .public)
2820

2921
try! ExtensionDeclSyntax(
3022
"""

CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/LayoutNodesParsableFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let layoutNodesParsableFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(RawSyntax) public import SwiftSyntax
23-
#else
24-
@_spi(RawSyntax) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .public)
2820

2921
DeclSyntax(
3022
"""

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

+1-9
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,7 @@ func tokenCaseMatch(
2727
}
2828

2929
let parserTokenSpecSetFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
30-
DeclSyntax(
31-
"""
32-
#if compiler(>=6)
33-
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
34-
#else
35-
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax
36-
#endif
37-
"""
38-
)
30+
importSwiftSyntax(accessLevel: .public)
3931

4032
for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode) {
4133
for child in layoutNode.children {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/TokenSpecStaticMembersFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let tokenSpecStaticMembersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(RawSyntax) internal import SwiftSyntax
23-
#else
24-
@_spi(RawSyntax) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax()
2820

2921
try! ExtensionDeclSyntax("extension TokenSpec") {
3022
for tokenSpec in Token.allCases.map(\.spec) where tokenSpec.kind != .keyword {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftparserdiagnostics/ChildNameForDiagnosticsFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let childNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
23-
#else
24-
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .internal)
2820

2921
try! FunctionDeclSyntax(
3022
"private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String?"

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

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let syntaxKindNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
23-
#else
24-
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax()
2820

2921
try! ExtensionDeclSyntax("extension SyntaxKind") {
3022
try VariableDeclSyntax("var nameForDiagnostics: String?") {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftparserdiagnostics/TokenNameForDiagnosticsFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let tokenNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(RawSyntax) internal import SwiftSyntax
23-
#else
24-
@_spi(RawSyntax) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax()
2820

2921
try! ExtensionDeclSyntax("extension TokenKind") {
3022
try! VariableDeclSyntax("var nameForDiagnostics: String") {

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ let nodesSections: String = {
3939
let baseTypes = ["\(baseKind.syntaxType)", "\(baseKind.syntaxType)Protocol", "Missing\(baseKind.syntaxType)"]
4040
let leafTypes =
4141
SYNTAX_NODES
42-
.filter({ $0.base == baseKind && !$0.kind.isMissing && !$0.isExperimental && !$0.kind.isDeprecated })
42+
.filter({ $0.base == baseKind && !$0.kind.isMissing && !$0.hiddenInDocumentation })
4343
.map(\.kind.syntaxType.description)
4444
addSection(heading: heading, types: baseTypes + leafTypes)
4545
}
@@ -51,26 +51,26 @@ let nodesSections: String = {
5151
"SyntaxChildrenIndex",
5252
]
5353
+ SYNTAX_NODES.flatMap({ (node: Node) -> [String] in
54-
guard let node = node.collectionNode, !node.isExperimental else {
54+
guard let node = node.collectionNode, !node.hiddenInDocumentation else {
5555
return []
5656
}
5757
return [node.kind.syntaxType.description]
5858
+ node.elementChoices
59-
.filter { SYNTAX_NODE_MAP[$0] != nil && !SYNTAX_NODE_MAP[$0]!.isExperimental && !$0.isDeprecated }
59+
.filter { SYNTAX_NODE_MAP[$0] != nil && !SYNTAX_NODE_MAP[$0]!.hiddenInDocumentation }
6060
.map(\.syntaxType.description)
6161
.filter { !handledSyntaxTypes.contains($0) }
6262
})
6363
)
6464

6565
addSection(
6666
heading: "Attributes",
67-
types: ATTRIBUTE_NODES.filter({ !$0.isExperimental && !$0.kind.isDeprecated }).map(\.kind.syntaxType.description)
67+
types: ATTRIBUTE_NODES.filter({ !$0.hiddenInDocumentation }).map(\.kind.syntaxType.description)
6868
.sorted()
6969
)
7070

7171
addSection(
7272
heading: "Miscellaneous Syntax",
73-
types: SYNTAX_NODES.filter({ !$0.isExperimental && !$0.kind.isDeprecated }).map(\.kind.syntaxType.description)
73+
types: SYNTAX_NODES.filter({ !$0.hiddenInDocumentation }).map(\.kind.syntaxType.description)
7474
.filter({
7575
!handledSyntaxTypes.contains($0)
7676
})

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/BuildableNodesFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let buildableNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
23-
#else
24-
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .public)
2820

2921
for node in SYNTAX_NODES.compactMap(\.layoutNode) {
3022
let type = node.type

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

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let renamedChildrenBuilderCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
public import SwiftSyntax
23-
#else
24-
import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .public)
2820

2921
for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ !$0.childHistory.isEmpty }) {
3022
let deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: layoutNode)

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import SyntaxSupport
1616
import Utils
1717

1818
let resultBuildersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19-
DeclSyntax(
20-
"""
21-
#if compiler(>=6)
22-
@_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
23-
#else
24-
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
25-
#endif
26-
"""
27-
)
19+
importSwiftSyntax(accessLevel: .public)
2820

2921
for node in SYNTAX_NODES.compactMap(\.collectionNode) {
3022
let type = SyntaxBuildableType(kind: .node(kind: node.kind))

0 commit comments

Comments
 (0)