mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-11-04 08:11:06 +01:00
237 lines
7.0 KiB
Swift
237 lines
7.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftRefactor
|
|
package import SwiftSyntax
|
|
|
|
/// Describes a "some" parameter that has been rewritten into a generic
|
|
/// parameter.
|
|
private struct RewrittenSome {
|
|
let original: SomeOrAnyTypeSyntax
|
|
let genericParam: GenericParameterSyntax
|
|
let genericParamRef: IdentifierTypeSyntax
|
|
}
|
|
|
|
/// Rewrite `some` parameters to explicit generic parameters.
|
|
///
|
|
/// ## Before
|
|
///
|
|
/// ```swift
|
|
/// func someFunction(_ input: some Value) {}
|
|
/// ```
|
|
///
|
|
/// ## After
|
|
///
|
|
/// ```swift
|
|
/// func someFunction<T1: Value>(_ input: T1) {}
|
|
/// ```
|
|
private class SomeParameterRewriter: SyntaxRewriter {
|
|
var rewrittenSomeParameters: [RewrittenSome] = []
|
|
|
|
override func visit(_ node: SomeOrAnyTypeSyntax) -> TypeSyntax {
|
|
if node.someOrAnySpecifier.text != "some" {
|
|
return TypeSyntax(node)
|
|
}
|
|
|
|
let paramName = "T\(rewrittenSomeParameters.count + 1)"
|
|
let paramNameSyntax = TokenSyntax.identifier(paramName)
|
|
|
|
let inheritedType: TypeSyntax?
|
|
let colon: TokenSyntax?
|
|
if node.constraint.description != "Any" {
|
|
colon = .colonToken()
|
|
inheritedType = node.constraint.with(\.leadingTrivia, .space)
|
|
} else {
|
|
colon = nil
|
|
inheritedType = nil
|
|
}
|
|
|
|
let genericParam = GenericParameterSyntax(
|
|
attributes: [],
|
|
specifier: nil,
|
|
name: paramNameSyntax,
|
|
colon: colon,
|
|
inheritedType: inheritedType,
|
|
trailingComma: nil
|
|
)
|
|
|
|
let genericParamRef = IdentifierTypeSyntax(
|
|
name: .identifier(paramName),
|
|
genericArgumentClause: nil
|
|
)
|
|
|
|
rewrittenSomeParameters.append(
|
|
.init(
|
|
original: node,
|
|
genericParam: genericParam,
|
|
genericParamRef: genericParamRef
|
|
)
|
|
)
|
|
|
|
return TypeSyntax(genericParamRef)
|
|
}
|
|
|
|
override func visit(_ node: TupleTypeSyntax) -> TypeSyntax {
|
|
let newNode = super.visit(node)
|
|
|
|
// If this tuple type is simple parentheses around a replaced "some"
|
|
// parameter, drop the parentheses.
|
|
guard let newTuple = newNode.as(TupleTypeSyntax.self),
|
|
newTuple.elements.count == 1,
|
|
let onlyElement = newTuple.elements.first,
|
|
onlyElement.firstName == nil,
|
|
onlyElement.ellipsis == nil,
|
|
let onlyIdentifierType =
|
|
onlyElement.type.as(IdentifierTypeSyntax.self),
|
|
rewrittenSomeParameters.first(
|
|
where: { $0.genericParamRef.name.text == onlyIdentifierType.name.text }
|
|
) != nil
|
|
else {
|
|
return newNode
|
|
}
|
|
|
|
return TypeSyntax(onlyIdentifierType)
|
|
}
|
|
}
|
|
|
|
/// Rewrite `some` parameters to explicit generic parameters.
|
|
///
|
|
/// ## Before
|
|
///
|
|
/// ```swift
|
|
/// func someFunction(_ input: some Value) {}
|
|
/// ```
|
|
///
|
|
/// ## After
|
|
///
|
|
/// ```swift
|
|
/// func someFunction<T1: Value>(_ input: T1) {}
|
|
/// ```
|
|
package struct OpaqueParameterToGeneric: SyntaxRefactoringProvider {
|
|
/// Replace all of the "some" parameters in the given parameter clause with
|
|
/// freshly-created generic parameters.
|
|
///
|
|
/// - Returns: nil if there was nothing to rewrite, or a pair of the
|
|
/// rewritten parameters and augmented generic parameter list.
|
|
static func replaceSomeParameters(
|
|
in params: FunctionParameterClauseSyntax,
|
|
augmenting genericParams: GenericParameterClauseSyntax?
|
|
) -> (FunctionParameterClauseSyntax, GenericParameterClauseSyntax)? {
|
|
let rewriter = SomeParameterRewriter(viewMode: .sourceAccurate)
|
|
let rewrittenParams = rewriter.visit(params.parameters)
|
|
|
|
if rewriter.rewrittenSomeParameters.isEmpty {
|
|
return nil
|
|
}
|
|
|
|
var newGenericParams: [GenericParameterSyntax] = []
|
|
if let genericParams {
|
|
newGenericParams.append(contentsOf: genericParams.parameters)
|
|
}
|
|
|
|
for rewritten in rewriter.rewrittenSomeParameters {
|
|
let newGenericParam = rewritten.genericParam
|
|
|
|
// Add a trailing comma to the prior generic parameter, if there is one.
|
|
if let lastNewGenericParam = newGenericParams.last {
|
|
newGenericParams[newGenericParams.count - 1] =
|
|
lastNewGenericParam.with(\.trailingComma, .commaToken())
|
|
newGenericParams.append(newGenericParam.with(\.leadingTrivia, .space))
|
|
} else {
|
|
newGenericParams.append(newGenericParam)
|
|
}
|
|
}
|
|
|
|
let newGenericParamSyntax = GenericParameterListSyntax(newGenericParams)
|
|
let newGenericParamClause: GenericParameterClauseSyntax
|
|
if let genericParams {
|
|
newGenericParamClause = genericParams.with(
|
|
\.parameters,
|
|
newGenericParamSyntax
|
|
)
|
|
} else {
|
|
newGenericParamClause = GenericParameterClauseSyntax(
|
|
leftAngle: .leftAngleToken(),
|
|
parameters: newGenericParamSyntax,
|
|
genericWhereClause: nil,
|
|
rightAngle: .rightAngleToken()
|
|
)
|
|
}
|
|
|
|
return (
|
|
params.with(\.parameters, rewrittenParams),
|
|
newGenericParamClause
|
|
)
|
|
}
|
|
|
|
package static func refactor(
|
|
syntax decl: DeclSyntax,
|
|
in context: Void
|
|
) throws -> DeclSyntax {
|
|
// Function declaration.
|
|
if let funcSyntax = decl.as(FunctionDeclSyntax.self) {
|
|
guard
|
|
let (newInput, newGenericParams) = replaceSomeParameters(
|
|
in: funcSyntax.signature.parameterClause,
|
|
augmenting: funcSyntax.genericParameterClause
|
|
)
|
|
else {
|
|
throw RefactoringNotApplicableError("found no parameters to rewrite")
|
|
}
|
|
|
|
return DeclSyntax(
|
|
funcSyntax
|
|
.with(\.signature, funcSyntax.signature.with(\.parameterClause, newInput))
|
|
.with(\.genericParameterClause, newGenericParams)
|
|
)
|
|
}
|
|
|
|
// Initializer declaration.
|
|
if let initSyntax = decl.as(InitializerDeclSyntax.self) {
|
|
guard
|
|
let (newInput, newGenericParams) = replaceSomeParameters(
|
|
in: initSyntax.signature.parameterClause,
|
|
augmenting: initSyntax.genericParameterClause
|
|
)
|
|
else {
|
|
throw RefactoringNotApplicableError("found no parameters to rewrite")
|
|
}
|
|
|
|
return DeclSyntax(
|
|
initSyntax
|
|
.with(\.signature, initSyntax.signature.with(\.parameterClause, newInput))
|
|
.with(\.genericParameterClause, newGenericParams)
|
|
)
|
|
}
|
|
|
|
// Subscript declaration.
|
|
if let subscriptSyntax = decl.as(SubscriptDeclSyntax.self) {
|
|
guard
|
|
let (newIndices, newGenericParams) = replaceSomeParameters(
|
|
in: subscriptSyntax.parameterClause,
|
|
augmenting: subscriptSyntax.genericParameterClause
|
|
)
|
|
else {
|
|
throw RefactoringNotApplicableError("found no parameters to rewrite")
|
|
}
|
|
|
|
return DeclSyntax(
|
|
subscriptSyntax
|
|
.with(\.parameterClause, newIndices)
|
|
.with(\.genericParameterClause, newGenericParams)
|
|
)
|
|
}
|
|
|
|
throw RefactoringNotApplicableError("unsupported declaration")
|
|
}
|
|
}
|