Skip to content

Commit 54e0aaf

Browse files
committed
Prevent Unrelated Casts on Child Choice Node
1 parent af32a12 commit 54e0aaf

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
112112

113113
StmtSyntax("return .choices(\(choices))")
114114
}
115+
116+
for choiceNodeName in node.elementChoices {
117+
let choiceNode = SYNTAX_NODE_MAP[choiceNodeName]!
118+
choiceNodeCastingMethods(for: choiceNode.kind)
119+
}
115120
}
116121
}
117122

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
17+
@MemberBlockItemListBuilder
18+
func choiceNodeCastingMethods(for syntaxNodeKind: SyntaxNodeKind) -> MemberBlockItemListSyntax {
19+
if syntaxNodeKind.isBase {
20+
DeclSyntax(
21+
"""
22+
public func `is`<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> Bool {
23+
return self.as(syntaxType) != nil
24+
}
25+
"""
26+
)
27+
28+
DeclSyntax(
29+
"""
30+
public func `as`<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> S? {
31+
return S.init(self)
32+
}
33+
"""
34+
)
35+
36+
DeclSyntax(
37+
"""
38+
public func cast<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> S {
39+
return self.as(S.self)!
40+
}
41+
"""
42+
)
43+
} else {
44+
DeclSyntax(
45+
"""
46+
public func `is`(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> Bool {
47+
return self.as(syntaxType) != nil
48+
}
49+
"""
50+
)
51+
52+
DeclSyntax(
53+
"""
54+
public func `as`(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> \(syntaxNodeKind.syntaxType)? {
55+
return \(syntaxNodeKind.syntaxType).init(self)
56+
}
57+
"""
58+
)
59+
60+
DeclSyntax(
61+
"""
62+
public func cast(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> \(syntaxNodeKind.syntaxType) {
63+
return self.as(\(syntaxNodeKind.syntaxType).self)!
64+
}
65+
"""
66+
)
67+
}
68+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,9 @@ private func generateSyntaxChildChoices(for child: Child) throws -> EnumDeclSynt
289289

290290
StmtSyntax("return .choices(\(choices))")
291291
}
292+
293+
for choiceNode in choices {
294+
choiceNodeCastingMethods(for: choiceNode.syntaxNodeKind)
295+
}
292296
}
293297
}

Release Notes/510.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
- Description: `is`, `as`, and `cast` methods on base node protocols with base-type conversions are marked as deprecated. The deprecated methods emit a warning that informs the developer that the cast will always succeed and should be done using the base node's initializer.
5050
- Issue: https://github.com/apple/swift-syntax/issues/2092
5151
- Pull Request: https://github.com/apple/swift-syntax/pull/2108
52+
53+
- Child Choice Node Casts
54+
- Description: TODO: Add!
55+
- Issue: https://github.com/apple/swift-syntax/issues/2092
56+
- Pull Request: https://github.com/apple/swift-syntax/pull/2184
5257

5358
## API-Incompatible Changes
5459

Sources/SwiftSyntax/SyntaxProtocol.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,3 +698,38 @@ public extension SyntaxProtocol {
698698
/// Protocol for the enums nested inside ``Syntax`` nodes that enumerate all the
699699
/// possible types a child node might have.
700700
public protocol SyntaxChildChoices: SyntaxProtocol {}
701+
702+
public extension SyntaxChildChoices {
703+
704+
/// Checks if the current ``SyntaxChildChoices`` instance can be cast to a given specialized syntax type.
705+
///
706+
/// - Returns: `true` if the node can be cast, `false` otherwise.
707+
///
708+
/// - Note: This method is marked as deprecated because it is advised not to use it for unrelated casts.
709+
@available(*, deprecated, message: "This cast will always fail")
710+
func `is`<S: SyntaxProtocol>(_ syntaxType: S.Type) -> Bool {
711+
return self.as(syntaxType) != nil
712+
}
713+
714+
/// Attempts to cast the current ``SyntaxChildChoices`` instance to a given specialized syntax type.
715+
///
716+
/// - Returns: An instance of the specialized syntax type, or `nil` if the cast fails.
717+
///
718+
/// - Note: This method is marked as deprecated because it is advised not to use it for unrelated casts.
719+
@available(*, deprecated, message: "This cast will always fail")
720+
func `as`<S: SyntaxProtocol>(_ syntaxType: S.Type) -> S? {
721+
return S.init(self)
722+
}
723+
724+
/// Force-casts the current ``SyntaxChildChoices`` instance to a given specialized syntax type.
725+
///
726+
/// - Returns: An instance of the specialized syntax type.
727+
///
728+
/// - Warning: This function will crash if the cast is not possible. Use `as` for a safe attempt.
729+
///
730+
/// - Note: This method is marked as deprecated because it is advised not to use it for unrelated casts.
731+
@available(*, deprecated, message: "This cast will always fail")
732+
func cast<S: SyntaxProtocol>(_ syntaxType: S.Type) -> S {
733+
return self.as(S.self)!
734+
}
735+
}

0 commit comments

Comments
 (0)