Skip to content

Syntax and parsing for '-define-availability' argument #2954

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/AvailabilityNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,29 @@ public let AVAILABILITY_NODES: [Node] = [
]
),

Node(
kind: .availabilityMacroDefinition,
base: .syntax,
spi: "Compiler",
nameForDiagnostics: "availability macro definition",
documentation: "Syntax for '-define-availability' compiler arguments, never appear in Swift source code",
parserFunction: "parseAvailabilityMacroDefinition",
children: [
Child(
name: "platformVersion",
kind: .node(kind: .platformVersion)
),
Child(
name: "colon",
kind: .token(choices: [.token(.colon)])
),
Child(
name: "specs",
kind: .collection(
kind: .availabilityArgumentList,
collectionElementName: "AvailabilityArgument"
)
),
]
),
]
26 changes: 24 additions & 2 deletions CodeGeneration/Sources/SyntaxSupport/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public class Node: NodeChoiceConvertible {

public let experimentalFeature: ExperimentalFeature?

/// SPI name if this node is only available for the SPI.
public let spi: TokenSyntax?

/// When the node name is printed for diagnostics, this name is used.
/// If `nil`, `nameForDiagnostics` will print the parent node’s name.
public let nameForDiagnostics: String?
Expand Down Expand Up @@ -103,6 +106,14 @@ public class Node: NodeChoiceConvertible {
"""
experimentalSPI.with(\.trailingTrivia, .newline)
}
if let spi = self.spi {
let spiAttr: AttributeListSyntax = """
#if compiler(>=5.8)
@_spi(\(spi))
#endif
"""
spiAttr.with(\.trailingTrivia, .newline)
}
if forRaw {
"@_spi(RawSyntax)"
}
Expand All @@ -119,6 +130,7 @@ public class Node: NodeChoiceConvertible {
kind: SyntaxNodeKind,
base: SyntaxNodeKind,
experimentalFeature: ExperimentalFeature? = nil,
spi: TokenSyntax? = nil,
nameForDiagnostics: String?,
documentation: String? = nil,
parserFunction: TokenSyntax? = nil,
Expand All @@ -132,6 +144,7 @@ public class Node: NodeChoiceConvertible {
self.kind = kind
self.base = base
self.experimentalFeature = experimentalFeature
self.spi = spi
self.nameForDiagnostics = nameForDiagnostics
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
self.parserFunction = parserFunction
Expand All @@ -141,6 +154,10 @@ public class Node: NodeChoiceConvertible {
self.data = .layout(children: childrenWithUnexpected, childHistory: childHistory, traits: traits)
}

public var hiddenInDocumentation: Bool {
self.isExperimental || self.spi != nil || self.kind.isDeprecated
}

/// A doc comment that lists all the nodes in which this node occurs as a child in.
public var containedIn: SwiftSyntax.Trivia {
if kind == .unexpectedNodes {
Expand All @@ -149,7 +166,10 @@ public class Node: NodeChoiceConvertible {
return []
}
var childIn: [(node: SyntaxNodeKind, child: Child?)] = []
for node in SYNTAX_NODES where !node.isExperimental {
for node in SYNTAX_NODES {
if !self.hiddenInDocumentation && node.hiddenInDocumentation {
continue
}
if let layout = node.layoutNode {
for child in layout.children {
if child.kinds.contains(self.kind) {
Expand Down Expand Up @@ -202,7 +222,7 @@ public class Node: NodeChoiceConvertible {

let list =
SYNTAX_NODES
.filter { $0.base == self.kind && !$0.isExperimental && !$0.kind.isDeprecated }
.filter { $0.base == self.kind && (!$0.hiddenInDocumentation || self.hiddenInDocumentation) }
.map { "- \($0.kind.doccLink)" }
.joined(separator: "\n")

Expand All @@ -226,6 +246,7 @@ public class Node: NodeChoiceConvertible {
kind: SyntaxNodeKind,
base: SyntaxNodeKind,
experimentalFeature: ExperimentalFeature? = nil,
spi: TokenSyntax? = nil,
nameForDiagnostics: String?,
documentation: String? = nil,
parserFunction: TokenSyntax? = nil,
Expand All @@ -235,6 +256,7 @@ public class Node: NodeChoiceConvertible {
precondition(base == .syntaxCollection)
self.base = base
self.experimentalFeature = experimentalFeature
self.spi = spi
self.nameForDiagnostics = nameForDiagnostics
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
self.parserFunction = parserFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon
case availabilityArgumentList
case availabilityCondition
case availabilityLabeledArgument
case availabilityMacroDefinition
case awaitExpr
case backDeployedAttributeArguments
case binaryOperatorExpr
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxBuilder
import SyntaxSupport

enum ImportAccessLevel {
case `public`
case `internal`
}

func importSwiftSyntax(accessLevel: ImportAccessLevel = .internal) -> DeclSyntax {
// Import all '@_spi'.
let importingAttrs = AttributeListSyntax {
var seen: Set<String> = []
AttributeSyntax("@_spi(RawSyntax)").with(\.trailingTrivia, .space)
AttributeSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .space)
for node in NON_BASE_SYNTAX_NODES {
if let spi = node.spi, seen.insert(spi.text).inserted {
AttributeSyntax("@_spi(\(spi))").with(\.trailingTrivia, .space)
}
}
}
let visibilityKeyword: TokenSyntax
switch accessLevel {
case .internal:
visibilityKeyword = "internal"
case .public:
visibilityKeyword = "public"
}

return DeclSyntax(
"""
#if compiler(>=6)
\(importingAttrs)\(visibilityKeyword) import SwiftSyntax
#else
\(importingAttrs)import SwiftSyntax
#endif
"""
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let isLexerClassifiedFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
public import SwiftSyntax
#else
import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

try! ExtensionDeclSyntax(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let layoutNodesParsableFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(RawSyntax) public import SwiftSyntax
#else
@_spi(RawSyntax) import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

DeclSyntax(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,7 @@ func tokenCaseMatch(
}

let parserTokenSpecSetFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
#else
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode) {
for child in layoutNode.children {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let tokenSpecStaticMembersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(RawSyntax) internal import SwiftSyntax
#else
@_spi(RawSyntax) import SwiftSyntax
#endif
"""
)
importSwiftSyntax()

try! ExtensionDeclSyntax("extension TokenSpec") {
for tokenSpec in Token.allCases.map(\.spec) where tokenSpec.kind != .keyword {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let childNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
#else
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .internal)

try! FunctionDeclSyntax(
"private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let syntaxKindNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
#else
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
"""
)
importSwiftSyntax()

try! ExtensionDeclSyntax("extension SyntaxKind") {
try VariableDeclSyntax("var nameForDiagnostics: String?") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let tokenNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(RawSyntax) internal import SwiftSyntax
#else
@_spi(RawSyntax) import SwiftSyntax
#endif
"""
)
importSwiftSyntax()

try! ExtensionDeclSyntax("extension TokenKind") {
try! VariableDeclSyntax("var nameForDiagnostics: String") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let nodesSections: String = {
let baseTypes = ["\(baseKind.syntaxType)", "\(baseKind.syntaxType)Protocol", "Missing\(baseKind.syntaxType)"]
let leafTypes =
SYNTAX_NODES
.filter({ $0.base == baseKind && !$0.kind.isMissing && !$0.isExperimental && !$0.kind.isDeprecated })
.filter({ $0.base == baseKind && !$0.kind.isMissing && !$0.hiddenInDocumentation })
.map(\.kind.syntaxType.description)
addSection(heading: heading, types: baseTypes + leafTypes)
}
Expand All @@ -51,26 +51,26 @@ let nodesSections: String = {
"SyntaxChildrenIndex",
]
+ SYNTAX_NODES.flatMap({ (node: Node) -> [String] in
guard let node = node.collectionNode, !node.isExperimental else {
guard let node = node.collectionNode, !node.hiddenInDocumentation else {
return []
}
return [node.kind.syntaxType.description]
+ node.elementChoices
.filter { SYNTAX_NODE_MAP[$0] != nil && !SYNTAX_NODE_MAP[$0]!.isExperimental && !$0.isDeprecated }
.filter { SYNTAX_NODE_MAP[$0] != nil && !SYNTAX_NODE_MAP[$0]!.hiddenInDocumentation }
.map(\.syntaxType.description)
.filter { !handledSyntaxTypes.contains($0) }
})
)

addSection(
heading: "Attributes",
types: ATTRIBUTE_NODES.filter({ !$0.isExperimental && !$0.kind.isDeprecated }).map(\.kind.syntaxType.description)
types: ATTRIBUTE_NODES.filter({ !$0.hiddenInDocumentation }).map(\.kind.syntaxType.description)
.sorted()
)

addSection(
heading: "Miscellaneous Syntax",
types: SYNTAX_NODES.filter({ !$0.isExperimental && !$0.kind.isDeprecated }).map(\.kind.syntaxType.description)
types: SYNTAX_NODES.filter({ !$0.hiddenInDocumentation }).map(\.kind.syntaxType.description)
.filter({
!handledSyntaxTypes.contains($0)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let buildableNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
#else
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

for node in SYNTAX_NODES.compactMap(\.layoutNode) {
let type = node.type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let renamedChildrenBuilderCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
public import SwiftSyntax
#else
import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ !$0.childHistory.isEmpty }) {
let deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: layoutNode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ import SyntaxSupport
import Utils

let resultBuildersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
#if compiler(>=6)
@_spi(ExperimentalLanguageFeatures) public import SwiftSyntax
#else
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
"""
)
importSwiftSyntax(accessLevel: .public)

for node in SYNTAX_NODES.compactMap(\.collectionNode) {
let type = SyntaxBuildableType(kind: .node(kind: node.kind))
Expand Down
Loading