1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
//===----------------------------------------------------------------------===//
//
// 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
/// Something that can either be a node of the given kind or a token of the
/// given token kind.
public enum SyntaxOrTokenNodeKind: Hashable {
case node(kind: SyntaxNodeKind)
case token(Token)
}
/// Extension to the ``Child`` type to provide functionality specific to
/// SwiftSyntaxBuilder.
extension Child {
/// The type of this child, represented by a ``SyntaxBuildableType``, which can
/// be used to create the corresponding `Buildable` and `ExpressibleAs` types.
public var buildableType: SyntaxBuildableType {
let buildableKind: SyntaxOrTokenNodeKind
switch kind {
case .node(kind: let kind):
buildableKind = .node(kind: kind)
case .nodeChoices:
buildableKind = .node(kind: .syntax)
case .collection(kind: let kind, _, _, _):
buildableKind = .node(kind: kind)
case .token:
buildableKind = .token(self.tokenKind!)
}
return SyntaxBuildableType(
kind: buildableKind,
isOptional: isOptional
)
}
public var parameterBaseType: TypeSyntax {
switch kind {
case .nodeChoices:
return self.syntaxChoicesType
default:
return buildableType.parameterBaseType
}
}
public var parameterType: TypeSyntax {
return self.buildableType.optionalWrapped(type: parameterBaseType)
}
public var defaultValue: ExprSyntax? {
if isOptional || isUnexpectedNodes {
if buildableType.isBaseType && kind.isNodeChoicesEmpty {
return ExprSyntax("\(buildableType.buildable).none")
} else {
return ExprSyntax("nil")
}
}
if case .collection(_, _, defaultsToEmpty: true, _) = kind {
return ExprSyntax("[]")
}
guard let token = token, isToken else {
return buildableType.defaultValue
}
if token.text != nil {
return ExprSyntax(".\(token.varOrCaseName)Token()")
}
if case .token(let choices, _, _) = kind,
case .keyword(let keyword) = choices.only
{
return ExprSyntax(".\(token.varOrCaseName)(.\(keyword.spec.varOrCaseName))")
}
return nil
}
/// If the child node has a default value, return an expression of the form
/// ` = default_value` that can be used as the default value to for a
/// function parameter. Otherwise, return `nil`.
public var defaultInitialization: InitializerClauseSyntax? {
if let defaultValue {
return InitializerClauseSyntax(
equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space),
value: defaultValue
)
} else {
return nil
}
}
/// If this node is a token that can't contain arbitrary text, generate a Swift
/// `precondition` statement that verifies the variable with name var_name and of type
/// ``TokenSyntax`` contains one of the supported text options. Otherwise return `nil`.
public func generateAssertStmtTextChoices(varName: String) -> FunctionCallExprSyntax? {
guard case .token(choices: let choices, requiresLeadingSpace: _, requiresTrailingSpace: _) = kind else {
return nil
}
let tokenCanContainArbitraryText = choices.contains {
switch $0 {
case .token(let token):
return token.spec.text == nil
case .keyword:
return false
}
}
let choicesTexts: [String]
if tokenCanContainArbitraryText {
// Don't generate a precondition statement if token can contain arbitrary text.
return nil
} else if !choices.isEmpty {
choicesTexts = choices.compactMap {
switch $0 {
case .token(let token):
return token.spec.text
case .keyword(let keyword):
return keyword.spec.name
}
}
} else {
return nil
}
var preconditionChoices: [ExprSyntax] = []
if buildableType.isOptional {
preconditionChoices.append(
ExprSyntax(
SequenceExprSyntax {
DeclReferenceExprSyntax(baseName: .identifier(varName))
BinaryOperatorExprSyntax(text: "==")
NilLiteralExprSyntax()
}
)
)
}
for textChoice in choicesTexts {
preconditionChoices.append(
ExprSyntax(
SequenceExprSyntax {
MemberAccessExprSyntax(
base: buildableType.forceUnwrappedIfNeeded(expr: DeclReferenceExprSyntax(baseName: .identifier(varName))),
name: "text"
)
BinaryOperatorExprSyntax(text: "==")
StringLiteralExprSyntax(content: textChoice)
}
)
)
}
let disjunction = ExprListSyntax(
preconditionChoices.flatMap { [$0, ExprSyntax(BinaryOperatorExprSyntax(text: "||"))] }.dropLast()
)
return FunctionCallExprSyntax(callee: ExprSyntax("precondition")) {
LabeledExprSyntax(expression: SequenceExprSyntax(elements: disjunction))
}
}
}
|