Skip to content

Commit 4d6a1fa

Browse files
committed
Convert between computed properties and zero-parameters functions
1 parent 5f8e664 commit 4d6a1fa

9 files changed

+700
-20
lines changed

Sources/SwiftRefactor/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ add_swift_syntax_library(SwiftRefactor
1010
AddSeparatorsToIntegerLiteral.swift
1111
CallToTrailingClosures.swift
1212
ConvertComputedPropertyToStored.swift
13+
ConvertComputedPropertyToZeroParameterFunction.swift
1314
ConvertStoredPropertyToComputed.swift
15+
ConvertZeroParameterFunctionToComputedProperty.swift
1416
ExpandEditorPlaceholder.swift
1517
FormatRawStringLiteral.swift
1618
IntegerLiteralUtilities.swift
1719
MigrateToNewIfLetSyntax.swift
1820
OpaqueParameterToGeneric.swift
1921
RefactoringProvider.swift
2022
RemoveSeparatorsFromIntegerLiteral.swift
23+
SyntaxUtils.swift
2124
)
2225

2326
target_link_swift_syntax_libraries(SwiftRefactor PUBLIC

Sources/SwiftRefactor/CallToTrailingClosures.swift

-6
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,6 @@ extension FunctionCallExprSyntax {
152152
}
153153
}
154154

155-
fileprivate extension Trivia {
156-
var droppingLeadingWhitespace: Trivia {
157-
return Trivia(pieces: self.drop(while: \.isWhitespace))
158-
}
159-
}
160-
161155
fileprivate extension Sequence {
162156
func dropSuffix(while predicate: (Element) -> Bool) -> [Element] {
163157
self.reversed().drop(while: predicate).reversed()

Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift

-12
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,3 @@ public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider {
8282
return nil
8383
}
8484
}
85-
86-
fileprivate extension TokenSyntax {
87-
var trivia: Trivia {
88-
return leadingTrivia + trailingTrivia
89-
}
90-
}
91-
92-
fileprivate extension Trivia {
93-
var droppingTrailingWhitespace: Trivia {
94-
return Trivia(pieces: self.reversed().drop(while: \.isWhitespace).reversed())
95-
}
96-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
#if swift(>=6)
14+
public import SwiftSyntax
15+
#else
16+
import SwiftSyntax
17+
#endif
18+
19+
public struct ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringProvider {
20+
public static func refactor(syntax: VariableDeclSyntax, in context: Void) -> FunctionDeclSyntax? {
21+
guard syntax.bindings.count == 1,
22+
let binding = syntax.bindings.first,
23+
let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self)
24+
else { return nil }
25+
26+
var statements: CodeBlockItemListSyntax
27+
28+
guard let typeAnnotation = binding.typeAnnotation,
29+
var accessorBlock = binding.accessorBlock
30+
else { return nil }
31+
32+
var effectSpecifiers: AccessorEffectSpecifiersSyntax?
33+
34+
switch accessorBlock.accessors {
35+
case .accessors(let accessors):
36+
guard accessors.count == 1, let accessor = accessors.first,
37+
accessor.accessorSpecifier.tokenKind == .keyword(.get), let codeBlock = accessor.body
38+
else { return nil }
39+
effectSpecifiers = accessor.effectSpecifiers
40+
statements = codeBlock.statements
41+
let accessorSpecifier = accessor.accessorSpecifier
42+
statements.leadingTrivia =
43+
accessorSpecifier.leadingTrivia + accessorSpecifier.trailingTrivia.droppingLeadingWhitespace
44+
+ codeBlock.leftBrace.leadingTrivia.droppingLeadingWhitespace
45+
+ codeBlock.leftBrace.trailingTrivia.droppingLeadingWhitespace
46+
+ statements.leadingTrivia
47+
statements.trailingTrivia += codeBlock.rightBrace.trivia.droppingLeadingWhitespace
48+
statements.trailingTrivia = statements.trailingTrivia.droppingTrailingWhitespace
49+
case .getter(let codeBlock):
50+
statements = codeBlock
51+
}
52+
53+
let returnType = typeAnnotation.type
54+
55+
var returnClause: ReturnClauseSyntax?
56+
let triviaAfterSignature: Trivia
57+
58+
if !returnType.isVoid {
59+
triviaAfterSignature = .space
60+
returnClause = ReturnClauseSyntax(
61+
arrow: .arrowToken(
62+
leadingTrivia: typeAnnotation.colon.leadingTrivia,
63+
trailingTrivia: typeAnnotation.colon.trailingTrivia
64+
),
65+
type: returnType
66+
)
67+
} else {
68+
triviaAfterSignature = typeAnnotation.colon.leadingTrivia + typeAnnotation.colon.trailingTrivia
69+
}
70+
71+
accessorBlock.leftBrace.leadingTrivia = accessorBlock.leftBrace.leadingTrivia.droppingLeadingWhitespace
72+
accessorBlock.rightBrace.trailingTrivia = accessorBlock.rightBrace.trailingTrivia.droppingTrailingWhitespace
73+
74+
let body = CodeBlockSyntax(
75+
leftBrace: accessorBlock.leftBrace,
76+
statements: statements,
77+
rightBrace: accessorBlock.rightBrace
78+
)
79+
80+
var parameterClause = FunctionParameterClauseSyntax(parameters: [])
81+
parameterClause.trailingTrivia = identifierPattern.identifier.trailingTrivia + triviaAfterSignature
82+
83+
let functionEffectSpecifiers = FunctionEffectSpecifiersSyntax(
84+
asyncSpecifier: effectSpecifiers?.asyncSpecifier,
85+
throwsClause: effectSpecifiers?.throwsClause
86+
)
87+
let functionSignature = FunctionSignatureSyntax(
88+
parameterClause: parameterClause,
89+
effectSpecifiers: functionEffectSpecifiers,
90+
returnClause: returnClause
91+
)
92+
93+
return FunctionDeclSyntax(
94+
modifiers: syntax.modifiers,
95+
funcKeyword: .keyword(
96+
.func,
97+
leadingTrivia: syntax.bindingSpecifier.leadingTrivia,
98+
trailingTrivia: syntax.bindingSpecifier.trailingTrivia
99+
),
100+
name: identifierPattern.identifier.with(\.trailingTrivia, []),
101+
signature: functionSignature,
102+
body: body
103+
)
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
#if swift(>=6)
14+
public import SwiftSyntax
15+
#else
16+
import SwiftSyntax
17+
#endif
18+
19+
public struct ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringProvider {
20+
public static func refactor(syntax: FunctionDeclSyntax, in context: ()) -> VariableDeclSyntax? {
21+
guard syntax.signature.parameterClause.parameters.isEmpty,
22+
let body = syntax.body
23+
else { return nil }
24+
25+
let variableName = PatternSyntax(
26+
IdentifierPatternSyntax(
27+
leadingTrivia: syntax.funcKeyword.trailingTrivia,
28+
identifier: syntax.name
29+
)
30+
)
31+
32+
let triviaFromParameters =
33+
(syntax.signature.parameterClause.leftParen.trivia + syntax.signature.parameterClause.rightParen.trivia)
34+
.droppingTrailingWhitespace
35+
36+
var variableType: TypeAnnotationSyntax?
37+
38+
if let returnClause = syntax.signature.returnClause {
39+
variableType = TypeAnnotationSyntax(
40+
colon: .colonToken(
41+
leadingTrivia: triviaFromParameters + returnClause.arrow.leadingTrivia,
42+
trailingTrivia: returnClause.arrow.trailingTrivia
43+
),
44+
type: returnClause.type
45+
)
46+
} else {
47+
variableType = TypeAnnotationSyntax(
48+
colon: .colonToken(
49+
leadingTrivia: triviaFromParameters,
50+
trailingTrivia: .space
51+
),
52+
type: TypeSyntax("Void").with(\.trailingTrivia, .space)
53+
)
54+
}
55+
56+
let accessorBlock = AccessorBlockSyntax(
57+
leftBrace: body.leftBrace,
58+
accessors: .getter(body.statements),
59+
rightBrace: body.rightBrace
60+
)
61+
62+
return VariableDeclSyntax(
63+
modifiers: syntax.modifiers,
64+
.var,
65+
name: variableName,
66+
type: variableType,
67+
accessorBlock: accessorBlock
68+
)
69+
}
70+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
#if swift(>=6)
14+
public import SwiftSyntax
15+
#else
16+
import SwiftSyntax
17+
#endif
18+
19+
extension TokenSyntax {
20+
var trivia: Trivia {
21+
return leadingTrivia + trailingTrivia
22+
}
23+
}
24+
25+
extension Trivia {
26+
var droppingLeadingWhitespace: Trivia {
27+
return Trivia(pieces: self.drop(while: \.isWhitespace))
28+
}
29+
30+
var droppingTrailingWhitespace: Trivia {
31+
return Trivia(pieces: self.reversed().drop(while: \.isWhitespace).reversed())
32+
}
33+
}
34+
35+
extension TypeSyntax {
36+
var isVoid: Bool {
37+
switch self.as(TypeSyntaxEnum.self) {
38+
case .identifierType(let identifierType) where identifierType.name.text == "Void": return true
39+
case .tupleType(let tupleType) where tupleType.elements.isEmpty: return true
40+
default: return false
41+
}
42+
}
43+
}

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ extension VariableDeclSyntax {
403403
_ bindingSpecifier: Keyword,
404404
name: PatternSyntax,
405405
type: TypeAnnotationSyntax? = nil,
406-
initializer: InitializerClauseSyntax? = nil
406+
initializer: InitializerClauseSyntax? = nil,
407+
accessorBlock: AccessorBlockSyntax? = nil
407408
) {
408409
self.init(
409410
leadingTrivia: leadingTrivia,
@@ -414,7 +415,8 @@ extension VariableDeclSyntax {
414415
PatternBindingSyntax(
415416
pattern: name,
416417
typeAnnotation: type,
417-
initializer: initializer
418+
initializer: initializer,
419+
accessorBlock: accessorBlock
418420
)
419421
}
420422
}

0 commit comments

Comments
 (0)