Skip to content

Remove the with<childName> functions on syntax nodes #1253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -290,69 +290,6 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: [.blockComment(gener
}
""")

FunctionDeclSyntax("""
/// Returns a new `\(raw: node.name)` with its leading trivia replaced
/// by the provided trivia.
public func withLeadingTrivia(_ leadingTrivia: Trivia) -> \(raw: node.name) {
return \(raw: node.name)(data.withLeadingTrivia(leadingTrivia, arena: SyntaxArena()))
}
""")

FunctionDeclSyntax("""
/// Returns a new `\(raw: node.name)` with its trailing trivia replaced
/// by the provided trivia.
public func withTrailingTrivia(_ trailingTrivia: Trivia) -> \(raw: node.name) {
return \(raw: node.name)(data.withTrailingTrivia(trailingTrivia, arena: SyntaxArena()))
}
""")

FunctionDeclSyntax("""
/// Returns a new `\(raw: node.name)` with its leading trivia removed.
public func withoutLeadingTrivia() -> \(raw: node.name) {
return withLeadingTrivia([])
}
""")


FunctionDeclSyntax("""
/// Returns a new `\(raw: node.name)` with its trailing trivia removed.
public func withoutTrailingTrivia() -> \(raw: node.name) {
return withTrailingTrivia([])
}
""")


FunctionDeclSyntax("""
/// Returns a new `\(raw: node.name)` with all trivia removed.
public func withoutTrivia() -> \(raw: node.name) {
return withoutLeadingTrivia().withoutTrailingTrivia()
}
""")

VariableDeclSyntax("""
/// The leading trivia (spaces, newlines, etc.) associated with this `\(raw: node.name)`.
public var leadingTrivia: Trivia? {
get {
return raw.formLeadingTrivia()
}
set {
self = withLeadingTrivia(newValue ?? [])
}
}
""")

VariableDeclSyntax("""
/// The trailing trivia (spaces, newlines, etc.) associated with this `\(raw: node.name)`.
public var trailingTrivia: Trivia? {
get {
return raw.formTrailingTrivia()
}
set {
self = withTrailingTrivia(newValue ?? [])
}
}
""")

FunctionDeclSyntax("""
public func childNameForDiagnostics(_ index: SyntaxChildrenIndex) -> String? {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,22 @@ let syntaxTraitsFile = SourceFileSyntax {
""") {

for child in trait.children {
VariableDeclSyntax("var \(raw: child.swiftName): \(raw: child.typeName)\(raw: child.isOptional ? "?" : "") { get }")
FunctionDeclSyntax("func with\(raw: child.name)(_ newChild: \(raw: child.typeName)\(raw: child.isOptional ? "?" : "")) -> Self")
VariableDeclSyntax("var \(raw: child.swiftName): \(raw: child.typeName)\(raw: child.isOptional ? "?" : "") { get set }")
}
}

ExtensionDeclSyntax("public extension \(trait.traitName)Syntax") {
FunctionDeclSyntax("""
/// Without this function, the `with` function defined on `SyntaxProtocol`
/// does not work on existentials of this protocol type.
@_disfavoredOverload
func with<T>(_ keyPath: WritableKeyPath<\(raw: trait.traitName)Syntax, T>, _ newChild: T) -> \(raw: trait.traitName)Syntax {
var copy: \(raw: trait.traitName)Syntax = self
copy[keyPath: keyPath] = newChild
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layoutKeyPath or something instead? keyPath: keyPath just looks weird 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it’s not just layouts right. keyPath can also point to trivia.

return copy
}
""")
}

ExtensionDeclSyntax("public extension SyntaxProtocol") {
FunctionDeclSyntax("""
Expand Down
10 changes: 5 additions & 5 deletions Sources/SwiftParserDiagnostics/DiagnosticExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ extension FixIt.Changes {
) -> Self {
var presentNode = PresentMaker().visit(Syntax(node))
if let leadingTrivia = leadingTrivia {
presentNode = presentNode.withLeadingTrivia(leadingTrivia)
presentNode = presentNode.with(\.leadingTrivia, leadingTrivia)
}
if let trailingTrivia = trailingTrivia {
presentNode = presentNode.withTrailingTrivia(trailingTrivia)
presentNode = presentNode.with(\.trailingTrivia, trailingTrivia)
}
if node.shouldBeInsertedAfterNextTokenTrivia,
let nextToken = node.nextToken(viewMode: .sourceAccurate),
Expand All @@ -89,7 +89,7 @@ extension FixIt.Changes {
return [
.replace(
oldNode: Syntax(node),
newNode: Syntax(presentNode).withLeadingTrivia(nextToken.leadingTrivia)
newNode: Syntax(presentNode).with(\.leadingTrivia, nextToken.leadingTrivia)
),
.replaceLeadingTrivia(token: nextToken, newTrivia: []),
]
Expand All @@ -105,7 +105,7 @@ extension FixIt.Changes {
return [
.replace(
oldNode: Syntax(node),
newNode: Syntax(presentNode).withLeadingTrivia(.space)
newNode: Syntax(presentNode).with(\.leadingTrivia, .space)
)
]
} else {
Expand All @@ -123,7 +123,7 @@ extension FixIt.Changes {
if let previousToken = token.previousToken(viewMode: .sourceAccurate) {
var presentToken = PresentMaker().visit(token)
if !previousToken.trailingTrivia.isEmpty {
presentToken = presentToken.withTrailingTrivia(previousToken.trailingTrivia)
presentToken = presentToken.with(\.trailingTrivia, previousToken.trailingTrivia)
}
return [
.replaceTrailingTrivia(token: previousToken, newTrivia: []),
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParserDiagnostics/MissingNodesError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ fileprivate enum NodesDescriptionPart {
tokens = tokens.map({ BasicFormat().visit($0) })
}
if !tokens.isEmpty {
tokens[0] = tokens[0].withLeadingTrivia([])
tokens[tokens.count - 1] = tokens[tokens.count - 1].withTrailingTrivia([])
tokens[0] = tokens[0].with(\.leadingTrivia, [])
tokens[tokens.count - 1] = tokens[tokens.count - 1].with(\.trailingTrivia, [])
}
let tokenContents = tokens.map(\.description).joined()
return "'\(tokenContents)'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {

let negatedAvailabilityKeyword = availability.availabilityKeyword.negatedAvailabilityKeyword
let negatedCoditionElement = ConditionElementSyntax(
condition: .availability(availability.withAvailabilityKeyword(negatedAvailabilityKeyword)),
condition: .availability(availability.with(\.availabilityKeyword, negatedAvailabilityKeyword)),
trailingComma: conditionElement.trailingComma
)
addDiagnostic(
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParserDiagnostics/SyntaxExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ extension SyntaxProtocol {
/// diagnostic message), return that.
/// Otherwise, return a generic message that describes the tokens in this node.
var shortSingleLineContentDescription: String {
let contentWithoutTrivia = self.withoutLeadingTrivia().withoutTrailingTrivia().description
let contentWithoutTrivia = self.trimmedDescription
if self.children(viewMode: .sourceAccurate).allSatisfy({ $0.as(TokenSyntax.self)?.tokenKind == .rightBrace }) {
if self.children(viewMode: .sourceAccurate).count == 1 {
return "brace"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public struct AddSeparatorsToIntegerLiteral: RefactoringProvider {
formattedText += value.byAddingGroupSeparators(at: lit.idealGroupSize)
return
lit
.withDigits(lit.digits.withKind(.integerLiteral(formattedText)))
.with(\.digits, lit.digits.withKind(.integerLiteral(formattedText)))
}
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/SwiftRefactor/FormatRawStringLiteral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ public struct FormatRawStringLiteral: RefactoringProvider {
guard maximumHashes > 0 else {
return
lit
.withOpenDelimiter(lit.openDelimiter?.withKind(.rawStringDelimiter("")))
.withCloseDelimiter(lit.closeDelimiter?.withKind(.rawStringDelimiter("")))
.with(\.openDelimiter, lit.openDelimiter?.withKind(.rawStringDelimiter("")))
.with(\.closeDelimiter, lit.closeDelimiter?.withKind(.rawStringDelimiter("")))
}

let delimiters = String(repeating: "#", count: maximumHashes + 1)
return
lit
.withOpenDelimiter(lit.openDelimiter?.withKind(.rawStringDelimiter(delimiters)))
.withCloseDelimiter(lit.closeDelimiter?.withKind(.rawStringDelimiter(delimiters)))
.with(\.openDelimiter, lit.openDelimiter?.withKind(.rawStringDelimiter(delimiters)))
.with(\.closeDelimiter, lit.closeDelimiter?.withKind(.rawStringDelimiter(delimiters)))
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ public struct MigrateToNewIfLetSyntax: RefactoringProvider {
binding.initializer = nil
// ... and remove whitespace before the comma (in `if` statements with multiple conditions).
if index != node.conditions.count - 1 {
binding.pattern = binding.pattern.withoutTrailingTrivia()
binding.pattern = binding.pattern.with(\.trailingTrivia, [])
}
conditionCopy.condition = .optionalBinding(binding)
}
return conditionCopy
}
return StmtSyntax(node.withConditions(ConditionElementListSyntax(newConditions)))
return StmtSyntax(node.with(\.conditions, ConditionElementListSyntax(newConditions)))
}
}
23 changes: 12 additions & 11 deletions Sources/SwiftRefactor/OpaqueParameterToGeneric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fileprivate class SomeParameterRewriter: SyntaxRewriter {
let colon: TokenSyntax?
if node.baseType.description != "Any" {
colon = .colonToken()
inheritedType = node.baseType.withLeadingTrivia(.space)
inheritedType = node.baseType.with(\.leadingTrivia, .space)
} else {
colon = nil
inheritedType = nil
Expand Down Expand Up @@ -143,8 +143,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
// Add a trailing comma to the prior generic parameter, if there is one.
if let lastNewGenericParam = newGenericParams.last {
newGenericParams[newGenericParams.count - 1] =
lastNewGenericParam.withTrailingComma(.commaToken())
newGenericParams.append(newGenericParam.withLeadingTrivia(.space))
lastNewGenericParam.with(\.trailingComma, .commaToken())
newGenericParams.append(newGenericParam.with(\.leadingTrivia, .space))
} else {
newGenericParams.append(newGenericParam)
}
Expand All @@ -153,7 +153,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
let newGenericParamSyntax = GenericParameterListSyntax(newGenericParams)
let newGenericParamClause: GenericParameterClauseSyntax
if let genericParams = genericParams {
newGenericParamClause = genericParams.withGenericParameterList(
newGenericParamClause = genericParams.with(
\.genericParameterList,
newGenericParamSyntax
)
} else {
Expand All @@ -166,7 +167,7 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {
}

return (
params.withParameterList(rewrittenParams),
params.with(\.parameterList, rewrittenParams),
newGenericParamClause
)
}
Expand All @@ -188,8 +189,8 @@ public struct OpaqueParameterToGeneric: RefactoringProvider {

return DeclSyntax(
funcSyntax
.withSignature(funcSyntax.signature.withInput(newInput))
.withGenericParameterClause(newGenericParams)
.with(\.signature, funcSyntax.signature.with(\.input, newInput))
.with(\.genericParameterClause, newGenericParams)
)
}

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

return DeclSyntax(
initSyntax
.withSignature(initSyntax.signature.withInput(newInput))
.withGenericParameterClause(newGenericParams)
.with(\.signature, initSyntax.signature.with(\.input, newInput))
.with(\.genericParameterClause, newGenericParams)
)
}

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

return DeclSyntax(
subscriptSyntax
.withIndices(newIndices)
.withGenericParameterClause(newGenericParams)
.with(\.indices, newIndices)
.with(\.genericParameterClause, newGenericParams)
)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftRefactor/RefactoringProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import SwiftSyntax
/// syntax trees. The SwiftSyntax API provides a natural, easy-to-use,
/// and compositional set of updates to the syntax tree. For example, a
/// refactoring action that wishes to exchange the leading trivia of a node
/// would call `withLeadingTrivia(_:)` against its input syntax and return
/// would call `with(\.leadingTrivia, _:)` against its input syntax and return
/// the resulting syntax node. For compound syntax nodes, entire sub-trees
/// can be added, exchanged, or removed by calling the corresponding `with`
/// API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ public struct RemoveSeparatorsFromIntegerLiteral: RefactoringProvider {
return lit
}
let formattedText = lit.digits.text.filter({ $0 != "_" })
return
lit
.withDigits(lit.digits.withKind(.integerLiteral(formattedText)))
return lit.with(\.digits, lit.digits.withKind(.integerLiteral(formattedText)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import Foundation
case .import(_, let statement):
return
statement
.withLeadingTrivia(offset == 0 ? [] : .newline)
.withTrailingTrivia([])
.with(\.leadingTrivia, offset == 0 ? [] : .newline)
.with(\.trailingTrivia, [])
case .other(let statement):
return statement
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@

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

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

Expand Down
Loading