From 5000d8b976eeabcb82d654843456324bfbebf6a0 Mon Sep 17 00:00:00 2001 From: Vladimir Gusev Date: Wed, 12 Nov 2025 23:39:25 +0300 Subject: [PATCH] Convert between computed properties and zero-parameters functions --- .../CodeActions/SyntaxCodeActions.swift | 2 + .../SyntaxRefactoringCodeActionProvider.swift | 22 ++++ Tests/SourceKitLSPTests/CodeActionTests.swift | 116 ++++++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/Sources/SwiftLanguageService/CodeActions/SyntaxCodeActions.swift b/Sources/SwiftLanguageService/CodeActions/SyntaxCodeActions.swift index ba1631138..82a6f6ae1 100644 --- a/Sources/SwiftLanguageService/CodeActions/SyntaxCodeActions.swift +++ b/Sources/SwiftLanguageService/CodeActions/SyntaxCodeActions.swift @@ -18,9 +18,11 @@ let allSyntaxCodeActions: [SyntaxCodeActionProvider.Type] = { var result: [SyntaxCodeActionProvider.Type] = [ AddDocumentation.self, AddSeparatorsToIntegerLiteral.self, + ConvertComputedPropertyToZeroParameterFunction.self, ConvertIntegerLiteral.self, ConvertJSONToCodableStruct.self, ConvertStringConcatenationToStringInterpolation.self, + ConvertZeroParameterFunctionToComputedProperty.self, FormatRawStringLiteral.self, MigrateToNewIfLetSyntax.self, OpaqueParameterToGeneric.self, diff --git a/Sources/SwiftLanguageService/CodeActions/SyntaxRefactoringCodeActionProvider.swift b/Sources/SwiftLanguageService/CodeActions/SyntaxRefactoringCodeActionProvider.swift index 79831ad81..7f8efc91e 100644 --- a/Sources/SwiftLanguageService/CodeActions/SyntaxRefactoringCodeActionProvider.swift +++ b/Sources/SwiftLanguageService/CodeActions/SyntaxRefactoringCodeActionProvider.swift @@ -113,6 +113,28 @@ extension RemoveSeparatorsFromIntegerLiteral: SyntaxRefactoringCodeActionProvide } } +extension ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringCodeActionProvider { + package static var title: String { "Convert to computed property" } + + static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? { + return scope.innermostNodeContainingRange?.findParentOfSelf( + ofType: FunctionDeclSyntax.self, + stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) } + ) + } +} + +extension ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringCodeActionProvider { + package static var title: String { "Convert to zero parameter function" } + + static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? { + return scope.innermostNodeContainingRange?.findParentOfSelf( + ofType: VariableDeclSyntax.self, + stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) } + ) + } +} + extension SyntaxProtocol { /// Finds the innermost parent of the given type while not walking outside of nodes that satisfy `stoppingIf`. func findParentOfSelf( diff --git a/Tests/SourceKitLSPTests/CodeActionTests.swift b/Tests/SourceKitLSPTests/CodeActionTests.swift index d03d42fb3..88d0ea76a 100644 --- a/Tests/SourceKitLSPTests/CodeActionTests.swift +++ b/Tests/SourceKitLSPTests/CodeActionTests.swift @@ -1180,6 +1180,122 @@ final class CodeActionTests: SourceKitLSPTestCase { ) } + func testConvertFunctionZeroParameterToComputedProperty() async throws { + let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + 1️⃣func someFunction() -> String2️⃣ { return "" }3️⃣ + """, + uri: uri + ) + + let request = CodeActionRequest( + range: positions["1️⃣"].. String 1️⃣{ + 2️⃣return "" + }3️⃣ + """##, + exhaustive: false + ) { uri, positions in + [] + } + } + + func testConvertComputedPropertyToZeroParameterFunction() async throws { + let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + 1️⃣var someFunction: String2️⃣ { return "" }3️⃣ + """, + uri: uri + ) + + let request = CodeActionRequest( + range: positions["1️⃣"].. String { return "" } + """ + ) + ] + ] + ), + command: nil + ) + + XCTAssertTrue(codeActions.contains(expectedCodeAction)) + } + + func testConvertComputedPropertyToZeroParameterFunctionIsNotShownFromTheBody() async throws { + try await assertCodeActions( + ##""" + var someFunction: String 1️⃣{ + 2️⃣return "" + }3️⃣ + """##, + exhaustive: false + ) { uri, positions in + [] + } + } + /// Retrieves the code action at a set of markers and asserts that it matches a list of expected code actions. /// /// - Parameters: