xref: /aosp_15_r20/external/moshi/moshi-kotlin-codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/kotlintypes.kt (revision 238ab3e782f339ab327592a602fa7df0a3f729ad)
1 /*
<lambda>null2  * Copyright (C) 2018 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.moshi.kotlin.codegen.api
17 
18 import com.squareup.kotlinpoet.ANY
19 import com.squareup.kotlinpoet.ARRAY
20 import com.squareup.kotlinpoet.BOOLEAN
21 import com.squareup.kotlinpoet.BYTE
22 import com.squareup.kotlinpoet.CHAR
23 import com.squareup.kotlinpoet.ClassName
24 import com.squareup.kotlinpoet.CodeBlock
25 import com.squareup.kotlinpoet.DOUBLE
26 import com.squareup.kotlinpoet.DelicateKotlinPoetApi
27 import com.squareup.kotlinpoet.FLOAT
28 import com.squareup.kotlinpoet.INT
29 import com.squareup.kotlinpoet.KModifier
30 import com.squareup.kotlinpoet.LONG
31 import com.squareup.kotlinpoet.LambdaTypeName
32 import com.squareup.kotlinpoet.NOTHING
33 import com.squareup.kotlinpoet.ParameterizedTypeName
34 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
35 import com.squareup.kotlinpoet.SHORT
36 import com.squareup.kotlinpoet.STAR
37 import com.squareup.kotlinpoet.TypeName
38 import com.squareup.kotlinpoet.TypeVariableName
39 import com.squareup.kotlinpoet.UNIT
40 import com.squareup.kotlinpoet.WildcardTypeName
41 import com.squareup.kotlinpoet.asClassName
42 import com.squareup.kotlinpoet.asTypeName
43 import java.lang.reflect.Array
44 
45 internal fun TypeName.rawType(): ClassName {
46   return findRawType() ?: throw IllegalArgumentException("Cannot get raw type from $this")
47 }
48 
findRawTypenull49 internal fun TypeName.findRawType(): ClassName? {
50   return when (this) {
51     is ClassName -> this
52     is ParameterizedTypeName -> rawType
53     is LambdaTypeName -> {
54       var count = parameters.size
55       if (receiver != null) {
56         count++
57       }
58       val functionSimpleName = if (count >= 23) {
59         "FunctionN"
60       } else {
61         "Function$count"
62       }
63       ClassName("kotlin.jvm.functions", functionSimpleName)
64     }
65     else -> null
66   }
67 }
68 
defaultPrimitiveValuenull69 internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
70   when (this) {
71     BOOLEAN -> CodeBlock.of("false")
72     CHAR -> CodeBlock.of("0.toChar()")
73     BYTE -> CodeBlock.of("0.toByte()")
74     SHORT -> CodeBlock.of("0.toShort()")
75     INT -> CodeBlock.of("0")
76     FLOAT -> CodeBlock.of("0f")
77     LONG -> CodeBlock.of("0L")
78     DOUBLE -> CodeBlock.of("0.0")
79     UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
80     else -> CodeBlock.of("null")
81   }
82 
83 @OptIn(DelicateKotlinPoetApi::class)
asTypeBlocknull84 internal fun TypeName.asTypeBlock(): CodeBlock {
85   if (annotations.isNotEmpty()) {
86     return copy(annotations = emptyList()).asTypeBlock()
87   }
88   when (this) {
89     is ParameterizedTypeName -> {
90       return if (rawType == ARRAY) {
91         val componentType = typeArguments[0]
92         if (componentType is ParameterizedTypeName) {
93           // "generic" array just uses the component's raw type
94           // java.lang.reflect.Array.newInstance(<raw-type>, 0).javaClass
95           CodeBlock.of(
96             "%T.newInstance(%L, 0).javaClass",
97             Array::class.java.asClassName(),
98             componentType.rawType.asTypeBlock()
99           )
100         } else {
101           CodeBlock.of("%T::class.java", copy(nullable = false))
102         }
103       } else {
104         rawType.asTypeBlock()
105       }
106     }
107     is TypeVariableName -> {
108       val bound = bounds.firstOrNull() ?: ANY
109       return bound.asTypeBlock()
110     }
111     is LambdaTypeName -> return rawType().asTypeBlock()
112     is ClassName -> {
113       // Check against the non-nullable version for equality, but we'll keep the nullability in
114       // consideration when creating the CodeBlock if needed.
115       return when (copy(nullable = false)) {
116         BOOLEAN, CHAR, BYTE, SHORT, INT, FLOAT, LONG, DOUBLE -> {
117           if (isNullable) {
118             // Remove nullable but keep the java object type
119             CodeBlock.of("%T::class.javaObjectType", copy(nullable = false))
120           } else {
121             CodeBlock.of("%T::class.javaPrimitiveType", this)
122           }
123         }
124         UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
125         else -> CodeBlock.of("%T::class.java", copy(nullable = false))
126       }
127     }
128     else -> throw UnsupportedOperationException("Parameter with type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, or type variables are allowed.")
129   }
130 }
131 
checkIsVisibilitynull132 internal fun KModifier.checkIsVisibility() {
133   require(ordinal <= ordinal) {
134     "Visibility must be one of ${(0..ordinal).joinToString { KModifier.values()[it].name }}. Is $name"
135   }
136 }
137 
stripTypeVarVariancenull138 internal fun TypeName.stripTypeVarVariance(resolver: TypeVariableResolver): TypeName {
139   return when (this) {
140     is ClassName -> this
141     is ParameterizedTypeName -> {
142       deepCopy { it.stripTypeVarVariance(resolver) }
143     }
144     is TypeVariableName -> resolver[name]
145     is WildcardTypeName -> deepCopy { it.stripTypeVarVariance(resolver) }
146     else -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.")
147   }
148 }
149 
deepCopynull150 internal fun ParameterizedTypeName.deepCopy(
151   transform: (TypeName) -> TypeName
152 ): ParameterizedTypeName {
153   return rawType.parameterizedBy(typeArguments.map { transform(it) })
154     .copy(nullable = isNullable, annotations = annotations, tags = tags)
155 }
156 
deepCopynull157 internal fun TypeVariableName.deepCopy(
158   variance: KModifier? = this.variance,
159   transform: (TypeName) -> TypeName
160 ): TypeVariableName {
161   return TypeVariableName(name = name, bounds = bounds.map { transform(it) }, variance = variance)
162     .copy(nullable = isNullable, annotations = annotations, tags = tags)
163 }
164 
deepCopynull165 internal fun WildcardTypeName.deepCopy(transform: (TypeName) -> TypeName): TypeName {
166   // TODO Would be nice if KotlinPoet modeled these easier.
167   // Producer type - empty inTypes, single element outTypes
168   // Consumer type - single element inTypes, single ANY element outType.
169   return when {
170     this == STAR -> this
171     outTypes.isNotEmpty() && inTypes.isEmpty() -> {
172       WildcardTypeName.producerOf(transform(outTypes[0]))
173         .copy(nullable = isNullable, annotations = annotations)
174     }
175     inTypes.isNotEmpty() -> {
176       WildcardTypeName.consumerOf(transform(inTypes[0]))
177         .copy(nullable = isNullable, annotations = annotations)
178     }
179     else -> throw UnsupportedOperationException("Not possible.")
180   }
181 }
182 
deepCopynull183 internal fun LambdaTypeName.deepCopy(transform: (TypeName) -> TypeName): TypeName {
184   return LambdaTypeName.get(
185     receiver?.let(transform),
186     parameters.map { it.toBuilder(type = transform(it.type)).build() },
187     transform(returnType)
188   ).copy(nullable = isNullable, annotations = annotations, suspending = isSuspending)
189 }
190 
191 internal interface TypeVariableResolver {
192   val parametersMap: Map<String, TypeVariableName>
getnull193   operator fun get(index: String): TypeVariableName
194 }
195 
196 internal fun List<TypeName>.toTypeVariableResolver(
197   fallback: TypeVariableResolver? = null,
198   sourceType: String? = null,
199 ): TypeVariableResolver {
200   val parametersMap = LinkedHashMap<String, TypeVariableName>()
201   val typeParamResolver = { id: String ->
202     parametersMap[id]
203       ?: fallback?.get(id)
204       ?: throw IllegalStateException("No type argument found for $id! Anaylzing $sourceType")
205   }
206 
207   val resolver = object : TypeVariableResolver {
208     override val parametersMap: Map<String, TypeVariableName> = parametersMap
209 
210     override operator fun get(index: String): TypeVariableName = typeParamResolver(index)
211   }
212 
213   // Fill the parametersMap. Need to do sequentially and allow for referencing previously defined params
214   for (typeVar in this) {
215     check(typeVar is TypeVariableName)
216     // Put the simple typevar in first, then it can be referenced in the full toTypeVariable()
217     // replacement later that may add bounds referencing this.
218     val id = typeVar.name
219     parametersMap[id] = TypeVariableName(id)
220   }
221 
222   for (typeVar in this) {
223     check(typeVar is TypeVariableName)
224     // Now replace it with the full version.
225     parametersMap[typeVar.name] = typeVar.deepCopy(null) { it.stripTypeVarVariance(resolver) }
226   }
227 
228   return resolver
229 }
230