Skip to content

Commit 3b566c3

Browse files
authored
Merge pull request #572 from rintaro/typedrawsyntax
Add typed raw syntax nodes
2 parents 71ab8cc + f515bfa commit 3b566c3

29 files changed

+16848
-12789
lines changed

Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ let package = Package(
5555
dependencies: ["_CSwiftSyntax"],
5656
exclude: [
5757
"Misc.swift.gyb",
58+
"RawSyntaxNodes.swift.gyb",
59+
"RawSyntaxValidation.swift.gyb",
5860
"SyntaxAnyVisitor.swift.gyb",
5961
"SyntaxBaseNodes.swift.gyb",
6062
"SyntaxClassification.swift.gyb",

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ extension RawSyntaxData.MaterializedToken {
5454
/// Represents the raw tree structure underlying the syntax tree. These nodes
5555
/// have no notion of identity and only provide structure to the tree. They
5656
/// are immutable and can be freely shared between syntax nodes.
57-
struct RawSyntax {
57+
@_spi(RawSyntax)
58+
public struct RawSyntax {
5859

5960
/// Pointer to the actual data which resides in a SyntaxArena.
6061
var pointer: UnsafePointer<RawSyntaxData>
@@ -514,7 +515,7 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
514515
/// Prints the RawSyntax node, and all of its children, to the provided
515516
/// stream. This implementation must be source-accurate.
516517
/// - Parameter stream: The stream on which to output this node.
517-
func write<Target: TextOutputStream>(to target: inout Target) {
518+
public func write<Target: TextOutputStream>(to target: inout Target) {
518519
switch rawData.payload {
519520
case .materializedToken(let dat):
520521
for p in dat.leadingTrivia { p.write(to: &target) }
@@ -530,7 +531,7 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
530531
}
531532

532533
/// A source-accurate description of this node.
533-
var description: String {
534+
public var description: String {
534535
var s = ""
535536
self.write(to: &s)
536537
return s
@@ -734,6 +735,7 @@ extension RawSyntax {
734735
descendantCount: Int,
735736
arena: SyntaxArena
736737
) -> RawSyntax {
738+
validateLayout(layout: layout, as: kind)
737739
let payload = RawSyntaxData.Layout(
738740
kind: kind, layout: layout,
739741
byteLength: byteLength, descendantCount: descendantCount)
@@ -756,7 +758,6 @@ extension RawSyntax {
756758
// Allocate and initialize the list.
757759
let layoutBuffer = arena.allocateRawSyntaxBuffer(count: count)
758760
initializer(layoutBuffer)
759-
// validateLayout(layout: RawSyntaxBuffer(layoutBuffer), as: kind)
760761

761762
// Calculate the "byte width".
762763
var byteLength = 0
@@ -892,15 +893,15 @@ extension RawSyntax: CustomDebugStringConvertible {
892893
target.write(")")
893894
}
894895

895-
var debugDescription: String {
896+
public var debugDescription: String {
896897
var string = ""
897898
debugWrite(to: &string, indent: 0, withChildren: false)
898899
return string
899900
}
900901
}
901902

902903
extension RawSyntax: CustomReflectable {
903-
var customMirror: Mirror {
904+
public var customMirror: Mirror {
904905
let mirrorChildren: [Any] = children.map {
905906
child in child ?? (nil as Any?) as Any
906907
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//===------- RawSyntaxNodeProtocol.swift - Typed Raw Syntax Protocol ------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// All typed raw syntax nodes conform to this protocol.
14+
/// `RawXXXSyntax` is a typed wrappeer of `RawSyntax`.
15+
@_spi(RawSyntax)
16+
public protocol RawSyntaxNodeProtocol {
17+
/// Returns `true` if `raw` can be cast to this concrete raw syntax type.
18+
static func isKindOf(_ raw: RawSyntax) -> Bool
19+
20+
/// Type erased raw syntax.
21+
var raw: RawSyntax { get }
22+
23+
/// Create the typed raw syntax if `other` can be cast to `Self`
24+
init?<T: RawSyntaxNodeProtocol>(_ other: T)
25+
}
26+
27+
public extension RawSyntaxNodeProtocol {
28+
/// Cast to the specified raw syntax type if possible.
29+
func `as`<Node: RawSyntaxNodeProtocol>(_: Node.Type) -> Node? {
30+
Node(self)
31+
}
32+
33+
/// Check if this instance can be cast to the specified syntax type.
34+
func `is`<Node: RawSyntaxNodeProtocol>(_: Node.Type) -> Bool {
35+
Node.isKindOf(self.raw)
36+
}
37+
}
38+
39+
/// `RawSyntax` itself conforms to `RawSyntaxNodeProtocol`.
40+
extension RawSyntax: RawSyntaxNodeProtocol {
41+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
42+
return true
43+
}
44+
public var raw: RawSyntax { self }
45+
46+
init(raw: RawSyntax) {
47+
self = raw
48+
}
49+
50+
public init<T: RawSyntaxNodeProtocol>(_ other: T) {
51+
self.init(raw: other.raw)
52+
}
53+
}
54+
55+
@_spi(RawSyntax)
56+
public struct RawTokenSyntax: RawSyntaxNodeProtocol {
57+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
58+
return raw.kind == .token
59+
}
60+
61+
public var raw: RawSyntax
62+
63+
init(raw: RawSyntax) {
64+
assert(Self.isKindOf(raw))
65+
self.raw = raw
66+
}
67+
68+
public init?<Node: RawSyntaxNodeProtocol>(_ other: Node) {
69+
guard Self.isKindOf(other.raw) else { return nil }
70+
self.init(raw: other.raw)
71+
}
72+
73+
public var tokenKind: RawTokenKind {
74+
return raw.rawTokenKind
75+
}
76+
77+
public var tokenText: SyntaxText {
78+
return raw.rawTokenText
79+
}
80+
81+
public var presence: SourcePresence {
82+
raw.presence
83+
}
84+
85+
public var isMissing: Bool {
86+
presence == .missing
87+
}
88+
89+
/// Creates a `TokenSyntax`. `text` and trivia must be managed by the same
90+
/// `arena`.
91+
public init(
92+
kind: RawTokenKind,
93+
text: SyntaxText,
94+
leadingTriviaPieces: [RawTriviaPiece],
95+
trailingTriviaPieces: [RawTriviaPiece],
96+
arena: SyntaxArena
97+
) {
98+
assert(arena.contains(text: text), "token text must be managed by the arena")
99+
let totalTriviaCount = leadingTriviaPieces.count + trailingTriviaPieces.count
100+
let buffer = arena.allocateRawTriviaPieceBuffer(count: totalTriviaCount)
101+
var byteLength = 0
102+
if totalTriviaCount != 0 {
103+
var ptr = buffer.baseAddress!
104+
for piece in leadingTriviaPieces + trailingTriviaPieces {
105+
assert(piece.storedText.map(arena.contains(text:)) ?? true,
106+
"trivia text must be managed by the arena")
107+
byteLength += piece.byteLength
108+
ptr.initialize(to: piece)
109+
ptr = ptr.advanced(by: 1)
110+
}
111+
}
112+
let raw = RawSyntax.materializedToken(
113+
kind: kind, text: text, triviaPieces: RawTriviaPieceBuffer(buffer),
114+
numLeadingTrivia: numericCast(leadingTriviaPieces.count),
115+
byteLength: numericCast(byteLength),
116+
arena: arena)
117+
self = RawTokenSyntax(raw: raw)
118+
}
119+
120+
/// Creates a missing `TokenSyntax` with the specified kind.
121+
public init(missing kind: RawTokenKind, arena: SyntaxArena) {
122+
self.init(
123+
kind: kind, text: "", leadingTriviaPieces: [], trailingTriviaPieces: [],
124+
arena: arena)
125+
}
126+
}
127+
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
%{
2+
from gyb_syntax_support import *
3+
NODE_MAP = create_node_map()
4+
# -*- mode: Swift -*-
5+
# Ignore the following admonition; it applies to the resulting .swift file only
6+
}%
7+
//// Automatically Generated From RawSyntaxNodes.swift.gyb.
8+
//// Do Not Edit Directly!
9+
//===------ RawSyntaxNodes.swift - Typed Raw Syntax Node definitions ------===//
10+
//
11+
// This source file is part of the Swift.org open source project
12+
//
13+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
14+
// Licensed under Apache License v2.0 with Runtime Library Exception
15+
//
16+
// See https://swift.org/LICENSE.txt for license information
17+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
% for node in SYNTAX_NODES:
22+
% if node.is_base():
23+
@_spi(RawSyntax)
24+
public protocol Raw${node.name}NodeProtocol: Raw${node.base_type}NodeProtocol {}
25+
% end
26+
% end
27+
28+
% for node in SYNTAX_NODES:
29+
30+
@_spi(RawSyntax)
31+
public struct Raw${node.name}: Raw${node.name if node.is_base() else node.base_type}NodeProtocol {
32+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
33+
% if node.is_base():
34+
% sub_kinds = ['.' + n.swift_syntax_kind for n in SYNTAX_NODES if n.base_kind == node.syntax_kind]
35+
switch raw.kind {
36+
case ${', '. join(sub_kinds)}: return true
37+
default: return false
38+
}
39+
% else:
40+
return raw.kind == .${node.swift_syntax_kind}
41+
% end
42+
}
43+
44+
public var raw: RawSyntax
45+
init(raw: RawSyntax) {
46+
assert(Self.isKindOf(raw))
47+
self.raw = raw
48+
}
49+
50+
public init?<Node: RawSyntaxNodeProtocol>(_ other: Node) {
51+
guard Self.isKindOf(other.raw) else { return nil }
52+
self.init(raw: other.raw)
53+
}
54+
55+
% if node.is_base():
56+
public init<Node: Raw${node.name}NodeProtocol>(_ other: Node) {
57+
self.init(raw: other.raw)
58+
}
59+
% end
60+
%
61+
% if node.is_syntax_collection():
62+
public init<C: Collection>(elements: C, arena: SyntaxArena) where C.Element == Raw${node.collection_element_type} {
63+
let raw = RawSyntax.makeLayout(kind: .${node.swift_syntax_kind}, from: elements.map { $0.raw }, arena: arena)
64+
self.init(raw: raw)
65+
}
66+
67+
public var elements: [Raw${node.collection_element_type}] {
68+
raw.children.map { Raw${node.collection_element_type}(raw: $0!) }
69+
}
70+
% end
71+
%
72+
% if node.is_buildable() or node.is_missing():
73+
public init(
74+
% for child in node.children:
75+
% param_label = "_ " if child.is_garbage_nodes() else ""
76+
% optional_mark = "?" if child.is_optional else ""
77+
% param_type = "Raw" + child.type_name + optional_mark
78+
% param_default = " = nil" if child.is_garbage_nodes() else ""
79+
${param_label}${child.swift_name}: ${param_type}${param_default},
80+
% end
81+
arena: SyntaxArena
82+
) {
83+
% if node.children:
84+
let raw = RawSyntax.makeLayout(
85+
kind: .${node.swift_syntax_kind}, uninitializedCount: ${len(node.children)}, arena: arena) { layout in
86+
layout.initialize(repeating: nil)
87+
% for idx, child in enumerate(node.children):
88+
% optional_mark = "?" if child.is_optional else ""
89+
layout[${idx}] = ${child.swift_name}${optional_mark}.raw
90+
% end
91+
}
92+
% else:
93+
let raw = RawSyntax.makeEmptyLayout(kind: .${node.swift_syntax_kind}, arena: arena)
94+
% end
95+
self.init(raw: raw)
96+
}
97+
98+
% for idx, child in enumerate(node.children):
99+
% optional_mark = "?" if child.is_optional else ""
100+
% iuo_mark = "!" if not child.is_optional else ""
101+
public var ${child.swift_name}: Raw${child.type_name + optional_mark} {
102+
% if child.type_name == "Syntax":
103+
raw.children[${idx}]${iuo_mark}
104+
% else:
105+
raw.children[${idx}].map(Raw${child.type_name}.init(raw:))${iuo_mark}
106+
% end
107+
}
108+
% end
109+
% end
110+
}
111+
% end
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
%{
2+
from gyb_syntax_support import *
3+
# -*- mode: Swift -*-
4+
# Ignore the following admonition; it applies to the resulting .swift file only
5+
}%
6+
//// Automatically Generated From RawSyntaxValidation.swift.gyb.
7+
//// Do Not Edit Directly!
8+
//===----------- RawSyntaxValidation.swift - Layout validation ------------===//
9+
//
10+
// This source file is part of the Swift.org open source project
11+
//
12+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
13+
// Licensed under Apache License v2.0 with Runtime Library Exception
14+
//
15+
// See https://swift.org/LICENSE.txt for license information
16+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
21+
/// Check that the `layout` is valid for the given 'SyntaxKind'.
22+
///
23+
/// Note that this only validates the immediate children.
24+
/// Results in an assertion failure if the layout is invalid.
25+
func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
26+
#if DEBUG
27+
func _verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node.Type, file: StaticString = #file, line: UInt = #line) {
28+
assert(raw != nil, file: file, line: line)
29+
assert(Node.isKindOf(raw!), file: file, line: line)
30+
}
31+
func _verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node?.Type, file: StaticString = #file, line: UInt = #line) {
32+
raw.map { assert(Node.isKindOf($0), file: file, line: line) }
33+
}
34+
switch kind {
35+
case .unknown:
36+
break
37+
case .token:
38+
assertionFailure("validateLayout for .token kind is not supported")
39+
% for node in NON_BASE_SYNTAX_NODES:
40+
case .${node.swift_syntax_kind}:
41+
% if node.is_buildable() or node.is_missing():
42+
assert(layout.count == ${len(node.children)})
43+
% for (idx, child) in enumerate(node.children):
44+
_verify(layout[${idx}], as: Raw${child.type_name}${"?" if child.is_optional else ""}.self)
45+
% end
46+
% elif node.is_syntax_collection():
47+
for element in layout {
48+
_verify(element, as: Raw${node.collection_element_type}.self)
49+
}
50+
% end
51+
break
52+
% end
53+
}
54+
#endif
55+
}

Sources/SwiftSyntax/Syntax.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
2424
self.data = data
2525
}
2626

27-
public func _validateLayout() {
28-
// Check the layout of the concrete type
29-
return self.asProtocol(SyntaxProtocol.self)._validateLayout()
27+
@_spi(RawSyntax)
28+
public init(raw: RawSyntax) {
29+
self.init(.forRoot(raw))
3030
}
3131

3232
/// Create a `Syntax` node from a specialized syntax node.
@@ -114,11 +114,6 @@ public protocol SyntaxProtocol: CustomStringConvertible,
114114
/// conversion is not possible.
115115
init?(_ syntaxNode: Syntax)
116116

117-
/// Check that the raw layout of this node is valid. Used to verify a node's
118-
/// integrity after it has been rewritten by the syntax rewriter.
119-
/// Results in an assertion failure if the layout is invalid.
120-
func _validateLayout()
121-
122117
/// Returns the underlying syntax node type.
123118
var syntaxNodeType: SyntaxProtocol.Type { get }
124119
}

0 commit comments

Comments
 (0)