Skip to content

Commit 1940941

Browse files
committed
Remove the with<childName> functions on syntax nodes
Instead, provide a single `with` function that takes a key path of the child you want to replace. This significatnly reduces the amout of code that is being generated for SwiftSyntax. While at it, also remove the `without(Leading|Trailing)?Trivia` functions
1 parent 1159abd commit 1940941

40 files changed

+6810
-20309
lines changed

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

-63
Original file line numberDiff line numberDiff line change
@@ -290,69 +290,6 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: [.blockComment(gener
290290
}
291291
""")
292292

293-
FunctionDeclSyntax("""
294-
/// Returns a new `\(raw: node.name)` with its leading trivia replaced
295-
/// by the provided trivia.
296-
public func withLeadingTrivia(_ leadingTrivia: Trivia) -> \(raw: node.name) {
297-
return \(raw: node.name)(data.withLeadingTrivia(leadingTrivia, arena: SyntaxArena()))
298-
}
299-
""")
300-
301-
FunctionDeclSyntax("""
302-
/// Returns a new `\(raw: node.name)` with its trailing trivia replaced
303-
/// by the provided trivia.
304-
public func withTrailingTrivia(_ trailingTrivia: Trivia) -> \(raw: node.name) {
305-
return \(raw: node.name)(data.withTrailingTrivia(trailingTrivia, arena: SyntaxArena()))
306-
}
307-
""")
308-
309-
FunctionDeclSyntax("""
310-
/// Returns a new `\(raw: node.name)` with its leading trivia removed.
311-
public func withoutLeadingTrivia() -> \(raw: node.name) {
312-
return withLeadingTrivia([])
313-
}
314-
""")
315-
316-
317-
FunctionDeclSyntax("""
318-
/// Returns a new `\(raw: node.name)` with its trailing trivia removed.
319-
public func withoutTrailingTrivia() -> \(raw: node.name) {
320-
return withTrailingTrivia([])
321-
}
322-
""")
323-
324-
325-
FunctionDeclSyntax("""
326-
/// Returns a new `\(raw: node.name)` with all trivia removed.
327-
public func withoutTrivia() -> \(raw: node.name) {
328-
return withoutLeadingTrivia().withoutTrailingTrivia()
329-
}
330-
""")
331-
332-
VariableDeclSyntax("""
333-
/// The leading trivia (spaces, newlines, etc.) associated with this `\(raw: node.name)`.
334-
public var leadingTrivia: Trivia? {
335-
get {
336-
return raw.formLeadingTrivia()
337-
}
338-
set {
339-
self = withLeadingTrivia(newValue ?? [])
340-
}
341-
}
342-
""")
343-
344-
VariableDeclSyntax("""
345-
/// The trailing trivia (spaces, newlines, etc.) associated with this `\(raw: node.name)`.
346-
public var trailingTrivia: Trivia? {
347-
get {
348-
return raw.formTrailingTrivia()
349-
}
350-
set {
351-
self = withTrailingTrivia(newValue ?? [])
352-
}
353-
}
354-
""")
355-
356293
FunctionDeclSyntax("""
357294
public func childNameForDiagnostics(_ index: SyntaxChildrenIndex) -> String? {
358295
return nil

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxTraitsFile.swift

+14-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,22 @@ let syntaxTraitsFile = SourceFileSyntax {
2424
""") {
2525

2626
for child in trait.children {
27-
VariableDeclSyntax("var \(raw: child.swiftName): \(raw: child.typeName)\(raw: child.isOptional ? "?" : "") { get }")
28-
FunctionDeclSyntax("func with\(raw: child.name)(_ newChild: \(raw: child.typeName)\(raw: child.isOptional ? "?" : "")) -> Self")
27+
VariableDeclSyntax("var \(raw: child.swiftName): \(raw: child.typeName)\(raw: child.isOptional ? "?" : "") { get set }")
2928
}
3029
}
30+
31+
ExtensionDeclSyntax("public extension \(trait.traitName)Syntax") {
32+
FunctionDeclSyntax("""
33+
/// Without this function, the `with` function defined on `SyntaxProtocol`
34+
/// does not work on existentials of this protocol type.
35+
@_disfavoredOverload
36+
func with<T>(_ keyPath: WritableKeyPath<\(raw: trait.traitName)Syntax, T>, _ newChild: T) -> \(raw: trait.traitName)Syntax {
37+
var copy: \(raw: trait.traitName)Syntax = self
38+
copy[keyPath: keyPath] = newChild
39+
return copy
40+
}
41+
""")
42+
}
3143

3244
ExtensionDeclSyntax("public extension SyntaxProtocol") {
3345
FunctionDeclSyntax("""

Sources/SwiftParserDiagnostics/DiagnosticExtensions.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ extension FixIt.Changes {
7777
) -> Self {
7878
var presentNode = PresentMaker().visit(Syntax(node))
7979
if let leadingTrivia = leadingTrivia {
80-
presentNode = presentNode.withLeadingTrivia(leadingTrivia)
80+
presentNode = presentNode.with(\.leadingTrivia, leadingTrivia)
8181
}
8282
if let trailingTrivia = trailingTrivia {
83-
presentNode = presentNode.withTrailingTrivia(trailingTrivia)
83+
presentNode = presentNode.with(\.trailingTrivia, trailingTrivia)
8484
}
8585
if node.shouldBeInsertedAfterNextTokenTrivia,
8686
let nextToken = node.nextToken(viewMode: .sourceAccurate),
@@ -89,7 +89,7 @@ extension FixIt.Changes {
8989
return [
9090
.replace(
9191
oldNode: Syntax(node),
92-
newNode: Syntax(presentNode).withLeadingTrivia(nextToken.leadingTrivia)
92+
newNode: Syntax(presentNode).with(\.leadingTrivia, nextToken.leadingTrivia)
9393
),
9494
.replaceLeadingTrivia(token: nextToken, newTrivia: []),
9595
]
@@ -105,7 +105,7 @@ extension FixIt.Changes {
105105
return [
106106
.replace(
107107
oldNode: Syntax(node),
108-
newNode: Syntax(presentNode).withLeadingTrivia(.space)
108+
newNode: Syntax(presentNode).with(\.leadingTrivia, .space)
109109
)
110110
]
111111
} else {
@@ -123,7 +123,7 @@ extension FixIt.Changes {
123123
if let previousToken = token.previousToken(viewMode: .sourceAccurate) {
124124
var presentToken = PresentMaker().visit(token)
125125
if !previousToken.trailingTrivia.isEmpty {
126-
presentToken = presentToken.withTrailingTrivia(previousToken.trailingTrivia)
126+
presentToken = presentToken.with(\.trailingTrivia, previousToken.trailingTrivia)
127127
}
128128
return [
129129
.replaceTrailingTrivia(token: previousToken, newTrivia: []),

Sources/SwiftParserDiagnostics/MissingNodesError.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ fileprivate enum NodesDescriptionPart {
3333
tokens = tokens.map({ BasicFormat().visit($0) })
3434
}
3535
if !tokens.isEmpty {
36-
tokens[0] = tokens[0].withLeadingTrivia([])
37-
tokens[tokens.count - 1] = tokens[tokens.count - 1].withTrailingTrivia([])
36+
tokens[0] = tokens[0].with(\.leadingTrivia, [])
37+
tokens[tokens.count - 1] = tokens[tokens.count - 1].with(\.trailingTrivia, [])
3838
}
3939
let tokenContents = tokens.map(\.description).joined()
4040
return "'\(tokenContents)'"

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
615615

616616
let negatedAvailabilityKeyword = availability.availabilityKeyword.negatedAvailabilityKeyword
617617
let negatedCoditionElement = ConditionElementSyntax(
618-
condition: .availability(availability.withAvailabilityKeyword(negatedAvailabilityKeyword)),
618+
condition: .availability(availability.with(\.availabilityKeyword, negatedAvailabilityKeyword)),
619619
trailingComma: conditionElement.trailingComma
620620
)
621621
addDiagnostic(

Sources/SwiftParserDiagnostics/SyntaxExtensions.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ extension SyntaxProtocol {
7171
/// diagnostic message), return that.
7272
/// Otherwise, return a generic message that describes the tokens in this node.
7373
var shortSingleLineContentDescription: String {
74-
let contentWithoutTrivia = self.withoutLeadingTrivia().withoutTrailingTrivia().description
74+
let contentWithoutTrivia = self.trimmedDescription
7575
if self.children(viewMode: .sourceAccurate).allSatisfy({ $0.as(TokenSyntax.self)?.tokenKind == .rightBrace }) {
7676
if self.children(viewMode: .sourceAccurate).count == 1 {
7777
return "brace"

Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public struct AddSeparatorsToIntegerLiteral: RefactoringProvider {
5151
formattedText += value.byAddingGroupSeparators(at: lit.idealGroupSize)
5252
return
5353
lit
54-
.withDigits(lit.digits.withKind(.integerLiteral(formattedText)))
54+
.with(\.digits, lit.digits.withKind(.integerLiteral(formattedText)))
5555
}
5656
}
5757

Sources/SwiftRefactor/FormatRawStringLiteral.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ public struct FormatRawStringLiteral: RefactoringProvider {
4949
guard maximumHashes > 0 else {
5050
return
5151
lit
52-
.withOpenDelimiter(lit.openDelimiter?.withKind(.rawStringDelimiter("")))
53-
.withCloseDelimiter(lit.closeDelimiter?.withKind(.rawStringDelimiter("")))
52+
.with(\.openDelimiter, lit.openDelimiter?.withKind(.rawStringDelimiter("")))
53+
.with(\.closeDelimiter, lit.closeDelimiter?.withKind(.rawStringDelimiter("")))
5454
}
5555

5656
let delimiters = String(repeating: "#", count: maximumHashes + 1)
5757
return
5858
lit
59-
.withOpenDelimiter(lit.openDelimiter?.withKind(.rawStringDelimiter(delimiters)))
60-
.withCloseDelimiter(lit.closeDelimiter?.withKind(.rawStringDelimiter(delimiters)))
59+
.with(\.openDelimiter, lit.openDelimiter?.withKind(.rawStringDelimiter(delimiters)))
60+
.with(\.closeDelimiter, lit.closeDelimiter?.withKind(.rawStringDelimiter(delimiters)))
6161
}
6262
}
6363

Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public struct MigrateToNewIfLetSyntax: RefactoringProvider {
5151
binding.initializer = nil
5252
// ... and remove whitespace before the comma (in `if` statements with multiple conditions).
5353
if index != node.conditions.count - 1 {
54-
binding.pattern = binding.pattern.withoutTrailingTrivia()
54+
binding.pattern = binding.pattern.with(\.trailingTrivia, [])
5555
}
5656
conditionCopy.condition = .optionalBinding(binding)
5757
}
5858
return conditionCopy
5959
}
60-
return StmtSyntax(node.withConditions(ConditionElementListSyntax(newConditions)))
60+
return StmtSyntax(node.with(\.conditions, ConditionElementListSyntax(newConditions)))
6161
}
6262
}

Sources/SwiftRefactor/OpaqueParameterToGeneric.swift

+12-11
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fileprivate class SomeParameterRewriter: SyntaxRewriter {
4848
let colon: TokenSyntax?
4949
if node.baseType.description != "Any" {
5050
colon = .colonToken()
51-
inheritedType = node.baseType.withLeadingTrivia(.space)
51+
inheritedType = node.baseType.with(\.leadingTrivia, .space)
5252
} else {
5353
colon = nil
5454
inheritedType = nil
@@ -143,8 +143,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
143143
// Add a trailing comma to the prior generic parameter, if there is one.
144144
if let lastNewGenericParam = newGenericParams.last {
145145
newGenericParams[newGenericParams.count - 1] =
146-
lastNewGenericParam.withTrailingComma(.commaToken())
147-
newGenericParams.append(newGenericParam.withLeadingTrivia(.space))
146+
lastNewGenericParam.with(\.trailingComma, .commaToken())
147+
newGenericParams.append(newGenericParam.with(\.leadingTrivia, .space))
148148
} else {
149149
newGenericParams.append(newGenericParam)
150150
}
@@ -153,7 +153,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
153153
let newGenericParamSyntax = GenericParameterListSyntax(newGenericParams)
154154
let newGenericParamClause: GenericParameterClauseSyntax
155155
if let genericParams = genericParams {
156-
newGenericParamClause = genericParams.withGenericParameterList(
156+
newGenericParamClause = genericParams.with(
157+
\.genericParameterList,
157158
newGenericParamSyntax
158159
)
159160
} else {
@@ -166,7 +167,7 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
166167
}
167168

168169
return (
169-
params.withParameterList(rewrittenParams),
170+
params.with(\.parameterList, rewrittenParams),
170171
newGenericParamClause
171172
)
172173
}
@@ -188,8 +189,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
188189

189190
return DeclSyntax(
190191
funcSyntax
191-
.withSignature(funcSyntax.signature.withInput(newInput))
192-
.withGenericParameterClause(newGenericParams)
192+
.with(\.signature, funcSyntax.signature.with(\.input, newInput))
193+
.with(\.genericParameterClause, newGenericParams)
193194
)
194195
}
195196

@@ -206,8 +207,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
206207

207208
return DeclSyntax(
208209
initSyntax
209-
.withSignature(initSyntax.signature.withInput(newInput))
210-
.withGenericParameterClause(newGenericParams)
210+
.with(\.signature, initSyntax.signature.with(\.input, newInput))
211+
.with(\.genericParameterClause, newGenericParams)
211212
)
212213
}
213214

@@ -224,8 +225,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
224225

225226
return DeclSyntax(
226227
subscriptSyntax
227-
.withIndices(newIndices)
228-
.withGenericParameterClause(newGenericParams)
228+
.with(\.indices, newIndices)
229+
.with(\.genericParameterClause, newGenericParams)
229230
)
230231
}
231232

Sources/SwiftRefactor/RefactoringProvider.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import SwiftSyntax
3232
/// syntax trees. The SwiftSyntax API provides a natural, easy-to-use,
3333
/// and compositional set of updates to the syntax tree. For example, a
3434
/// refactoring action that wishes to exchange the leading trivia of a node
35-
/// would call `withLeadingTrivia(_:)` against its input syntax and return
35+
/// would call `with(\.leadingTrivia, _:)` against its input syntax and return
3636
/// the resulting syntax node. For compound syntax nodes, entire sub-trees
3737
/// can be added, exchanged, or removed by calling the corresponding `with`
3838
/// API.

Sources/SwiftRefactor/RemoveSeparatorsFromIntegerLiteral.swift

+1-3
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ public struct RemoveSeparatorsFromIntegerLiteral: RefactoringProvider {
3232
return lit
3333
}
3434
let formattedText = lit.digits.text.filter({ $0 != "_" })
35-
return
36-
lit
37-
.withDigits(lit.digits.withKind(.integerLiteral(formattedText)))
35+
return lit.with(\.digits, lit.digits.withKind(.integerLiteral(formattedText)))
3836
}
3937
}

Sources/SwiftSyntax/Documentation.docc/Resources/Formatter.step11.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ import Foundation
5050
case .import(_, let statement):
5151
return
5252
statement
53-
.withLeadingTrivia(offset == 0 ? [] : .newline)
54-
.withTrailingTrivia([])
53+
.with(\.leadingTrivia, offset == 0 ? [] : .newline)
54+
.with(\.trailingTrivia, [])
5555
case .other(let statement):
5656
return statement
5757
}

Sources/SwiftSyntax/Documentation.docc/Tutorials/SwiftSyntax By Example.tutorial

+4-4
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,14 @@
212212

213213
@Step {
214214
Normalize the whitespace of all the imports by calling the trivia
215-
manipulation functions `withLeadingTrivia(_:)` and
216-
`withTrailingTrivia(_:)` on import items.
215+
manipulation functions `with(\.leadingTrivia, _:)` and
216+
`with(\.trailingTrivia, _:)` on import items.
217217

218218
SwiftSyntax does not automatically format whitespace and trivia when
219219
items are moved around. SwiftSyntax provides a convenient way of
220220
manipulating whitespace by calling the
221-
`withLeadingTrivia(_:)` and
222-
`withTrailingTrivia(_:)` methods. By normalizing all
221+
`with(\.leadingTrivia, _:)` and
222+
`with(\.trailingTrivia, _:)` methods. By normalizing all
223223
of the whitespace to a single leading newline we can fix bug #1. And
224224
by removing all trailing whitespace we can fix bug #2.
225225

0 commit comments

Comments
 (0)