|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2022 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 SwiftSyntax |
| 14 | + |
| 15 | +/// Describes a "some" parameter that has been rewritten into a generic |
| 16 | +/// parameter. |
| 17 | +fileprivate struct RewrittenSome { |
| 18 | + let original: ConstrainedSugarTypeSyntax |
| 19 | + let genericParam: GenericParameterSyntax |
| 20 | + let genericParamRef: SimpleTypeIdentifierSyntax |
| 21 | +} |
| 22 | + |
| 23 | +/// Rewrite `some` parameters to explicit generic parameters. |
| 24 | +/// |
| 25 | +/// ## Before |
| 26 | +/// |
| 27 | +/// ```swift |
| 28 | +/// func someFunction(_ input: some Value) {} |
| 29 | +/// ``` |
| 30 | +/// |
| 31 | +/// ## After |
| 32 | +/// |
| 33 | +/// ```swift |
| 34 | +/// func someFunction<T1: Value>(_ input: T1) {} |
| 35 | +/// ``` |
| 36 | +fileprivate class SomeParameterRewriter: SyntaxRewriter { |
| 37 | + var rewrittenSomeParameters: [RewrittenSome] = [] |
| 38 | + |
| 39 | + override func visit(_ node: ConstrainedSugarTypeSyntax) -> TypeSyntax { |
| 40 | + if node.someOrAnySpecifier.text != "some" { |
| 41 | + return TypeSyntax(node) |
| 42 | + } |
| 43 | + |
| 44 | + let paramName = "T\(rewrittenSomeParameters.count + 1)" |
| 45 | + let paramNameSyntax = TokenSyntax.identifier(paramName) |
| 46 | + |
| 47 | + let inheritedType: TypeSyntax? |
| 48 | + let colon: TokenSyntax? |
| 49 | + if node.baseType.description != "Any" { |
| 50 | + colon = .colonToken() |
| 51 | + inheritedType = node.baseType.withLeadingTrivia(.space) |
| 52 | + } else { |
| 53 | + colon = nil |
| 54 | + inheritedType = nil |
| 55 | + } |
| 56 | + |
| 57 | + let genericParam = GenericParameterSyntax( |
| 58 | + attributes: nil, name: paramNameSyntax, ellipsis: nil, colon: colon, |
| 59 | + inheritedType: inheritedType, trailingComma: nil |
| 60 | + ) |
| 61 | + |
| 62 | + let genericParamRef = SimpleTypeIdentifierSyntax( |
| 63 | + name: .identifier(paramName), genericArgumentClause: nil |
| 64 | + ) |
| 65 | + |
| 66 | + rewrittenSomeParameters.append( |
| 67 | + .init( |
| 68 | + original: node, genericParam: genericParam, |
| 69 | + genericParamRef: genericParamRef)) |
| 70 | + |
| 71 | + return TypeSyntax(genericParamRef) |
| 72 | + } |
| 73 | + |
| 74 | + override func visit(_ node: TupleTypeSyntax) -> TypeSyntax { |
| 75 | + let newNode = super.visit(node) |
| 76 | + |
| 77 | + // If this tuple type is simple parentheses around a replaced "some" |
| 78 | + // parameter, drop the parentheses. |
| 79 | + guard let newTuple = newNode.as(TupleTypeSyntax.self), |
| 80 | + newTuple.elements.count == 1, |
| 81 | + let onlyElement = newTuple.elements.first, |
| 82 | + onlyElement.name == nil, |
| 83 | + onlyElement.ellipsis == nil, |
| 84 | + let onlyIdentifierType = |
| 85 | + onlyElement.type.as(SimpleTypeIdentifierSyntax.self), |
| 86 | + rewrittenSomeParameters.first( |
| 87 | + where: { $0.genericParamRef.name.text == onlyIdentifierType.name.text } |
| 88 | + ) != nil |
| 89 | + else { |
| 90 | + return newNode |
| 91 | + } |
| 92 | + |
| 93 | + return TypeSyntax(onlyIdentifierType) |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +/// Rewrite `some` parameters to explicit generic parameters. |
| 98 | +/// |
| 99 | +/// ## Before |
| 100 | +/// |
| 101 | +/// ```swift |
| 102 | +/// func someFunction(_ input: some Value) {} |
| 103 | +/// ``` |
| 104 | +/// |
| 105 | +/// ## After |
| 106 | +/// |
| 107 | +/// ```swift |
| 108 | +/// func someFunction<T1: Value>(_ input: T1) {} |
| 109 | +/// ``` |
| 110 | +public struct OpaqueParameterToGeneric: RefactoringProvider { |
| 111 | + /// Replace all of the "some" parameters in the given parameter clause with |
| 112 | + /// freshly-created generic parameters. |
| 113 | + /// |
| 114 | + /// - Returns: nil if there was nothing to rewrite, or a pair of the |
| 115 | + /// rewritten parameters and augmented generic parameter list. |
| 116 | + static func replaceSomeParameters( |
| 117 | + in params: ParameterClauseSyntax, |
| 118 | + augmenting genericParams: GenericParameterClauseSyntax? |
| 119 | + ) -> (ParameterClauseSyntax, GenericParameterClauseSyntax)? { |
| 120 | + let rewriter = SomeParameterRewriter() |
| 121 | + let rewrittenParams = rewriter.visit(params.parameterList) |
| 122 | + |
| 123 | + if rewriter.rewrittenSomeParameters.isEmpty { |
| 124 | + return nil |
| 125 | + } |
| 126 | + |
| 127 | + var newGenericParams: [GenericParameterSyntax] = [] |
| 128 | + if let genericParams = genericParams { |
| 129 | + newGenericParams.append(contentsOf: genericParams.genericParameterList) |
| 130 | + } |
| 131 | + |
| 132 | + for rewritten in rewriter.rewrittenSomeParameters { |
| 133 | + let newGenericParam = rewritten.genericParam |
| 134 | + |
| 135 | + // Add a trailing comma to the prior generic parameter, if there is one. |
| 136 | + if let lastNewGenericParam = newGenericParams.last { |
| 137 | + newGenericParams[newGenericParams.count-1] = |
| 138 | + lastNewGenericParam.withTrailingComma(.commaToken()) |
| 139 | + newGenericParams.append(newGenericParam.withLeadingTrivia(.space)) |
| 140 | + } else { |
| 141 | + newGenericParams.append(newGenericParam) |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + let newGenericParamSyntax = GenericParameterListSyntax(newGenericParams) |
| 146 | + let newGenericParamClause: GenericParameterClauseSyntax |
| 147 | + if let genericParams = genericParams { |
| 148 | + newGenericParamClause = genericParams.withGenericParameterList( |
| 149 | + newGenericParamSyntax |
| 150 | + ) |
| 151 | + } else { |
| 152 | + newGenericParamClause = GenericParameterClauseSyntax( |
| 153 | + leftAngleBracket: .leftAngleToken(), |
| 154 | + genericParameterList: newGenericParamSyntax, |
| 155 | + genericWhereClause: nil, |
| 156 | + rightAngleBracket: .rightAngleToken() |
| 157 | + ) |
| 158 | + } |
| 159 | + |
| 160 | + return ( |
| 161 | + params.withParameterList(rewrittenParams), |
| 162 | + newGenericParamClause |
| 163 | + ) |
| 164 | + } |
| 165 | + |
| 166 | + public static func refactor( |
| 167 | + syntax decl: DeclSyntax, in context: Void |
| 168 | + ) -> DeclSyntax? { |
| 169 | + // Function declaration. |
| 170 | + if let funcSyntax = decl.as(FunctionDeclSyntax.self) { |
| 171 | + guard let (newInput, newGenericParams) = replaceSomeParameters( |
| 172 | + in: funcSyntax.signature.input, |
| 173 | + augmenting: funcSyntax.genericParameterClause |
| 174 | + ) else { |
| 175 | + return nil |
| 176 | + } |
| 177 | + |
| 178 | + return DeclSyntax( |
| 179 | + funcSyntax |
| 180 | + .withSignature(funcSyntax.signature.withInput(newInput)) |
| 181 | + .withGenericParameterClause(newGenericParams) |
| 182 | + ) |
| 183 | + } |
| 184 | + |
| 185 | + // Initializer declaration. |
| 186 | + if let initSyntax = decl.as(InitializerDeclSyntax.self) { |
| 187 | + guard let (newInput, newGenericParams) = replaceSomeParameters( |
| 188 | + in: initSyntax.signature.input, |
| 189 | + augmenting: initSyntax.genericParameterClause |
| 190 | + ) else { |
| 191 | + return nil |
| 192 | + } |
| 193 | + |
| 194 | + return DeclSyntax( |
| 195 | + initSyntax |
| 196 | + .withSignature(initSyntax.signature.withInput(newInput)) |
| 197 | + .withGenericParameterClause(newGenericParams) |
| 198 | + ) |
| 199 | + } |
| 200 | + |
| 201 | + // Subscript declaration. |
| 202 | + if let subscriptSyntax = decl.as(SubscriptDeclSyntax.self) { |
| 203 | + guard let (newIndices, newGenericParams) = replaceSomeParameters( |
| 204 | + in: subscriptSyntax.indices, |
| 205 | + augmenting: subscriptSyntax.genericParameterClause |
| 206 | + ) else { |
| 207 | + return nil |
| 208 | + } |
| 209 | + |
| 210 | + return DeclSyntax( |
| 211 | + subscriptSyntax |
| 212 | + .withIndices(newIndices) |
| 213 | + .withGenericParameterClause(newGenericParams) |
| 214 | + ) |
| 215 | + } |
| 216 | + |
| 217 | + return nil |
| 218 | + } |
| 219 | +} |
| 220 | + |
0 commit comments