Skip to content

Commit 2861240

Browse files
committed
Add casting methods to protocols
These return the non type-erased types of a node if it conforms to the given protocol and nil otherwise. With these methods returning the non type-erased type we can get rid of _asConcreteType.
1 parent 40a46ab commit 2861240

17 files changed

+1012
-622
lines changed

Changelog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ For increased performance, the modelling of the syntax node hierarchy has been s
101101
exprSyntax.is(IdentifierExprSyntax.self)
102102
```
103103

104+
- To retrieve the non-type erased version of a type, use the `as(_: SyntaxProtocol.self)` method
105+
106+
```swift
107+
let identifierExprSyntax: IdentifierExprSyntax = /* ... */
108+
let node = Syntax(identifierExprSyntax)
109+
node.as(SyntaxProtocol.self) // returns a IdentifierExprSyntax with static type SyntaxProtocol
110+
node.as(ExprSyntaxProtocol.self) // returns a IdentifierExprSyntax with static type ExprSyntaxProtocol?
111+
```
112+
104113

105114
- Downcasting can no longer be performed using the `as` operator. For downcasting use the `as(_: SyntaxProtocol)` method on any type eraser. ([#155](https://github.com/apple/swift-syntax/pull/155))
106115

Sources/SwiftSyntax/Misc.swift.gyb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,16 @@ extension SyntaxNode {
3939
% end
4040
}
4141

42-
public extension Syntax {
43-
/// Retrieve the concretely typed node that this Syntax node wraps.
44-
/// This property is exposed for testing purposes only.
45-
var _asConcreteType: Any {
42+
extension Syntax {
43+
/// Syntax nodes always conform to SyntaxProtocol. This API is just added
44+
/// for consistency.
45+
@available(*, deprecated, message: "Expression always evaluates to true")
46+
public func `is`(_: SyntaxProtocol.Protocol) -> Bool {
47+
return true
48+
}
49+
50+
/// Return the non-type erased version of this syntax node.
51+
public func `as`(_: SyntaxProtocol.Protocol) -> SyntaxProtocol {
4652
switch self.as(SyntaxEnum.self) {
4753
case .token(let node):
4854
return node

Sources/SwiftSyntax/Syntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ extension Syntax: CustomReflectable {
5353
/// Reconstructs the real syntax type for this type from the node's kind and
5454
/// provides a mirror that reflects this type.
5555
public var customMirror: Mirror {
56-
return Mirror(reflecting: self._asConcreteType)
56+
return Mirror(reflecting: self.as(SyntaxProtocol.self))
5757
}
5858
}
5959

Sources/SwiftSyntax/SyntaxBaseNodes.swift.gyb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@
2929
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
3030
public protocol ${node.name}Protocol: ${base_type}Protocol {}
3131

32+
public extension Syntax {
33+
/// Check whether the non-type erased version of this syntax node conforms to
34+
/// ${node.name}Protocol.
35+
func `is`(_: ${node.name}Protocol.Protocol) -> Bool {
36+
return self.as(${node.name}Protocol.self) != nil
37+
}
38+
39+
/// Return the non-type erased version of this syntax node if it conforms to
40+
/// ${node.name}Protocol. Otherwise return nil.
41+
func `as`(_: ${node.name}Protocol.Protocol) -> ${node.name}Protocol? {
42+
return self.as(SyntaxProtocol.self) as? ${node.name}Protocol
43+
}
44+
}
45+
3246
% for line in dedented_lines(node.description):
3347
/// ${line}
3448
% end
@@ -83,13 +97,25 @@ public struct ${node.name}: ${node.name}Protocol, SyntaxHashable {
8397
public func `as`<S: ${node.name}Protocol>(_ syntaxType: S.Type) -> S? {
8498
return S.init(_syntaxNode)
8599
}
100+
101+
/// Syntax nodes always conform to `${node.name}Protocol`. This API is just
102+
/// added for consistency.
103+
@available(*, deprecated, message: "Expression always evaluates to true")
104+
public func `is`(_: ${node.name}Protocol.Protocol) -> Bool {
105+
return true
106+
}
107+
108+
/// Return the non-type erased version of this syntax node.
109+
public func `as`(_: ${node.name}Protocol.Protocol) -> ${node.name}Protocol {
110+
return Syntax(self).as(${node.name}Protocol.self)!
111+
}
86112
}
87113

88114
extension ${node.name}: CustomReflectable {
89115
/// Reconstructs the real syntax type for this type from the node's kind and
90116
/// provides a mirror that reflects this type.
91117
public var customMirror: Mirror {
92-
return Mirror(reflecting: Syntax(self)._asConcreteType)
118+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
93119
}
94120
}
95121

Sources/SwiftSyntax/SyntaxNodes.swift.gyb.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ extension ${node.name}: CustomReflectable {
162162
return Mirror(self, children: [
163163
% for child in node.children:
164164
% if child.is_optional:
165-
"${child.swift_name}": ${child.swift_name}.map(Syntax.init)?._asConcreteType as Any,
165+
"${child.swift_name}": ${child.swift_name}.map(Syntax.init)?.as(SyntaxProtocol.self) as Any,
166166
% else:
167-
"${child.swift_name}": Syntax(${child.swift_name})._asConcreteType,
167+
"${child.swift_name}": Syntax(${child.swift_name}).as(SyntaxProtocol.self),
168168
% end
169169
% end
170170
])

Sources/SwiftSyntax/SyntaxTraits.swift.gyb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
//===----------------------------------------------------------------------===//
2121

2222
% for trait in TRAITS:
23+
// MARK: - ${trait.trait_name}Syntax
24+
2325
public protocol ${trait.trait_name}Syntax: SyntaxProtocol {
2426
% for child in trait.children:
2527
% ret_type = child.type_name
@@ -30,6 +32,21 @@ public protocol ${trait.trait_name}Syntax: SyntaxProtocol {
3032
func with${child.name}(_ newChild: ${child.type_name}?) -> Self
3133
% end
3234
}
35+
36+
public extension SyntaxProtocol {
37+
/// Check whether the non-type erased version of this syntax node conforms to
38+
/// `${trait.trait_name}Syntax`.
39+
func `is`(_: ${trait.trait_name}Syntax.Protocol) -> Bool {
40+
return self.as(${trait.trait_name}Syntax.self) != nil
41+
}
42+
43+
/// Return the non-type erased version of this syntax node if it conforms to
44+
/// `${trait.trait_name}Syntax`. Otherwise return `nil`.
45+
func `as`(_: ${trait.trait_name}Syntax.Protocol) -> ${trait.trait_name}Syntax? {
46+
return Syntax(self).as(SyntaxProtocol.self) as? ${trait.trait_name}Syntax
47+
}
48+
}
49+
3350
% end
3451

3552
% for node in SYNTAX_NODES:

Sources/SwiftSyntax/gyb_generated/Misc.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,10 +1348,16 @@ extension SyntaxNode {
13481348
}
13491349
}
13501350

1351-
public extension Syntax {
1352-
/// Retrieve the concretely typed node that this Syntax node wraps.
1353-
/// This property is exposed for testing purposes only.
1354-
var _asConcreteType: Any {
1351+
extension Syntax {
1352+
/// Syntax nodes always conform to SyntaxProtocol. This API is just added
1353+
/// for consistency.
1354+
@available(*, deprecated, message: "Expression always evaluates to true")
1355+
public func `is`(_: SyntaxProtocol.Protocol) -> Bool {
1356+
return true
1357+
}
1358+
1359+
/// Return the non-type erased version of this syntax node.
1360+
public func `as`(_: SyntaxProtocol.Protocol) -> SyntaxProtocol {
13551361
switch self.as(SyntaxEnum.self) {
13561362
case .token(let node):
13571363
return node

Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@
1919
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
2020
public protocol DeclSyntaxProtocol: SyntaxProtocol {}
2121

22+
public extension Syntax {
23+
/// Check whether the non-type erased version of this syntax node conforms to
24+
/// DeclSyntaxProtocol.
25+
func `is`(_: DeclSyntaxProtocol.Protocol) -> Bool {
26+
return self.as(DeclSyntaxProtocol.self) != nil
27+
}
28+
29+
/// Return the non-type erased version of this syntax node if it conforms to
30+
/// DeclSyntaxProtocol. Otherwise return nil.
31+
func `as`(_: DeclSyntaxProtocol.Protocol) -> DeclSyntaxProtocol? {
32+
return self.as(SyntaxProtocol.self) as? DeclSyntaxProtocol
33+
}
34+
}
35+
2236
public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable {
2337
public let _syntaxNode: Syntax
2438

@@ -64,13 +78,25 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable {
6478
public func `as`<S: DeclSyntaxProtocol>(_ syntaxType: S.Type) -> S? {
6579
return S.init(_syntaxNode)
6680
}
81+
82+
/// Syntax nodes always conform to `DeclSyntaxProtocol`. This API is just
83+
/// added for consistency.
84+
@available(*, deprecated, message: "Expression always evaluates to true")
85+
public func `is`(_: DeclSyntaxProtocol.Protocol) -> Bool {
86+
return true
87+
}
88+
89+
/// Return the non-type erased version of this syntax node.
90+
public func `as`(_: DeclSyntaxProtocol.Protocol) -> DeclSyntaxProtocol {
91+
return Syntax(self).as(DeclSyntaxProtocol.self)!
92+
}
6793
}
6894

6995
extension DeclSyntax: CustomReflectable {
7096
/// Reconstructs the real syntax type for this type from the node's kind and
7197
/// provides a mirror that reflects this type.
7298
public var customMirror: Mirror {
73-
return Mirror(reflecting: Syntax(self)._asConcreteType)
99+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
74100
}
75101
}
76102

@@ -81,6 +107,20 @@ extension DeclSyntax: CustomReflectable {
81107
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
82108
public protocol ExprSyntaxProtocol: SyntaxProtocol {}
83109

110+
public extension Syntax {
111+
/// Check whether the non-type erased version of this syntax node conforms to
112+
/// ExprSyntaxProtocol.
113+
func `is`(_: ExprSyntaxProtocol.Protocol) -> Bool {
114+
return self.as(ExprSyntaxProtocol.self) != nil
115+
}
116+
117+
/// Return the non-type erased version of this syntax node if it conforms to
118+
/// ExprSyntaxProtocol. Otherwise return nil.
119+
func `as`(_: ExprSyntaxProtocol.Protocol) -> ExprSyntaxProtocol? {
120+
return self.as(SyntaxProtocol.self) as? ExprSyntaxProtocol
121+
}
122+
}
123+
84124
public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
85125
public let _syntaxNode: Syntax
86126

@@ -126,13 +166,25 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
126166
public func `as`<S: ExprSyntaxProtocol>(_ syntaxType: S.Type) -> S? {
127167
return S.init(_syntaxNode)
128168
}
169+
170+
/// Syntax nodes always conform to `ExprSyntaxProtocol`. This API is just
171+
/// added for consistency.
172+
@available(*, deprecated, message: "Expression always evaluates to true")
173+
public func `is`(_: ExprSyntaxProtocol.Protocol) -> Bool {
174+
return true
175+
}
176+
177+
/// Return the non-type erased version of this syntax node.
178+
public func `as`(_: ExprSyntaxProtocol.Protocol) -> ExprSyntaxProtocol {
179+
return Syntax(self).as(ExprSyntaxProtocol.self)!
180+
}
129181
}
130182

131183
extension ExprSyntax: CustomReflectable {
132184
/// Reconstructs the real syntax type for this type from the node's kind and
133185
/// provides a mirror that reflects this type.
134186
public var customMirror: Mirror {
135-
return Mirror(reflecting: Syntax(self)._asConcreteType)
187+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
136188
}
137189
}
138190

@@ -143,6 +195,20 @@ extension ExprSyntax: CustomReflectable {
143195
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
144196
public protocol StmtSyntaxProtocol: SyntaxProtocol {}
145197

198+
public extension Syntax {
199+
/// Check whether the non-type erased version of this syntax node conforms to
200+
/// StmtSyntaxProtocol.
201+
func `is`(_: StmtSyntaxProtocol.Protocol) -> Bool {
202+
return self.as(StmtSyntaxProtocol.self) != nil
203+
}
204+
205+
/// Return the non-type erased version of this syntax node if it conforms to
206+
/// StmtSyntaxProtocol. Otherwise return nil.
207+
func `as`(_: StmtSyntaxProtocol.Protocol) -> StmtSyntaxProtocol? {
208+
return self.as(SyntaxProtocol.self) as? StmtSyntaxProtocol
209+
}
210+
}
211+
146212
public struct StmtSyntax: StmtSyntaxProtocol, SyntaxHashable {
147213
public let _syntaxNode: Syntax
148214

@@ -188,13 +254,25 @@ public struct StmtSyntax: StmtSyntaxProtocol, SyntaxHashable {
188254
public func `as`<S: StmtSyntaxProtocol>(_ syntaxType: S.Type) -> S? {
189255
return S.init(_syntaxNode)
190256
}
257+
258+
/// Syntax nodes always conform to `StmtSyntaxProtocol`. This API is just
259+
/// added for consistency.
260+
@available(*, deprecated, message: "Expression always evaluates to true")
261+
public func `is`(_: StmtSyntaxProtocol.Protocol) -> Bool {
262+
return true
263+
}
264+
265+
/// Return the non-type erased version of this syntax node.
266+
public func `as`(_: StmtSyntaxProtocol.Protocol) -> StmtSyntaxProtocol {
267+
return Syntax(self).as(StmtSyntaxProtocol.self)!
268+
}
191269
}
192270

193271
extension StmtSyntax: CustomReflectable {
194272
/// Reconstructs the real syntax type for this type from the node's kind and
195273
/// provides a mirror that reflects this type.
196274
public var customMirror: Mirror {
197-
return Mirror(reflecting: Syntax(self)._asConcreteType)
275+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
198276
}
199277
}
200278

@@ -205,6 +283,20 @@ extension StmtSyntax: CustomReflectable {
205283
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
206284
public protocol TypeSyntaxProtocol: SyntaxProtocol {}
207285

286+
public extension Syntax {
287+
/// Check whether the non-type erased version of this syntax node conforms to
288+
/// TypeSyntaxProtocol.
289+
func `is`(_: TypeSyntaxProtocol.Protocol) -> Bool {
290+
return self.as(TypeSyntaxProtocol.self) != nil
291+
}
292+
293+
/// Return the non-type erased version of this syntax node if it conforms to
294+
/// TypeSyntaxProtocol. Otherwise return nil.
295+
func `as`(_: TypeSyntaxProtocol.Protocol) -> TypeSyntaxProtocol? {
296+
return self.as(SyntaxProtocol.self) as? TypeSyntaxProtocol
297+
}
298+
}
299+
208300
public struct TypeSyntax: TypeSyntaxProtocol, SyntaxHashable {
209301
public let _syntaxNode: Syntax
210302

@@ -250,13 +342,25 @@ public struct TypeSyntax: TypeSyntaxProtocol, SyntaxHashable {
250342
public func `as`<S: TypeSyntaxProtocol>(_ syntaxType: S.Type) -> S? {
251343
return S.init(_syntaxNode)
252344
}
345+
346+
/// Syntax nodes always conform to `TypeSyntaxProtocol`. This API is just
347+
/// added for consistency.
348+
@available(*, deprecated, message: "Expression always evaluates to true")
349+
public func `is`(_: TypeSyntaxProtocol.Protocol) -> Bool {
350+
return true
351+
}
352+
353+
/// Return the non-type erased version of this syntax node.
354+
public func `as`(_: TypeSyntaxProtocol.Protocol) -> TypeSyntaxProtocol {
355+
return Syntax(self).as(TypeSyntaxProtocol.self)!
356+
}
253357
}
254358

255359
extension TypeSyntax: CustomReflectable {
256360
/// Reconstructs the real syntax type for this type from the node's kind and
257361
/// provides a mirror that reflects this type.
258362
public var customMirror: Mirror {
259-
return Mirror(reflecting: Syntax(self)._asConcreteType)
363+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
260364
}
261365
}
262366

@@ -267,6 +371,20 @@ extension TypeSyntax: CustomReflectable {
267371
/// DO NOT CONFORM TO THIS PROTOCOL YOURSELF!
268372
public protocol PatternSyntaxProtocol: SyntaxProtocol {}
269373

374+
public extension Syntax {
375+
/// Check whether the non-type erased version of this syntax node conforms to
376+
/// PatternSyntaxProtocol.
377+
func `is`(_: PatternSyntaxProtocol.Protocol) -> Bool {
378+
return self.as(PatternSyntaxProtocol.self) != nil
379+
}
380+
381+
/// Return the non-type erased version of this syntax node if it conforms to
382+
/// PatternSyntaxProtocol. Otherwise return nil.
383+
func `as`(_: PatternSyntaxProtocol.Protocol) -> PatternSyntaxProtocol? {
384+
return self.as(SyntaxProtocol.self) as? PatternSyntaxProtocol
385+
}
386+
}
387+
270388
public struct PatternSyntax: PatternSyntaxProtocol, SyntaxHashable {
271389
public let _syntaxNode: Syntax
272390

@@ -312,13 +430,25 @@ public struct PatternSyntax: PatternSyntaxProtocol, SyntaxHashable {
312430
public func `as`<S: PatternSyntaxProtocol>(_ syntaxType: S.Type) -> S? {
313431
return S.init(_syntaxNode)
314432
}
433+
434+
/// Syntax nodes always conform to `PatternSyntaxProtocol`. This API is just
435+
/// added for consistency.
436+
@available(*, deprecated, message: "Expression always evaluates to true")
437+
public func `is`(_: PatternSyntaxProtocol.Protocol) -> Bool {
438+
return true
439+
}
440+
441+
/// Return the non-type erased version of this syntax node.
442+
public func `as`(_: PatternSyntaxProtocol.Protocol) -> PatternSyntaxProtocol {
443+
return Syntax(self).as(PatternSyntaxProtocol.self)!
444+
}
315445
}
316446

317447
extension PatternSyntax: CustomReflectable {
318448
/// Reconstructs the real syntax type for this type from the node's kind and
319449
/// provides a mirror that reflects this type.
320450
public var customMirror: Mirror {
321-
return Mirror(reflecting: Syntax(self)._asConcreteType)
451+
return Mirror(reflecting: Syntax(self).as(SyntaxProtocol.self))
322452
}
323453
}
324454

0 commit comments

Comments
 (0)