Skip to content

Commit

Permalink
More examples of autogenerating convenience inits
Browse files Browse the repository at this point in the history
  • Loading branch information
natikgadzhi committed Sep 7, 2023
1 parent 8a30b2d commit 6eacdba
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 79 deletions.
4 changes: 2 additions & 2 deletions CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public let ATTRIBUTE_NODES: [Node] = [
documentation: "An `@` attribute.",
parserFunction: "parseAttribute",
rules: [
NodeInitRule(
ConvenienceInitRule(
nonOptionalChildName: "arguments",
childDefaultValues: [
defaults: [
"leftParen": .leftParen,
"rightParen": .rightParen
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import SwiftSyntax
///
/// The convenience initializer will take a non-optional parameter
/// `nonOptionalChildName`, and when a non-optional value is passed, it'll call
/// the full memberwise initializer with the provided `childDefaultValues`.
/// the full memberwise initializer with the provided `defaults`.
///
/// For example, when initializing an `EnumCaseParameterSyntax`, the convenience
/// initializer will take a non-optional `firstName` parameter, and when it's
/// passed, it'll call the full memberwise initializer with
/// `colon = .colonToken()`.
public struct NodeInitRule {
public struct ConvenienceInitRule {
/// The name of the parameter that is required to be present for
/// this conveniece initializer rule to apply.
public let nonOptionalChildName: String

/// A dicrionary of parameter names to their respective default values
/// to apply when the `nonOptionalChildName` is passed as concrete value.
public let childDefaultValues: [String: Token]
public let defaults: [String: Token]
}
8 changes: 8 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,14 @@ public let DECL_NODES: [Node] = [
nameForDiagnostics: "parameter",
parserFunction: "parseEnumCaseParameter",
traits: ["WithTrailingComma", "WithModifiers"],
rules: [
ConvenienceInitRule(
nonOptionalChildName: "firstName",
defaults: [
"colon": .colon
]
)
],
children: [
Child(
name: "modifiers",
Expand Down
17 changes: 17 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ public let EXPR_NODES: [Node] = [
traits: [
"WithTrailingComma"
],
rules: [
ConvenienceInitRule(
nonOptionalChildName: "name",
defaults: [
"equal": .equal
]
)
],
children: [
Child(
name: "specifier",
Expand Down Expand Up @@ -794,6 +802,15 @@ public let EXPR_NODES: [Node] = [
kind: .functionCallExpr,
base: .expr,
nameForDiagnostics: "function call",
rules: [
ConvenienceInitRule(
nonOptionalChildName: "arguments",
defaults: [
"leftParen": .leftParen,
"rightParen": .rightParen
]
)
],
children: [
Child(
name: "calledExpression",
Expand Down
6 changes: 3 additions & 3 deletions CodeGeneration/Sources/SyntaxSupport/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class Node {

/// List of convenience initializer rules for this node. CodeGeneration will
/// generate a convenience initializer for each rule.
public let rules: [NodeInitRule]
public let rules: [ConvenienceInitRule]

/// If this is a layout node, return a view of the node that provides access
/// to the layout-node specific properties.
Expand Down Expand Up @@ -116,7 +116,7 @@ public class Node {
documentation: String? = nil,
parserFunction: TokenSyntax? = nil,
traits: [String] = [],
rules: [NodeInitRule] = [],
rules: [ConvenienceInitRule] = [],
children: [Child] = []
) {
precondition(base != .syntaxCollection)
Expand Down Expand Up @@ -235,7 +235,7 @@ public class Node {
isExperimental: Bool = false,
nameForDiagnostics: String?,
documentation: String? = nil,
rules: [NodeInitRule] = [],
rules: [ConvenienceInitRule] = [],
parserFunction: TokenSyntax? = nil,
elementChoices: [SyntaxNodeKind]
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ extension LayoutNode {
}

/// Generates a convenience memberwise SyntaxNode initializer based on a
/// given ``NodeInitRule``.
/// given ``ConvenienceInitRule``.
///
/// - parameters:
/// - rule: The ``NodeInitRule`` to use for generating the initializer. Applying a rule will make some children non-optional, and set default values for other children.
/// - rule: The ``ConvenienceInitRule`` to use for generating the initializer. Applying a rule will make some children non-optional, and set default values for other children.
/// - useDeprecatedChildName: Whether to use the deprecated child name for the initializer parameter.
/// - returns:
/// - ``SyntaxNodeString``: The generated initializer.
func generateInitializerDeclHeader(for rule: NodeInitRule? = nil, useDeprecatedChildName: Bool = false) -> SyntaxNodeString {
func generateInitializerDeclHeader(for rule: ConvenienceInitRule? = nil, useDeprecatedChildName: Bool = false) -> SyntaxNodeString {
if children.isEmpty {
return "public init()"
}
Expand All @@ -66,13 +66,13 @@ extension LayoutNode {
}

/// Returns whether a given child should be optional in the initializer,
/// based on a provided ``NodeInitRule``.
/// based on a provided ``ConvenienceInitRule``.
///
/// If the rule is `nil`, this func will return `nil` as well, which means
/// that you should fall back to whether child is optional in the ``Node``
/// definition.
///
func ruleBasedChildIsOptional(for child: Child, with rule: NodeInitRule?) -> Bool {
func ruleBasedChildIsOptional(for child: Child, with rule: ConvenienceInitRule?) -> Bool {
if let rule = rule {
if rule.nonOptionalChildName == child.name {
return false
Expand All @@ -85,13 +85,13 @@ extension LayoutNode {
}

/// Returns a default value for a given child, based on a provided
/// ``NodeInitRule``.
/// ``ConvenienceInitRule``.
///
/// If the rule should not affect this child, the
/// `child.defualtInitialization` will be returned.
func ruleBasedChildDefaultValue(for child: Child, with rule: NodeInitRule?) -> InitializerClauseSyntax? {
func ruleBasedChildDefaultValue(for child: Child, with rule: ConvenienceInitRule?) -> InitializerClauseSyntax? {
if ruleBasedShouldOverrideDefault(for: child, with: rule) {
if let rule, let defaultValue = rule.childDefaultValues[child.name] {
if let rule, let defaultValue = rule.defaults[child.name] {
return InitializerClauseSyntax(
equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space),
value: ExprSyntax(".\(defaultValue.spec.varOrCaseName)Token()")
Expand All @@ -111,10 +111,10 @@ extension LayoutNode {
/// Returns `true` if there is a default value in the rule, or if the rule
/// requires this parameter to be non-optional.
/// If the rule is `nil`, it will return false.
func ruleBasedShouldOverrideDefault(for child: Child, with rule: NodeInitRule?) -> Bool {
func ruleBasedShouldOverrideDefault(for child: Child, with rule: ConvenienceInitRule?) -> Bool {
if let rule {
// If the rule provides a default for this child, override it and set the rule-based default.
if rule.childDefaultValues[child.name] != nil {
if rule.defaults[child.name] != nil {
return true
}

Expand Down Expand Up @@ -182,9 +182,9 @@ extension LayoutNode {
/// Returns a DccC comment for the parameters that get a default value,
/// with their corresponding default values, for a rule-based convenience initializer
/// for a node.
func generateRuleBasedInitParamsDocComment(for rule: NodeInitRule) -> SwiftSyntax.Trivia {
func generateRuleBasedInitParamsDocComment(for rule: ConvenienceInitRule) -> SwiftSyntax.Trivia {
var params = ""
for (childName, defaultValue) in rule.childDefaultValues {
for (childName, defaultValue) in rule.defaults {
params += " - `\(childName)`: `TokenSyntax.\(defaultValue.spec.varOrCaseName)Token()`\n"
}
return docCommentTrivia(from: params)
Expand Down
58 changes: 0 additions & 58 deletions Sources/SwiftSyntax/Convenience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,6 @@
//
//===----------------------------------------------------------------------===//

extension ClosureCaptureSyntax {

/// Creates a ``ClosureCaptureSyntax`` with a `name`, and automatically adds an `equal` token to it since the name is non-optional.
///
/// - SeeAlso: ``ClosureCaptureSyntax/init(leadingTrivia:_:specifier:_:name:_:equal:_:expression:_:trailingComma:_:trailingTrivia:)``.
///
public init(
leadingTrivia: Trivia? = nil,
specifier: ClosureCaptureSpecifierSyntax? = nil,
name: TokenSyntax,
equal: TokenSyntax = TokenSyntax.equalToken(),
expression: some ExprSyntaxProtocol,
trailingComma: TokenSyntax? = nil,
trailingTrivia: Trivia? = nil
) {
self.init(
leadingTrivia: leadingTrivia,
specifier: specifier,
name: name as TokenSyntax?,
equal: equal,
expression: expression,
trailingComma: trailingComma,
trailingTrivia: trailingTrivia
)
}
}

extension EnumCaseParameterSyntax {

/// Creates an ``EnumCaseParameterSyntax`` with a `firstName`, and automatically adds a `colon` to it.
///
/// - SeeAlso: For more information on the arguments, see ``EnumCaseParameterSyntax/init(leadingTrivia:_:modifiers:_:firstName:_:secondName:_:colon:_:type:_:defaultArgument:_:trailingComma:_:trailingTrivia:)``
///
public init(
leadingTrivia: Trivia? = nil,
modifiers: DeclModifierListSyntax = [],
firstName: TokenSyntax,
secondName: TokenSyntax? = nil,
colon: TokenSyntax = TokenSyntax.colonToken(),
type: some TypeSyntaxProtocol,
defaultValue: InitializerClauseSyntax? = nil,
trailingComma: TokenSyntax? = nil,
trailingTrivia: Trivia? = nil
) {
self.init(
leadingTrivia: leadingTrivia,
modifiers: modifiers,
firstName: firstName as TokenSyntax?,
secondName: secondName,
colon: colon,
type: type,
defaultValue: defaultValue,
trailingComma: trailingComma,
trailingTrivia: trailingTrivia
)
}
}

extension MemberAccessExprSyntax {
/// Creates a new ``MemberAccessExprSyntax`` where the accessed member is represented by
/// an identifier without specifying argument labels.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2578,8 +2578,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr
/// A convenience initializer for ``AttributeSyntax``
/// that takes a non-optional value for `arguments` parameter,
/// and adds the following default values:
/// - `rightParen`: `TokenSyntax.rightParenToken()`
/// - `leftParen`: `TokenSyntax.leftParenToken()`
/// - `rightParen`: `TokenSyntax.rightParenToken()`
///
public init(
leadingTrivia: Trivia? = nil,
Expand Down
26 changes: 26 additions & 0 deletions Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,32 @@ public struct ClosureCaptureSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxN
self._syntaxNode = Syntax(data)
}

/// A convenience initializer for ``ClosureCaptureSyntax``
/// that takes a non-optional value for `name` parameter,
/// and adds the following default values:
/// - `equal`: `TokenSyntax.equalToken()`
///
public init(
leadingTrivia: Trivia? = nil,
specifier: ClosureCaptureSpecifierSyntax? = nil,
name: TokenSyntax,
equal: TokenSyntax? = .equalToken(),
expression: some ExprSyntaxProtocol,
trailingComma: TokenSyntax? = nil,
trailingTrivia: Trivia? = nil

) {
self.init(
leadingTrivia: leadingTrivia,
specifier: specifier,
name: name as TokenSyntax?,
equal: equal,
expression: expression,
trailingComma: trailingComma,
trailingTrivia: trailingTrivia
)
}

/// - Parameters:
/// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
/// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
Expand Down
59 changes: 59 additions & 0 deletions Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,36 @@ public struct EnumCaseParameterSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynt
self._syntaxNode = Syntax(data)
}

/// A convenience initializer for ``EnumCaseParameterSyntax``
/// that takes a non-optional value for `firstName` parameter,
/// and adds the following default values:
/// - `colon`: `TokenSyntax.colonToken()`
///
public init(
leadingTrivia: Trivia? = nil,
modifiers: DeclModifierListSyntax = [],
firstName: TokenSyntax,
secondName: TokenSyntax? = nil,
colon: TokenSyntax? = .colonToken(),
type: some TypeSyntaxProtocol,
defaultValue: InitializerClauseSyntax? = nil,
trailingComma: TokenSyntax? = nil,
trailingTrivia: Trivia? = nil

) {
self.init(
leadingTrivia: leadingTrivia,
modifiers: modifiers,
firstName: firstName as TokenSyntax?,
secondName: secondName,
colon: colon,
type: type,
defaultValue: defaultValue,
trailingComma: trailingComma,
trailingTrivia: trailingTrivia
)
}

/// - Parameters:
/// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
/// - colon: If the parameter has a label, the colon separating the label from the type.
Expand Down Expand Up @@ -3128,6 +3158,35 @@ public struct FunctionCallExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafE
self._syntaxNode = Syntax(data)
}

/// A convenience initializer for ``FunctionCallExprSyntax``
/// that takes a non-optional value for `arguments` parameter,
/// and adds the following default values:
/// - `rightParen`: `TokenSyntax.rightParenToken()`
/// - `leftParen`: `TokenSyntax.leftParenToken()`
///
public init(
leadingTrivia: Trivia? = nil,
calledExpression: some ExprSyntaxProtocol,
leftParen: TokenSyntax? = .leftParenToken(),
arguments: LabeledExprListSyntax,
rightParen: TokenSyntax? = .rightParenToken(),
trailingClosure: ClosureExprSyntax? = nil,
additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [],
trailingTrivia: Trivia? = nil

) {
self.init(
leadingTrivia: leadingTrivia,
calledExpression: calledExpression,
leftParen: leftParen,
arguments: arguments as LabeledExprListSyntax?,
rightParen: rightParen,
trailingClosure: trailingClosure,
additionalTrailingClosures: additionalTrailingClosures,
trailingTrivia: trailingTrivia
)
}

/// - Parameters:
/// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
/// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
Expand Down

0 comments on commit 6eacdba

Please sign in to comment.