Skip to content

Commit 2b61d4e

Browse files
authored
SwiftSyntax: add a mechanism to define traits of syntax nodes to allow abstract access to popular child kinds. NFC (#14668)
Swift syntax APIs lack an abstract way of accessing children. The client has to down-cast a syntax node to the leaf type to access any of its children. However, some children are common among different syntax kinds, e.g. DeclAttributeSyntax and DeclMembers. We should allow an abstract way to access and modify them, so that clients can avoid logic duplication. This patch adds a mechanism to define new traits and specify satisfied traits in specific syntax nodes. A trait is a set of common children and implemented in Swift as a protocol for syntax nodes to conform to. As a proof-of-concept, we added two traits for now including DeclGroupSyntax and BracedSyntax. Resolves: SR-6931 and SR-6916
1 parent 220883f commit 2b61d4e

File tree

7 files changed

+59
-12
lines changed

7 files changed

+59
-12
lines changed

tools/SwiftSyntax/SyntaxNodes.swift.gyb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
%{
22
# -*- mode: Swift -*-
33
from gyb_syntax_support import *
4+
from gyb_syntax_support.Traits import TRAITS
45
NODE_MAP = create_node_map()
56
# Ignore the following admonition it applies to the resulting .swift file only
67
}%
@@ -53,6 +54,19 @@ public struct UnknownSyntax: _SyntaxBase {
5354
}
5455
}
5556

57+
% for trait in TRAITS:
58+
public protocol ${trait.trait_name} {
59+
% for child in trait.children:
60+
% ret_type = child.type_name
61+
% if child.is_optional:
62+
% ret_type += '?'
63+
% end
64+
var ${child.swift_name}: ${ret_type} { get }
65+
func with${child.name}(_ newChild: ${child.type_name}?) -> Self
66+
% end
67+
}
68+
% end
69+
5670
% for node in SYNTAX_NODES:
5771
% base_type = node.base_type
5872
% if node.is_base():
@@ -61,7 +75,12 @@ public protocol ${node.name}: Syntax {}
6175
% elif node.collection_element:
6276
% pass
6377
% else:
64-
public struct ${node.name}: ${base_type}, _SyntaxBase, Hashable {
78+
% traits_list = ""
79+
% if node.traits:
80+
% traits_list = ", ".join(node.traits)
81+
% traits_list = traits_list + ", "
82+
% end
83+
public struct ${node.name}: ${traits_list} ${base_type}, _SyntaxBase, Hashable {
6584
% if node.children:
6685
enum Cursor: Int {
6786
% for child in node.children:

utils/gyb_syntax_support/CommonNodes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232

3333
# code-block -> '{' stmt-list '}'
3434
Node('CodeBlock', kind='Syntax',
35+
traits=['BracedSyntax'],
3536
children=[
36-
Child('OpenBrace', kind='LeftBraceToken'),
37+
Child('LeftBrace', kind='LeftBraceToken'),
3738
Child('Statements', kind='CodeBlockItemList'),
38-
Child('CloseBrace', kind='RightBraceToken'),
39+
Child('RightBrace', kind='RightBraceToken'),
3940
]),
4041
]

utils/gyb_syntax_support/DeclNodes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
# generic-where-clause?
158158
# '{' class-members '}'
159159
# class-name -> identifier
160-
Node('ClassDecl', kind='Decl',
160+
Node('ClassDecl', kind='Decl', traits=['DeclGroupSyntax'],
161161
children=[
162162
Child('Attributes', kind='AttributeList',
163163
is_optional=True),
@@ -181,7 +181,7 @@
181181
# generic-where-clause?
182182
# '{' struct-members '}'
183183
# struct-name -> identifier
184-
Node('StructDecl', kind='Decl',
184+
Node('StructDecl', kind='Decl', traits=['DeclGroupSyntax'],
185185
children=[
186186
Child('Attributes', kind='AttributeList',
187187
is_optional=True),
@@ -198,7 +198,7 @@
198198
Child('Members', kind='MemberDeclBlock'),
199199
]),
200200

201-
Node('ProtocolDecl', kind='Decl',
201+
Node('ProtocolDecl', kind='Decl', traits=['DeclGroupSyntax'],
202202
children=[
203203
Child('Attributes', kind='AttributeList',
204204
is_optional=True),
@@ -219,7 +219,7 @@
219219
# generic-where-clause?
220220
# '{' extension-members '}'
221221
# extension-name -> identifier
222-
Node('ExtensionDecl', kind='Decl',
222+
Node('ExtensionDecl', kind='Decl', traits=['DeclGroupSyntax'],
223223
children=[
224224
Child('Attributes', kind='AttributeList',
225225
is_optional=True),
@@ -234,7 +234,7 @@
234234
Child('Members', kind='MemberDeclBlock'),
235235
]),
236236

237-
Node('MemberDeclBlock', kind='Syntax',
237+
Node('MemberDeclBlock', kind='Syntax', traits=['BracedSyntax'],
238238
children=[
239239
Child('LeftBrace', kind='LeftBraceToken'),
240240
Child('Members', kind='DeclList'),
@@ -464,7 +464,7 @@
464464

465465
Node('AccessorList', kind="SyntaxCollection", element='AccessorDecl'),
466466

467-
Node('AccessorBlock', kind="Syntax",
467+
Node('AccessorBlock', kind="Syntax", traits=['BracedSyntax'],
468468
children=[
469469
Child('LeftBrace', kind='LeftBraceToken'),
470470
Child('AccessorListOrStmtList', kind='Syntax',

utils/gyb_syntax_support/ExprNodes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@
384384
]),
385385

386386
Node('ClosureExpr', kind='Expr',
387+
traits=['BracedSyntax'],
387388
children=[
388389
Child('LeftBrace', kind='LeftBraceToken'),
389390
Child('Signature', kind='ClosureSignature', is_optional=True),

utils/gyb_syntax_support/Node.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ class Node(object):
1616
subclass.
1717
"""
1818

19-
def __init__(self, name, kind=None, children=None,
19+
def __init__(self, name, kind=None, traits=None, children=None,
2020
element=None, element_name=None, element_choices=None):
2121
self.syntax_kind = name
2222
self.swift_syntax_kind = lowercase_first_word(name)
2323
self.name = kind_to_type(self.syntax_kind)
2424

25+
self.traits = traits or []
2526
self.children = children or []
2627
self.base_kind = kind
2728
self.base_type = kind_to_type(self.base_kind)

utils/gyb_syntax_support/StmtNodes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,17 @@
9191
# switch-stmt -> identifier? ':'? 'switch' expr '{'
9292
# switch-case-list '}' ';'?
9393
Node('SwitchStmt', kind='Stmt',
94+
traits=['BracedSyntax'],
9495
children=[
9596
Child('LabelName', kind='IdentifierToken',
9697
is_optional=True),
9798
Child('LabelColon', kind='ColonToken',
9899
is_optional=True),
99100
Child('SwitchKeyword', kind='SwitchToken'),
100101
Child('Expression', kind='Expr'),
101-
Child('OpenBrace', kind='LeftBraceToken'),
102+
Child('LeftBrace', kind='LeftBraceToken'),
102103
Child('Cases', kind='SwitchCaseList'),
103-
Child('CloseBrace', kind='RightBraceToken'),
104+
Child('RightBrace', kind='RightBraceToken'),
104105
]),
105106

106107
# catch-clause-list -> catch-clause catch-clause-list?

utils/gyb_syntax_support/Traits.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from Child import Child
2+
3+
4+
class Trait(object):
5+
def __init__(self, trait_name, children):
6+
self.trait_name = trait_name
7+
self.children = children
8+
9+
10+
TRAITS = [
11+
Trait('DeclGroupSyntax',
12+
children=[
13+
Child('Attributes', kind='AttributeList', is_optional=True),
14+
Child('AccessLevelModifier', kind='DeclModifier',
15+
is_optional=True),
16+
Child('Members', kind='MemberDeclBlock'),
17+
]),
18+
19+
Trait('BracedSyntax',
20+
children=[
21+
Child('LeftBrace', kind='LeftBraceToken'),
22+
Child('RightBrace', kind='RightBraceToken'),
23+
]),
24+
]

0 commit comments

Comments
 (0)