Skip to content

Commit ecd3f9d

Browse files
committed
[SwiftRefactor] EditRefactoringProvider: Make textRefactor and refactor requirements throwing
The result of both has been made non-optional as well. The idea here is to be able to produce a description for every failure instead of `nil` result, this is something that package model refactoring actions already do. The only error we currently have is "not applicable" but that's just a start.
1 parent 9453f46 commit ecd3f9d

14 files changed

+99
-75
lines changed

EditorExtension/SwiftRefactorExtension/SourceEditorCommand.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ final class SourceEditorCommand: NSObject, XCSourceEditorCommand {
4040
}
4141

4242
override func visitAny(_ node: Syntax) -> Syntax? {
43-
func withOpenedRefactoringProvider<T: SyntaxRefactoringProvider>(_ providerType: T.Type) -> Syntax? {
43+
func withOpenedRefactoringProvider<T: SyntaxRefactoringProvider>(_ providerType: T.Type) throws -> Syntax? {
4444
guard
4545
let input = node.as(providerType.Input.self),
4646
providerType.Context.self == Void.self
4747
else {
4848
return nil
4949
}
5050
let context = unsafeBitCast(Void(), to: providerType.Context.self)
51-
return providerType.refactor(syntax: input, in: context).map { Syntax($0) }
51+
return try Syntax(providerType.refactor(syntax: input, in: context))
5252
}
5353

54-
return _openExistential(self.provider, do: withOpenedRefactoringProvider)
54+
return try? _openExistential(self.provider, do: withOpenedRefactoringProvider)
5555
}
5656
}
5757

Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ import SwiftSyntax
3737
/// 0b1_010
3838
/// ```
3939
public struct AddSeparatorsToIntegerLiteral: SyntaxRefactoringProvider {
40-
public static func refactor(syntax lit: IntegerLiteralExprSyntax, in context: Void) -> IntegerLiteralExprSyntax? {
40+
public static func refactor(
41+
syntax lit: IntegerLiteralExprSyntax,
42+
in context: Void
43+
) throws -> IntegerLiteralExprSyntax {
4144
if lit.literal.text.contains("_") {
42-
guard let strippedLiteral = RemoveSeparatorsFromIntegerLiteral.refactor(syntax: lit) else {
43-
return nil
44-
}
45+
let strippedLiteral = try RemoveSeparatorsFromIntegerLiteral.refactor(syntax: lit)
4546
return self.addSeparators(to: strippedLiteral)
4647
} else {
4748
return self.addSeparators(to: lit)

Sources/SwiftRefactor/CallToTrailingClosures.swift

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,39 +55,43 @@ public struct CallToTrailingClosures: SyntaxRefactoringProvider {
5555

5656
/// Apply the refactoring to a given syntax node. If either a
5757
/// non-function-like syntax node is passed, or the refactoring fails,
58-
/// `nil` is returned.
59-
// TODO: Rather than returning nil, we should consider throwing errors with
60-
// appropriate messages instead.
58+
/// error is thrown.
6159
public static func refactor(
6260
syntax: Syntax,
6361
in context: Context = Context()
64-
) -> Syntax? {
65-
guard let call = syntax.asProtocol(CallLikeSyntax.self) else { return nil }
66-
return Syntax(fromProtocol: _refactor(syntax: call, in: context))
62+
) throws -> Syntax {
63+
guard let call = syntax.asProtocol(CallLikeSyntax.self) else {
64+
throw RefactoringNotApplicableError("not a call")
65+
}
66+
return try Syntax(fromProtocol: _refactor(syntax: call, in: context))
6767
}
6868

6969
@available(*, deprecated, message: "Pass a Syntax argument instead of FunctionCallExprSyntax")
7070
public static func refactor(
7171
syntax call: FunctionCallExprSyntax,
7272
in context: Context = Context()
73-
) -> FunctionCallExprSyntax? {
74-
_refactor(syntax: call, in: context)
73+
) throws -> FunctionCallExprSyntax {
74+
try _refactor(syntax: call, in: context)
7575
}
7676

7777
internal static func _refactor<C: CallLikeSyntax>(
7878
syntax call: C,
7979
in context: Context = Context()
80-
) -> C? {
81-
let converted = call.convertToTrailingClosures(from: context.startAtArgument)
82-
return converted?.formatted().as(C.self)
80+
) throws -> C {
81+
let converted = try call.convertToTrailingClosures(from: context.startAtArgument)
82+
83+
guard let formatted = converted.formatted().as(C.self) else {
84+
throw RefactoringNotApplicableError("cannot cast formatted call to \(C.self)")
85+
}
86+
87+
return formatted
8388
}
8489
}
8590

8691
extension CallLikeSyntax {
87-
fileprivate func convertToTrailingClosures(from startAtArgument: Int) -> Self? {
92+
fileprivate func convertToTrailingClosures(from startAtArgument: Int) throws -> Self {
8893
guard trailingClosure == nil, additionalTrailingClosures.isEmpty, leftParen != nil, rightParen != nil else {
89-
// Already have trailing closures
90-
return nil
94+
throw RefactoringNotApplicableError("call already uses trailing closures")
9195
}
9296

9397
var closures = [(original: LabeledExprSyntax, closure: ClosureExprSyntax)]()
@@ -106,7 +110,7 @@ extension CallLikeSyntax {
106110
}
107111

108112
guard !closures.isEmpty else {
109-
return nil
113+
throw RefactoringNotApplicableError("no arguments to convert to closures")
110114
}
111115

112116
// First trailing closure won't have label/colon. Transfer their trivia.

Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ import SwiftSyntax
1717
#endif
1818

1919
public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider {
20-
public static func refactor(syntax: VariableDeclSyntax, in context: ()) -> VariableDeclSyntax? {
21-
guard syntax.bindings.count == 1, let binding = syntax.bindings.first,
22-
let accessorBlock = binding.accessorBlock, case let .getter(body) = accessorBlock.accessors, !body.isEmpty
20+
public static func refactor(syntax: VariableDeclSyntax, in context: ()) throws -> VariableDeclSyntax {
21+
guard syntax.bindings.count == 1, let binding = syntax.bindings.first else {
22+
throw RefactoringNotApplicableError("unsupported variable declaration")
23+
}
24+
25+
guard let accessorBlock = binding.accessorBlock,
26+
case let .getter(body) = accessorBlock.accessors, !body.isEmpty
2327
else {
24-
return nil
28+
throw RefactoringNotApplicableError("getter is missing or empty")
2529
}
2630

2731
let refactored = { (initializer: InitializerClauseSyntax) -> VariableDeclSyntax in
@@ -55,7 +59,7 @@ public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider {
5559
}
5660

5761
guard body.count == 1, let item = body.first?.item else {
58-
return nil
62+
throw RefactoringNotApplicableError("getter body is not a single expression")
5963
}
6064

6165
if let item = item.as(ReturnStmtSyntax.self), let expression = item.expression {
@@ -79,6 +83,6 @@ public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider {
7983
)
8084
}
8185

82-
return nil
86+
throw RefactoringNotApplicableError("getter body is unsupported: \(item)")
8387
}
8488
}

Sources/SwiftRefactor/ConvertComputedPropertyToZeroParameterFunction.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,25 @@ import SwiftSyntax
1717
#endif
1818

1919
public struct ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringProvider {
20-
public static func refactor(syntax: VariableDeclSyntax, in context: Void) -> FunctionDeclSyntax? {
20+
public static func refactor(syntax: VariableDeclSyntax, in context: Void) throws -> FunctionDeclSyntax {
2121
guard syntax.bindings.count == 1,
2222
let binding = syntax.bindings.first,
2323
let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self)
24-
else { return nil }
24+
else { throw RefactoringNotApplicableError("unsupported variable declaration") }
2525

2626
var statements: CodeBlockItemListSyntax
2727

2828
guard let typeAnnotation = binding.typeAnnotation,
2929
var accessorBlock = binding.accessorBlock
30-
else { return nil }
30+
else { throw RefactoringNotApplicableError("no type annotation or stored") }
3131

3232
var effectSpecifiers: AccessorEffectSpecifiersSyntax?
3333

3434
switch accessorBlock.accessors {
3535
case .accessors(let accessors):
3636
guard accessors.count == 1, let accessor = accessors.first,
3737
accessor.accessorSpecifier.tokenKind == .keyword(.get), let codeBlock = accessor.body
38-
else { return nil }
38+
else { throw RefactoringNotApplicableError("not a getter-only declaration") }
3939
effectSpecifiers = accessor.effectSpecifiers
4040
statements = codeBlock.statements
4141
let accessorSpecifier = accessor.accessorSpecifier

Sources/SwiftRefactor/ConvertStoredPropertyToComputed.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ import SwiftSyntax
1717
#endif
1818

1919
public struct ConvertStoredPropertyToComputed: SyntaxRefactoringProvider {
20-
public static func refactor(syntax: VariableDeclSyntax, in context: ()) -> VariableDeclSyntax? {
20+
public static func refactor(syntax: VariableDeclSyntax, in context: ()) throws -> VariableDeclSyntax {
2121
guard syntax.bindings.count == 1, let binding = syntax.bindings.first, let initializer = binding.initializer else {
22-
return nil
22+
throw RefactoringNotApplicableError("unsupported variable declaration")
2323
}
2424

2525
var codeBlockSyntax: CodeBlockItemListSyntax
2626

2727
if let functionExpression = initializer.value.as(FunctionCallExprSyntax.self),
2828
let closureExpression = functionExpression.calledExpression.as(ClosureExprSyntax.self)
2929
{
30-
guard functionExpression.arguments.isEmpty else { return nil }
30+
guard functionExpression.arguments.isEmpty else {
31+
throw RefactoringNotApplicableError("not a nullary call")
32+
}
3133

3234
codeBlockSyntax = closureExpression.statements
3335
codeBlockSyntax.leadingTrivia =

Sources/SwiftRefactor/ConvertZeroParameterFunctionToComputedProperty.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import SwiftSyntax
1717
#endif
1818

1919
public struct ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringProvider {
20-
public static func refactor(syntax: FunctionDeclSyntax, in context: ()) -> VariableDeclSyntax? {
20+
public static func refactor(syntax: FunctionDeclSyntax, in context: ()) throws -> VariableDeclSyntax {
2121
guard syntax.signature.parameterClause.parameters.isEmpty,
2222
let body = syntax.body
23-
else { return nil }
23+
else { throw RefactoringNotApplicableError("not a zero parameter function") }
2424

2525
let variableName = PatternSyntax(
2626
IdentifierPatternSyntax(

Sources/SwiftRefactor/ExpandEditorPlaceholder.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,19 @@ public struct ExpandEditorPlaceholdersToLiteralClosures: SyntaxRefactoringProvid
275275
public static func refactor(
276276
syntax: Syntax,
277277
in context: Context = Context()
278-
) -> Syntax? {
279-
guard let call = syntax.asProtocol(CallLikeSyntax.self) else { return nil }
280-
let expanded = Self.expandClosurePlaceholders(
281-
in: call,
282-
ifIncluded: nil,
283-
context: context
284-
)
278+
) throws -> Syntax {
279+
guard let call = syntax.asProtocol(CallLikeSyntax.self) else {
280+
throw RefactoringNotApplicableError("not a call")
281+
}
282+
guard
283+
let expanded = Self.expandClosurePlaceholders(
284+
in: call,
285+
ifIncluded: nil,
286+
context: context
287+
)
288+
else {
289+
throw RefactoringNotApplicableError("could not expand closure placeholders")
290+
}
285291
return Syntax(fromProtocol: expanded)
286292
}
287293

@@ -325,7 +331,7 @@ public struct ExpandEditorPlaceholdersToLiteralClosures: SyntaxRefactoringProvid
325331
let callToTrailingContext = CallToTrailingClosures.Context(
326332
startAtArgument: call.arguments.count - expanded.numClosures
327333
)
328-
return CallToTrailingClosures._refactor(syntax: expanded.expr, in: callToTrailingContext)
334+
return try? CallToTrailingClosures._refactor(syntax: expanded.expr, in: callToTrailingContext)
329335
}
330336
}
331337
}

Sources/SwiftRefactor/FormatRawStringLiteral.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import SwiftSyntax
3535
/// "Hello World"
3636
/// ```
3737
public struct FormatRawStringLiteral: SyntaxRefactoringProvider {
38-
public static func refactor(syntax lit: StringLiteralExprSyntax, in context: Void) -> StringLiteralExprSyntax? {
38+
public static func refactor(syntax lit: StringLiteralExprSyntax, in context: Void) -> StringLiteralExprSyntax {
3939
var maximumHashes = 0
4040
for segment in lit.segments {
4141
switch segment {

Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import SwiftSyntax
3939
/// // ...
4040
/// }
4141
public struct MigrateToNewIfLetSyntax: SyntaxRefactoringProvider {
42-
public static func refactor(syntax node: IfExprSyntax, in context: ()) -> IfExprSyntax? {
42+
public static func refactor(syntax node: IfExprSyntax, in context: ()) -> IfExprSyntax {
4343
// Visit all conditions in the node.
4444
let newConditions = node.conditions.enumerated().map { (index, condition) -> ConditionElementListSyntax.Element in
4545
var conditionCopy = condition

0 commit comments

Comments
 (0)