Skip to content

Commit bcf256e

Browse files
committed
[Syntax] Introduce SyntaxLayout
This replaces most use case of `keyPathInParent` and `SyntaxNodeStructure.layout`. Since getting and comparing `KeyPath` is not trivial, `keyPathInParent` were suffered from performance issue, Newly introduced `SyntaxProtocol.propertyInParent` is trivial, and comparing it with a SyntaxLayoutProperty is also trivial.
1 parent fd232f9 commit bcf256e

34 files changed

+13355
-4195
lines changed

CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift

-13
Original file line numberDiff line numberDiff line change
@@ -376,19 +376,6 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon
376376
return "\(syntaxType)Protocol"
377377
}
378378

379-
/// For base node types, generates the name of the protocol to which all
380-
/// concrete leaf nodes that derive from this base kind should conform.
381-
///
382-
/// - Warning: This property can only be accessed for base node kinds; attempting to
383-
/// access it for a non-base kind will result in a runtime error.
384-
public var leafProtocolType: TypeSyntax {
385-
if isBase {
386-
return "_Leaf\(syntaxType)NodeProtocol"
387-
} else {
388-
fatalError("Only base kind can define leaf protocol")
389-
}
390-
}
391-
392379
/// If the syntax kind has been renamed, the previous raw value that is now
393380
/// deprecated.
394381
public var deprecatedRawValue: String? {

CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ struct GenerateSwiftSyntax: AsyncParsableCommand {
115115
),
116116

117117
// SwiftSyntax
118-
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["ChildNameForKeyPath.swift"], childNameForKeyPathFile),
118+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["ConcreteSyntaxProperty.swift"], concreteSyntaxPropertyFile),
119119
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Keyword.swift"], keywordFile),
120120
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["raw", "RawSyntaxValidation.swift"], rawSyntaxValidationFile),
121121
GeneratedFileSpec(
@@ -128,6 +128,7 @@ struct GenerateSwiftSyntax: AsyncParsableCommand {
128128
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxCollections.swift"], syntaxCollectionsFile),
129129
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxEnum.swift"], syntaxEnumFile),
130130
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxKind.swift"], syntaxKindFile),
131+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxLayoutPropertyName.swift"], syntaxLayoutPropertyNameFile),
131132
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxRewriter.swift"], syntaxRewriterFile),
132133
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTraits.swift"], syntaxTraitsFile),
133134
GeneratedFileSpec(

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ let childNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader
2727
)
2828

2929
try! FunctionDeclSyntax(
30-
"private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String?"
30+
"private func childNameForDiagnostics(_ property: SyntaxLayoutProperty) -> String?"
3131
) {
32-
try! SwitchExprSyntax("switch keyPath") {
32+
try! SwitchExprSyntax("switch property") {
3333
for node in NON_BASE_SYNTAX_NODES.compactMap(\.layoutNode) {
3434
for child in node.children {
3535
if let nameForDiagnostics = child.nameForDiagnostics {
36-
SwitchCaseSyntax("case \\\(node.type.syntaxBaseName).\(child.memberCallName):") {
36+
SwitchCaseSyntax("case \(node.type.syntaxBaseName).layout[.\(child.memberCallName)]:") {
3737
StmtSyntax(#"return "\#(raw: nameForDiagnostics)""#)
3838
}
3939
}
@@ -52,10 +52,10 @@ let childNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader
5252
"""
5353
extension SyntaxProtocol {
5454
var childNameInParent: String? {
55-
guard let keyPath = self.keyPathInParent else {
55+
guard let property = self.propertyInParent else {
5656
return nil
5757
}
58-
return childNameForDiagnostics(keyPath)
58+
return childNameForDiagnostics(property)
5959
}
6060
}
6161
"""

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

-46
This file was deleted.
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) 2014 - 2023 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+
import Utils
17+
18+
let concreteSyntaxPropertyFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
19+
DeclSyntax(
20+
"""
21+
extension ConcreteSyntaxProperty {
22+
fileprivate init(_ index: Int) {
23+
self.index = .init(index)
24+
}
25+
}
26+
"""
27+
)
28+
29+
for node in NON_BASE_SYNTAX_NODES {
30+
if let layoutNode = node.layoutNode {
31+
try! ExtensionDeclSyntax(
32+
"\(node.apiAttributes())extension ConcreteSyntaxProperty where Base == \(node.kind.syntaxType)"
33+
) {
34+
for (index, child) in layoutNode.children.enumerated() {
35+
let childType: TypeSyntax =
36+
child.kind.isNodeChoicesEmpty
37+
? child.syntaxNodeKind.syntaxType
38+
: "\(node.kind.syntaxType).\(child.syntaxChoicesType)"
39+
let type = child.isOptional ? TypeSyntax("\(childType)?") : TypeSyntax("\(childType)")
40+
DeclSyntax(
41+
"""
42+
public static var \(child.varDeclName): ConcreteSyntaxProperty<\(node.kind.syntaxType), \(type)> {
43+
.init(\(raw: index))
44+
}
45+
"""
46+
)
47+
}
48+
}
49+
}
50+
}
51+
}

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

+5-20
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,7 @@ let syntaxBaseNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
287287
}
288288
}
289289

290-
leafProtocolDecl(type: node.kind.leafProtocolType, inheritedType: node.kind.protocolType)
291-
leafProtocolExtension(type: node.kind.leafProtocolType, inheritedType: node.kind.protocolType)
290+
leafProtocolExtension(inheritedType: node.kind.protocolType)
292291
}
293292

294293
try! ExtensionDeclSyntax(
@@ -317,27 +316,13 @@ let syntaxBaseNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
317316
}
318317
}
319318

320-
leafProtocolDecl(type: "_LeafSyntaxNodeProtocol", inheritedType: "SyntaxProtocol")
321-
leafProtocolExtension(type: "_LeafSyntaxNodeProtocol", inheritedType: "SyntaxProtocol")
319+
leafProtocolExtension(inheritedType: "SyntaxProtocol")
322320
}
323321

324-
private func leafProtocolDecl(type: TypeSyntax, inheritedType: TypeSyntax) -> DeclSyntax {
325-
DeclSyntax(
326-
"""
327-
/// Protocol that syntax nodes conform to if they don't have any semantic subtypes.
328-
/// These are syntax nodes that are not considered base nodes for other syntax types.
329-
///
330-
/// Syntax nodes conforming to this protocol have their inherited casting methods
331-
/// deprecated to prevent incorrect casting.
332-
public protocol \(type): \(inheritedType) {}
333-
"""
334-
)
335-
}
336-
337-
private func leafProtocolExtension(type: TypeSyntax, inheritedType: TypeSyntax) -> DeclSyntax {
338-
DeclSyntax(
322+
private func leafProtocolExtension(inheritedType: TypeSyntax) -> DeclSyntax {
323+
return DeclSyntax(
339324
#"""
340-
extension \#(type) {
325+
extension _LeafSyntaxNodeProtocol where Self: \#(inheritedType) {
341326
/// Checks if the current leaf syntax node can be cast to a different specified type.
342327
///
343328
/// - Returns: `false` since the leaf node cannot be cast to a different specified type.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
5959
"""
6060
)
6161

62-
DeclSyntax("public static let syntaxKind = SyntaxKind.\(node.memberCallName)")
62+
DeclSyntax("public static var syntaxKind: SyntaxKind { .\(node.enumCaseCallName) }")
6363
}
6464
}
6565
}

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

+14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ let syntaxKindFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
4646
}
4747
}
4848

49+
try VariableDeclSyntax("public var isLayout: Bool") {
50+
try SwitchExprSyntax("switch self") {
51+
for node in SYNTAX_NODES where node.layoutNode != nil {
52+
SwitchCaseSyntax("case .\(node.enumCaseCallName):") {
53+
StmtSyntax("return true")
54+
}
55+
}
56+
57+
SwitchCaseSyntax("default:") {
58+
StmtSyntax("return false")
59+
}
60+
}
61+
}
62+
4963
try VariableDeclSyntax("public var isMissing: Bool") {
5064
try SwitchExprSyntax("switch self") {
5165
for name in SyntaxNodeKind.allCases where name.isMissing {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
import Utils
17+
18+
let syntaxLayoutPropertyNameFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) {
19+
try ExtensionDeclSyntax("extension SyntaxLayoutProperty") {
20+
try VariableDeclSyntax(
21+
"""
22+
/// Property name if this is a valid property.
23+
/// 'nil' if the `syntaxKind` is not a layout syntax, or the index is out of range.
24+
@_spi(RawSyntax)
25+
public var name: String?
26+
"""
27+
) {
28+
try SwitchExprSyntax("switch (self.syntaxKind, self.index.value)") {
29+
for node in NON_BASE_SYNTAX_NODES {
30+
if let layoutNode = node.layoutNode {
31+
for (index, child) in layoutNode.children.enumerated() {
32+
SwitchCaseSyntax("case (.\(node.enumCaseCallName), \(literal: index)):") {
33+
StmtSyntax("return \(literal: child.identifier.description)")
34+
}
35+
}
36+
}
37+
}
38+
SwitchCaseSyntax("default: return nil")
39+
}
40+
}
41+
}
42+
}

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax {
3030
3131
\(SwiftSyntax.Trivia(joining: [node.documentation, node.experimentalDocNote, node.grammar, node.containedIn]))\
3232
\(node.node.apiAttributes())\
33-
public struct \(node.kind.syntaxType): \(node.baseType.syntaxBaseName)Protocol, SyntaxHashable, \(node.base.leafProtocolType)
33+
public struct \(node.kind.syntaxType): \(node.baseType.syntaxBaseName)Protocol, SyntaxHashable, _LayoutSyntaxNodeProtocol
3434
"""
3535
) {
3636
for childNodeChoices in node.node.childrenNodeChoices() {
@@ -208,6 +208,12 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax {
208208
}
209209
}
210210

211+
// ========
212+
// Metadata
213+
// ========
214+
215+
DeclSyntax("public static var syntaxKind: SyntaxKind { .\(node.enumCaseCallName) }")
216+
211217
let layout = ArrayExprSyntax {
212218
for child in node.children {
213219
ArrayElementSyntax(

0 commit comments

Comments
 (0)