Skip to content

Commit

Permalink
Prevent Unrelated Casts on Child Choice Node
Browse files Browse the repository at this point in the history
  • Loading branch information
Matejkob committed Sep 14, 2023
1 parent af32a12 commit 9e7c4ce
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {

StmtSyntax("return .choices(\(choices))")
}

for choiceNodeName in node.elementChoices {
let choiceNode = SYNTAX_NODE_MAP[choiceNodeName]!
choiceNodeCastingMethods(for: choiceNode.kind)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxBuilder
import SyntaxSupport

@MemberBlockItemListBuilder
func choiceNodeCastingMethods(for syntaxNodeKind: SyntaxNodeKind) -> MemberBlockItemListSyntax {
if syntaxNodeKind.isBase {
DeclSyntax(
"""
public func `is`<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> Bool {
return self.as(syntaxType) != nil
}
"""
)

DeclSyntax(
"""
public func `as`<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> S? {
return S.init(self)
}
"""
)

DeclSyntax(
"""
public func cast<S: \(syntaxNodeKind.protocolType)>(_ syntaxType: S.Type) -> S {
return self.as(S.self)!
}
"""
)
} else {
DeclSyntax(
"""
public func `is`(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> Bool {
return self.as(syntaxType) != nil
}
"""
)

DeclSyntax(
"""
public func `as`(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> \(syntaxNodeKind.syntaxType)? {
return \(syntaxNodeKind.syntaxType).init(self)
}
"""
)

DeclSyntax(
"""
public func cast(_ syntaxType: \(syntaxNodeKind.syntaxType).Type) -> \(syntaxNodeKind.syntaxType) {
return self.as(\(syntaxNodeKind.syntaxType).self)!
}
"""
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,9 @@ private func generateSyntaxChildChoices(for child: Child) throws -> EnumDeclSynt

StmtSyntax("return .choices(\(choices))")
}

for choiceNode in choices {
choiceNodeCastingMethods(for: choiceNode.syntaxNodeKind)
}
}
}
5 changes: 5 additions & 0 deletions Release Notes/510.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
- 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.
- Issue: https://github.com/apple/swift-syntax/issues/2092
- Pull Request: https://github.com/apple/swift-syntax/pull/2108

- Child Choice Node Casts
- Description: TODO: Add!
- Issue: https://github.com/apple/swift-syntax/issues/2092
- Pull Request: https://github.com/apple/swift-syntax/pull/2184

## API-Incompatible Changes

Expand Down
17 changes: 17 additions & 0 deletions Sources/SwiftSyntax/SyntaxProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -698,3 +698,20 @@ public extension SyntaxProtocol {
/// Protocol for the enums nested inside ``Syntax`` nodes that enumerate all the
/// possible types a child node might have.
public protocol SyntaxChildChoices: SyntaxProtocol {}

public extension SyntaxChildChoices {
@available(*, deprecated, message: "This cast will always fail")
func `is`<S: SyntaxProtocol>(_ syntaxType: S.Type) -> Bool {
return self.as(syntaxType) != nil
}

@available(*, deprecated, message: "This cast will always fail")
func `as`<S: SyntaxProtocol>(_ syntaxType: S.Type) -> S? {
return S.init(self)
}

@available(*, deprecated, message: "This cast will always fail")
func cast<S: SyntaxProtocol>(_ syntaxType: S.Type) -> S {
return self.as(S.self)!
}
}

0 comments on commit 9e7c4ce

Please sign in to comment.