Skip to content

Commit c197b4d

Browse files
committed
Add deprecation template deprecated children in SwiftSyntaxBuilder
1 parent 9aad641 commit c197b4d

File tree

7 files changed

+301
-111
lines changed

7 files changed

+301
-111
lines changed

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
124124
swiftSyntaxBuilderGeneratedDir + ["SyntaxExpressibleByStringInterpolationConformances.swift"],
125125
syntaxExpressibleByStringInterpolationConformancesFile
126126
),
127+
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["RenamedChildrenBuilderCompatibility.swift"], renamedChildrenBuilderCompatibilityFile),
127128
]
128129
+ BASE_KIND_FILES.map { baseKind in
129130
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["syntaxNodes", baseKind.value], syntaxNode(emitKind: baseKind.key))

CodeGeneration/Sources/generate-swiftsyntax/LayoutNode+Extensions.swift

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,103 @@ extension LayoutNode {
7373
)
7474
"""
7575
}
76+
77+
/// Create a builder-based convenience initializer, if needed.
78+
func createConvenienceBuilerInitializer(useDeprecatedChildName: Bool = false) throws -> InitializerDeclSyntax? {
79+
// Only create the convenience initializer if at least one parameter
80+
// is different than in the default initializer generated above.
81+
var shouldCreateInitializer = false
82+
83+
// Keep track of init parameters and result builder parameters in different
84+
// lists to make sure result builder params occur at the end, so that
85+
// they can use trailing closure syntax.
86+
var normalParameters: [FunctionParameterSyntax] = []
87+
var builderParameters: [FunctionParameterSyntax] = []
88+
var delegatedInitArgs: [TupleExprElementSyntax] = []
89+
90+
for child in children {
91+
/// The expression that is used to call the default initializer defined above.
92+
let produceExpr: ExprSyntax
93+
let childName: String
94+
95+
if useDeprecatedChildName, let deprecatedVarName = child.deprecatedVarName {
96+
childName = deprecatedVarName
97+
} else {
98+
childName = child.varName
99+
}
100+
101+
if child.type.isBuilderInitializable {
102+
// Allow initializing certain syntax collections with result builders
103+
shouldCreateInitializer = true
104+
let builderInitializableType = child.type.builderInitializableType
105+
if child.type.builderInitializableType != child.type {
106+
let param = Node.from(type: child.type).layoutNode!.singleNonDefaultedChild
107+
if child.isOptional {
108+
produceExpr = ExprSyntax("\(raw: childName)Builder().map { \(raw: child.type.syntaxBaseName)(\(raw: param.varName): $0) }")
109+
} else {
110+
produceExpr = ExprSyntax("\(raw: child.type.syntaxBaseName)(\(raw: param.varName): \(raw: childName)Builder())")
111+
}
112+
} else {
113+
produceExpr = ExprSyntax("\(raw: childName)Builder()")
114+
}
115+
builderParameters.append(
116+
FunctionParameterSyntax(
117+
"@\(builderInitializableType.resultBuilderType) \(raw: childName)Builder: () throws-> \(raw: builderInitializableType.syntax)"
118+
)
119+
)
120+
} else {
121+
produceExpr = convertFromSyntaxProtocolToSyntaxType(child: child, useDeprecatedChildName: useDeprecatedChildName)
122+
normalParameters.append(
123+
FunctionParameterSyntax(
124+
firstName: .identifier(childName),
125+
colon: .colonToken(),
126+
type: child.parameterType,
127+
defaultArgument: child.defaultInitialization
128+
)
129+
)
130+
}
131+
delegatedInitArgs.append(TupleExprElementSyntax(label: child.isUnexpectedNodes ? nil : child.varName, expression: produceExpr))
132+
}
133+
134+
guard shouldCreateInitializer else {
135+
return nil
136+
}
137+
138+
let params = ParameterClauseSyntax {
139+
FunctionParameterSyntax("leadingTrivia: Trivia? = nil")
140+
for param in normalParameters + builderParameters {
141+
param
142+
}
143+
FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
144+
}
145+
146+
return try InitializerDeclSyntax(
147+
"""
148+
/// A convenience initializer that allows initializing syntax collections using result builders
149+
public init\(params) rethrows
150+
"""
151+
) {
152+
FunctionCallExprSyntax(callee: ExprSyntax("try self.init")) {
153+
TupleExprElementSyntax(label: "leadingTrivia", expression: ExprSyntax("leadingTrivia"))
154+
for arg in delegatedInitArgs {
155+
arg
156+
}
157+
TupleExprElementSyntax(label: "trailingTrivia", expression: ExprSyntax("trailingTrivia"))
158+
}
159+
}
160+
}
161+
}
162+
163+
fileprivate func convertFromSyntaxProtocolToSyntaxType(child: Child, useDeprecatedChildName: Bool = false) -> ExprSyntax {
164+
let childName: String
165+
if useDeprecatedChildName, let deprecatedVarName = child.deprecatedVarName {
166+
childName = deprecatedVarName
167+
} else {
168+
childName = child.varName
169+
}
170+
171+
if child.type.isBaseType && !child.kind.isNodeChoices {
172+
return ExprSyntax("\(raw: child.type.syntaxBaseName)(fromProtocol: \(raw: childName))")
173+
}
174+
return ExprSyntax("\(raw: childName)")
76175
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/BuildableNodesFile.swift

Lines changed: 1 addition & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let buildableNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2121
for node in SYNTAX_NODES.compactMap(\.layoutNode) {
2222
let type = node.type
2323

24-
if let convenienceInit = try! createConvenienceInitializer(node: node) {
24+
if let convenienceInit = try! node.createConvenienceBuilerInitializer() {
2525
DeclSyntax(
2626
"""
2727
extension \(raw: type.syntaxBaseName) {
@@ -32,87 +32,3 @@ let buildableNodesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3232
}
3333
}
3434
}
35-
36-
private func convertFromSyntaxProtocolToSyntaxType(child: Child) -> ExprSyntax {
37-
if child.type.isBaseType && !child.kind.isNodeChoices {
38-
return ExprSyntax("\(raw: child.type.syntaxBaseName)(fromProtocol: \(raw: child.varName))")
39-
}
40-
return ExprSyntax("\(raw: child.varName)")
41-
}
42-
43-
/// Create a builder-based convenience initializer, if needed.
44-
private func createConvenienceInitializer(node: LayoutNode) throws -> InitializerDeclSyntax? {
45-
// Only create the convenience initializer if at least one parameter
46-
// is different than in the default initializer generated above.
47-
var shouldCreateInitializer = false
48-
49-
// Keep track of init parameters and result builder parameters in different
50-
// lists to make sure result builder params occur at the end, so that
51-
// they can use trailing closure syntax.
52-
var normalParameters: [FunctionParameterSyntax] = []
53-
var builderParameters: [FunctionParameterSyntax] = []
54-
var delegatedInitArgs: [TupleExprElementSyntax] = []
55-
56-
for child in node.children {
57-
/// The expression that is used to call the default initializer defined above.
58-
let produceExpr: ExprSyntax
59-
if child.type.isBuilderInitializable {
60-
// Allow initializing certain syntax collections with result builders
61-
shouldCreateInitializer = true
62-
let builderInitializableType = child.type.builderInitializableType
63-
if child.type.builderInitializableType != child.type {
64-
let param = Node.from(type: child.type).layoutNode!.singleNonDefaultedChild
65-
if child.isOptional {
66-
produceExpr = ExprSyntax("\(raw: child.varName)Builder().map { \(raw: child.type.syntaxBaseName)(\(raw: param.varName): $0) }")
67-
} else {
68-
produceExpr = ExprSyntax("\(raw: child.type.syntaxBaseName)(\(raw: param.varName): \(raw: child.varName)Builder())")
69-
}
70-
} else {
71-
produceExpr = ExprSyntax("\(raw: child.varName)Builder()")
72-
}
73-
builderParameters.append(
74-
FunctionParameterSyntax(
75-
"@\(builderInitializableType.resultBuilderType) \(raw: child.varName)Builder: () throws-> \(raw: builderInitializableType.syntax)"
76-
)
77-
)
78-
} else {
79-
produceExpr = convertFromSyntaxProtocolToSyntaxType(child: child)
80-
normalParameters.append(
81-
FunctionParameterSyntax(
82-
firstName: .identifier(child.varName),
83-
colon: .colonToken(),
84-
type: child.parameterType,
85-
defaultArgument: child.defaultInitialization
86-
)
87-
)
88-
}
89-
delegatedInitArgs.append(TupleExprElementSyntax(label: child.isUnexpectedNodes ? nil : child.varName, expression: produceExpr))
90-
}
91-
92-
guard shouldCreateInitializer else {
93-
return nil
94-
}
95-
96-
let params = ParameterClauseSyntax {
97-
FunctionParameterSyntax("leadingTrivia: Trivia? = nil")
98-
for param in normalParameters + builderParameters {
99-
param
100-
}
101-
FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
102-
}
103-
104-
return try InitializerDeclSyntax(
105-
"""
106-
/// A convenience initializer that allows initializing syntax collections using result builders
107-
public init\(params) rethrows
108-
"""
109-
) {
110-
FunctionCallExprSyntax(callee: ExprSyntax("try self.init")) {
111-
TupleExprElementSyntax(label: "leadingTrivia", expression: ExprSyntax("leadingTrivia"))
112-
for arg in delegatedInitArgs {
113-
arg
114-
}
115-
TupleExprElementSyntax(label: "trailingTrivia", expression: ExprSyntax("trailingTrivia"))
116-
}
117-
}
118-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
import Utils
17+
18+
let renamedChildrenBuilderCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) {
19+
DeclSyntax("import SwiftSyntax")
20+
21+
for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ $0.children.hasDeprecatedChild }) {
22+
if let convenienceInit = try layoutNode.createConvenienceBuilerInitializer(useDeprecatedChildName: true) {
23+
let deprecatedNames = layoutNode.children
24+
.filter { !$0.isUnexpectedNodes }
25+
.compactMap { $0.deprecatedName?.withFirstCharacterLowercased }
26+
.joined(separator: ", ")
27+
28+
DeclSyntax(
29+
"""
30+
extension \(raw: layoutNode.type.syntaxBaseName) {
31+
@available(*, deprecated, message: "Use an initializer with \(raw: deprecatedNames) argument(s).")
32+
@_disfavoredOverload
33+
\(convenienceInit)
34+
}
35+
"""
36+
)
37+
}
38+
}
39+
}

Sources/SwiftSyntaxBuilder/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_swift_host_library(SwiftSyntaxBuilder
1010
ConvenienceInitializers.swift
1111
Indenter.swift
1212
ResultBuilderExtensions.swift
13+
SwiftSyntaxBuilderCompatibility.swift
1314
Syntax+StringInterpolation.swift
1415
SyntaxNodeWithBody.swift
1516
ValidatingSyntaxNodes.swift
@@ -19,6 +20,7 @@ add_swift_host_library(SwiftSyntaxBuilder
1920
generated/BuildableCollectionNodes.swift
2021
generated/BuildableNodes.swift
2122
generated/ResultBuilders.swift
23+
generated/RenamedChildrenBuilderCompatibility.swift
2224
generated/SyntaxExpressibleByStringInterpolationConformances.swift
2325
)
2426

Sources/SwiftSyntaxBuilder/SwiftSyntaxBuilderCompatibility.swift

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,7 @@ import SwiftSyntax
1818
@available(*, deprecated, renamed: "ImportPathBuilder")
1919
public typealias AccessPathBuilder = ImportPathBuilder
2020

21-
extension TupleExprSyntax {
22-
@available(*, deprecated, message: "Use an initializer with a elements argument")
23-
public init(
24-
leadingTrivia: Trivia? = nil,
25-
unexpectedBeforeLeftParen: UnexpectedNodesSyntax? = nil,
26-
leftParen: TokenSyntax = .leftParenToken(),
27-
unexpectedBetweenLeftParenAndElementList: UnexpectedNodesSyntax? = nil,
28-
unexpectedBetweenElementListAndRightParen: UnexpectedNodesSyntax? = nil,
29-
rightParen: TokenSyntax = .rightParenToken(),
30-
unexpectedAfterRightParen: UnexpectedNodesSyntax? = nil,
31-
@TupleExprElementListBuilder elementListBuilder: () throws -> TupleExprElementListSyntax,
32-
trailingTrivia: Trivia? = nil
33-
) rethrows {
34-
try self.init(
35-
leadingTrivia: leadingTrivia,
36-
unexpectedBeforeLeftParen,
37-
leftParen: leftParen,
38-
unexpectedBetweenLeftParenAndElementList,
39-
elements: elementListBuilder(),
40-
unexpectedBetweenElementListAndRightParen,
41-
rightParen: rightParen,
42-
unexpectedAfterRightParen,
43-
trailingTrivia: trailingTrivia
44-
)
45-
}
46-
}
21+
//==========================================================================//
22+
// IMPORTANT: If you are tempted to add a compatiblity layer code here //
23+
// please insert it in alphabetical order above //
24+
//==========================================================================//

0 commit comments

Comments
 (0)