Skip to content

Commit 818349c

Browse files
committed
[SwiftSyntaxCodeActions] Move SwiftRefactor code actions in from swift-syntax
1 parent b0df3fe commit 818349c

25 files changed

Lines changed: 3297 additions & 3 deletions
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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 SwiftRefactor
14+
package import SwiftSyntax
15+
16+
/// Format an integer literal by inserting underscores at base-appropriate
17+
/// locations.
18+
///
19+
/// This pass will also clean up any errant underscores.
20+
///
21+
/// ## Before
22+
///
23+
/// ```swift
24+
/// 123456789
25+
/// 0xFFFFFFFFF
26+
/// 0b1_0_1_0
27+
/// ```
28+
///
29+
/// ## After
30+
///
31+
/// ```swift
32+
/// 123_456_789
33+
/// 0xF_FFFF_FFFF
34+
/// 0b1_010
35+
/// ```
36+
package struct AddSeparatorsToIntegerLiteral: SyntaxRefactoringProvider {
37+
package static func refactor(
38+
syntax lit: IntegerLiteralExprSyntax,
39+
in context: Void
40+
) throws -> IntegerLiteralExprSyntax {
41+
if lit.literal.text.contains("_") {
42+
let strippedLiteral = try RemoveSeparatorsFromIntegerLiteral.refactor(syntax: lit)
43+
return self.addSeparators(to: strippedLiteral)
44+
} else {
45+
return self.addSeparators(to: lit)
46+
}
47+
}
48+
49+
private static func addSeparators(to lit: IntegerLiteralExprSyntax) -> IntegerLiteralExprSyntax {
50+
var formattedText = ""
51+
let (prefix, value) = lit.split()
52+
formattedText += prefix
53+
formattedText += value.byAddingGroupSeparators(at: lit.idealGroupSize)
54+
return
55+
lit
56+
.with(\.literal, lit.literal.with(\.tokenKind, .integerLiteral(formattedText)))
57+
}
58+
}
59+
60+
extension Substring {
61+
fileprivate func byAddingGroupSeparators(at interval: Int) -> String {
62+
var result = ""
63+
result.reserveCapacity(self.count)
64+
for (i, char) in self.filter({ $0 != "_" }).reversed().enumerated() {
65+
if i > 0 && i % interval == 0 {
66+
result.append("_")
67+
}
68+
result.append(char)
69+
}
70+
return String(result.reversed())
71+
}
72+
}

Sources/SwiftSyntaxCodeActions/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
add_library(SwiftSyntaxCodeActions STATIC
22
AddDocumentation.swift
3+
AddSeparatorsToIntegerLiteral.swift
34
ApplyDeMorganLaw.swift
45
ConvertCommentToDocComment.swift
6+
ConvertComputedPropertyToStored.swift
7+
ConvertComputedPropertyToZeroParameterFunction.swift
58
ConvertIfLetToGuard.swift
69
ConvertIntegerLiteral.swift
710
ConvertJSONToCodableStruct.swift
11+
ConvertStoredPropertyToComputed.swift
812
ConvertStringConcatenationToStringInterpolation.swift
13+
ConvertZeroParameterFunctionToComputedProperty.swift
14+
DeclModifierRemover.swift
15+
FormatRawStringLiteral.swift
916
IndentationRemover.swift
17+
IntegerLiteralUtilities.swift
18+
MigrateToNewIfLetSyntax.swift
19+
OpaqueParameterToGeneric.swift
1020
PackageManifestEdits.swift
21+
RemoveRedundantParentheses.swift
22+
RemoveSeparatorsFromIntegerLiteral.swift
1123
SyntaxCodeActionProvider.swift
1224
SyntaxCodeActions.swift
1325
SyntaxRefactoringCodeActionProvider.swift
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
import SwiftRefactor
14+
package import SwiftSyntax
15+
import SwiftSyntaxBuilder
16+
17+
package struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider {
18+
package static func refactor(syntax: VariableDeclSyntax, in context: ()) throws -> VariableDeclSyntax {
19+
guard syntax.bindings.count == 1, let binding = syntax.bindings.first else {
20+
throw RefactoringNotApplicableError("unsupported variable declaration")
21+
}
22+
23+
guard let accessorBlock = binding.accessorBlock,
24+
case let .getter(body) = accessorBlock.accessors, !body.isEmpty
25+
else {
26+
throw RefactoringNotApplicableError("getter is missing or empty")
27+
}
28+
29+
let refactored = { (initializer: InitializerClauseSyntax) -> VariableDeclSyntax in
30+
let newBinding =
31+
binding
32+
.with(\.initializer, initializer)
33+
.with(\.accessorBlock, nil)
34+
35+
let bindingSpecifier = syntax.bindingSpecifier
36+
.with(\.tokenKind, .keyword(.let))
37+
38+
return
39+
syntax
40+
.with(\.bindingSpecifier, bindingSpecifier)
41+
.with(\.bindings, PatternBindingListSyntax([newBinding]))
42+
}
43+
44+
guard body.count == 1 else {
45+
let closure = ClosureExprSyntax(
46+
leftBrace: accessorBlock.leftBrace,
47+
statements: body,
48+
rightBrace: accessorBlock.rightBrace
49+
)
50+
51+
return refactored(
52+
InitializerClauseSyntax(
53+
equal: .equalToken(trailingTrivia: .space),
54+
value: FunctionCallExprSyntax(callee: closure)
55+
)
56+
)
57+
}
58+
59+
guard body.count == 1, let item = body.first?.item else {
60+
throw RefactoringNotApplicableError("getter body is not a single expression")
61+
}
62+
63+
if let item = item.as(ReturnStmtSyntax.self), let expression = item.expression {
64+
let trailingTrivia: Trivia = expression.leadingTrivia.isEmpty ? .space : []
65+
return refactored(
66+
InitializerClauseSyntax(
67+
leadingTrivia: accessorBlock.leftBrace.trivia,
68+
equal: .equalToken(trailingTrivia: trailingTrivia),
69+
value: expression,
70+
trailingTrivia: accessorBlock.rightBrace.trivia.droppingTrailingWhitespace
71+
)
72+
)
73+
} else if var item = item.as(ExprSyntax.self) {
74+
item.trailingTrivia = item.trailingTrivia.droppingTrailingWhitespace
75+
return refactored(
76+
InitializerClauseSyntax(
77+
equal: .equalToken(trailingTrivia: .space),
78+
value: item,
79+
trailingTrivia: accessorBlock.trailingTrivia
80+
)
81+
)
82+
}
83+
84+
throw RefactoringNotApplicableError("could not extract initial value of stored property")
85+
}
86+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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+
import SwiftRefactor
14+
package import SwiftSyntax
15+
16+
package struct ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringProvider {
17+
package static func refactor(syntax: VariableDeclSyntax, in context: Void) throws -> FunctionDeclSyntax {
18+
guard syntax.bindings.count == 1,
19+
let binding = syntax.bindings.first,
20+
let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self)
21+
else { throw RefactoringNotApplicableError("unsupported variable declaration") }
22+
23+
var statements: CodeBlockItemListSyntax
24+
25+
guard let typeAnnotation = binding.typeAnnotation,
26+
var accessorBlock = binding.accessorBlock
27+
else { throw RefactoringNotApplicableError("no type annotation or stored") }
28+
29+
var effectSpecifiers: AccessorEffectSpecifiersSyntax?
30+
31+
switch accessorBlock.accessors {
32+
case .accessors(let accessors):
33+
guard accessors.count == 1, let accessor = accessors.first,
34+
accessor.accessorSpecifier.tokenKind == .keyword(.get), let codeBlock = accessor.body
35+
else { throw RefactoringNotApplicableError("not a getter-only declaration") }
36+
effectSpecifiers = accessor.effectSpecifiers
37+
statements = codeBlock.statements
38+
let accessorSpecifier = accessor.accessorSpecifier
39+
statements.leadingTrivia =
40+
accessorSpecifier.leadingTrivia + accessorSpecifier.trailingTrivia.droppingLeadingWhitespace
41+
+ codeBlock.leftBrace.leadingTrivia.droppingLeadingWhitespace
42+
+ codeBlock.leftBrace.trailingTrivia.droppingLeadingWhitespace
43+
+ statements.leadingTrivia
44+
statements.trailingTrivia += codeBlock.rightBrace.trivia.droppingLeadingWhitespace
45+
statements.trailingTrivia = statements.trailingTrivia.droppingTrailingWhitespace
46+
case .getter(let codeBlock):
47+
statements = codeBlock
48+
}
49+
50+
let returnType = typeAnnotation.type
51+
52+
var returnClause: ReturnClauseSyntax?
53+
let triviaAfterSignature: Trivia
54+
55+
if !returnType.isVoid {
56+
triviaAfterSignature = .space
57+
returnClause = ReturnClauseSyntax(
58+
arrow: .arrowToken(
59+
leadingTrivia: typeAnnotation.colon.leadingTrivia,
60+
trailingTrivia: typeAnnotation.colon.trailingTrivia
61+
),
62+
type: returnType
63+
)
64+
} else {
65+
triviaAfterSignature = typeAnnotation.colon.leadingTrivia + typeAnnotation.colon.trailingTrivia
66+
}
67+
68+
accessorBlock.leftBrace.leadingTrivia = accessorBlock.leftBrace.leadingTrivia.droppingLeadingWhitespace
69+
accessorBlock.rightBrace.trailingTrivia = accessorBlock.rightBrace.trailingTrivia.droppingTrailingWhitespace
70+
71+
let body = CodeBlockSyntax(
72+
leftBrace: accessorBlock.leftBrace,
73+
statements: statements,
74+
rightBrace: accessorBlock.rightBrace
75+
)
76+
77+
var parameterClause = FunctionParameterClauseSyntax(parameters: [])
78+
parameterClause.trailingTrivia = identifierPattern.identifier.trailingTrivia + triviaAfterSignature
79+
80+
let functionEffectSpecifiers = FunctionEffectSpecifiersSyntax(
81+
asyncSpecifier: effectSpecifiers?.asyncSpecifier,
82+
throwsClause: effectSpecifiers?.throwsClause
83+
)
84+
let functionSignature = FunctionSignatureSyntax(
85+
parameterClause: parameterClause,
86+
effectSpecifiers: functionEffectSpecifiers,
87+
returnClause: returnClause
88+
)
89+
90+
return FunctionDeclSyntax(
91+
modifiers: syntax.modifiers,
92+
funcKeyword: .keyword(
93+
.func,
94+
leadingTrivia: syntax.bindingSpecifier.leadingTrivia,
95+
trailingTrivia: syntax.bindingSpecifier.trailingTrivia
96+
),
97+
name: identifierPattern.identifier.with(\.trailingTrivia, []),
98+
signature: functionSignature,
99+
body: body
100+
)
101+
}
102+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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+
import SwiftRefactor
14+
package import SwiftSyntax
15+
import SwiftSyntaxBuilder
16+
17+
package struct ConvertStoredPropertyToComputed: SyntaxRefactoringProvider {
18+
package struct Context {
19+
package let type: TypeSyntax?
20+
21+
package init(type: TypeSyntax? = nil) {
22+
self.type = type
23+
}
24+
}
25+
package static func refactor(syntax: VariableDeclSyntax, in context: Context) throws -> VariableDeclSyntax {
26+
guard syntax.bindings.count == 1, let binding = syntax.bindings.first, let initializer = binding.initializer else {
27+
throw RefactoringNotApplicableError("unsupported variable declaration")
28+
}
29+
30+
var syntax = syntax
31+
32+
if let lazyKeyword = syntax.modifiers.first(where: { $0.name.tokenKind == .keyword(.lazy) }) {
33+
syntax = DeclModifierRemover { $0.id == lazyKeyword.id }
34+
.rewrite(syntax)
35+
.cast(VariableDeclSyntax.self)
36+
}
37+
38+
var codeBlockSyntax: CodeBlockItemListSyntax
39+
40+
if let functionExpression = initializer.value.as(FunctionCallExprSyntax.self),
41+
let closureExpression = functionExpression.calledExpression.as(ClosureExprSyntax.self)
42+
{
43+
guard functionExpression.arguments.isEmpty else {
44+
throw RefactoringNotApplicableError(
45+
"initializer is a closure that takes arguments"
46+
)
47+
}
48+
49+
codeBlockSyntax = closureExpression.statements
50+
codeBlockSyntax.leadingTrivia =
51+
closureExpression.leftBrace.leadingTrivia + closureExpression.leftBrace.trailingTrivia
52+
+ codeBlockSyntax.leadingTrivia
53+
codeBlockSyntax.trailingTrivia +=
54+
closureExpression.trailingTrivia + closureExpression.rightBrace.leadingTrivia
55+
+ closureExpression.rightBrace.trailingTrivia + functionExpression.trailingTrivia
56+
} else {
57+
var body = CodeBlockItemListSyntax([
58+
CodeBlockItemSyntax(
59+
item: .expr(initializer.value)
60+
)
61+
])
62+
body.leadingTrivia = initializer.equal.trailingTrivia + body.leadingTrivia
63+
body.trailingTrivia += .space
64+
codeBlockSyntax = body
65+
}
66+
let typeAnnotation: TypeAnnotationSyntax?
67+
if let existingType = binding.typeAnnotation {
68+
typeAnnotation = existingType
69+
} else if let providedType = context.type {
70+
typeAnnotation = TypeAnnotationSyntax(
71+
colon: .colonToken(trailingTrivia: .space),
72+
type: providedType
73+
)
74+
} else {
75+
typeAnnotation = TypeAnnotationSyntax(
76+
colon: .colonToken(trailingTrivia: .space),
77+
type: TypeSyntax(stringLiteral: "<#Type#>")
78+
)
79+
}
80+
81+
let newBinding =
82+
binding
83+
.with(\.pattern, binding.pattern.with(\.trailingTrivia, []))
84+
.with(\.initializer, nil)
85+
.with(\.typeAnnotation, typeAnnotation)
86+
.with(
87+
\.accessorBlock,
88+
AccessorBlockSyntax(
89+
accessors: .getter(codeBlockSyntax)
90+
)
91+
)
92+
93+
let newBindingSpecifier =
94+
syntax.bindingSpecifier
95+
.with(\.tokenKind, .keyword(.var))
96+
97+
return
98+
syntax
99+
.with(\.bindingSpecifier, newBindingSpecifier)
100+
.with(\.bindings, PatternBindingListSyntax([newBinding]))
101+
}
102+
}

0 commit comments

Comments
 (0)