1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
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  *      http://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 
17 package com.android.tools.metalava.model.psi
18 
19 import com.android.tools.metalava.model.TypeNullability
20 import com.intellij.psi.PsiElement
21 import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
22 import org.jetbrains.kotlin.analysis.api.analyze
23 import org.jetbrains.kotlin.analysis.api.components.buildClassType
24 import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
25 import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
26 import org.jetbrains.kotlin.analysis.api.types.KtType
27 import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
28 import org.jetbrains.kotlin.analysis.api.types.KtTypeParameterType
29 import org.jetbrains.kotlin.psi.KtCallableDeclaration
30 import org.jetbrains.kotlin.psi.KtClass
31 import org.jetbrains.kotlin.psi.KtElement
32 import org.jetbrains.kotlin.psi.KtFunction
33 import org.jetbrains.kotlin.psi.KtParameter
34 import org.jetbrains.kotlin.psi.KtProperty
35 import org.jetbrains.kotlin.psi.KtPropertyAccessor
36 import org.jetbrains.kotlin.psi.KtTypeReference
37 import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
38 import org.jetbrains.uast.UElement
39 import org.jetbrains.uast.UField
40 import org.jetbrains.uast.UMethod
41 import org.jetbrains.uast.UParameter
42 import org.jetbrains.uast.getContainingUMethod
43 
44 /**
45  * A wrapper for a [KtType] and the [KtAnalysisSession] needed to analyze it and the [PsiElement]
46  * that is the use site.
47  */
48 internal class KotlinTypeInfo
49 private constructor(
50     val analysisSession: KtAnalysisSession?,
51     ktType: KtType?,
52     val context: PsiElement,
53     /**
54      * Override list of type arguments that should have been, but for some reason could not be,
55      * encapsulated within [ktType].
56      */
57     val overrideTypeArguments: List<KotlinTypeInfo>? = null,
58 ) {
59     constructor(context: PsiElement) : this(null, null, context)
60 
61     /** Make sure that any typealiases are fully expanded. */
62     val ktType =
63         analysisSession?.run { ktType?.fullyExpandedType }
64             ?: ktType?.let {
65                 error("cannot have non-null ktType ($ktType) with a null analysisSession")
66             }
67 
68     override fun toString(): String {
69         return "KotlinTypeInfo($ktType for $context)"
70     }
71 
72     fun copy(
73         ktType: KtType? = this.ktType,
74         overrideTypeArguments: List<KotlinTypeInfo>? = this.overrideTypeArguments,
75     ) = KotlinTypeInfo(analysisSession, ktType, context, overrideTypeArguments)
76 
77     /**
78      * Finds the nullability of the [ktType]. If there is no [analysisSession] or [ktType], defaults
79      * to `null` to allow for other sources, like annotations and inferred nullability to take
80      * effect.
81      */
82     fun nullability(): TypeNullability? {
83         return if (analysisSession != null && ktType != null) {
84             analysisSession.run {
85                 if (analysisSession.isInheritedGenericType(ktType)) {
86                     TypeNullability.UNDEFINED
87                 } else if (ktType.nullability == KtTypeNullability.NULLABLE) {
88                     TypeNullability.NULLABLE
89                 } else if (ktType.nullability == KtTypeNullability.NON_NULLABLE) {
90                     TypeNullability.NONNULL
91                 } else {
92                     // No nullability information, possibly a propagated platform type.
93                     null
94                 }
95             }
96         } else {
97             null
98         }
99     }
100 
101     /**
102      * Creates [KotlinTypeInfo] for the component type of this [ktType], assuming it is an array.
103      */
104     fun forArrayComponentType(): KotlinTypeInfo {
105         return KotlinTypeInfo(
106             analysisSession,
107             analysisSession?.run { ktType?.getArrayElementType() },
108             context,
109         )
110     }
111 
112     /**
113      * Creates [KotlinTypeInfo] for the type argument at [index] of this [KotlinTypeInfo], assuming
114      * it is a class type.
115      */
116     fun forTypeArgument(index: Int): KotlinTypeInfo {
117         overrideTypeArguments?.getOrNull(index)?.let {
118             return it
119         }
120         return KotlinTypeInfo(
121             analysisSession,
122             analysisSession?.run {
123                 when (ktType) {
124                     is KtNonErrorClassType -> ktType.ownTypeArguments.getOrNull(index)?.type
125                     else -> null
126                 }
127             },
128             context,
129         )
130     }
131 
132     /**
133      * Creates [KotlinTypeInfo] for the outer class type of this [ktType], assuming it is a class.
134      */
135     fun forOuterClass(): KotlinTypeInfo {
136         return KotlinTypeInfo(
137             analysisSession,
138             analysisSession?.run {
139                 (ktType as? KtNonErrorClassType)?.classId?.outerClassId?.let { outerClassId ->
140                     buildClassType(outerClassId) {
141                         // Add the parameters of the class type with nullability information.
142                         ktType.qualifiers
143                             .firstOrNull { it.name == outerClassId.shortClassName }
144                             ?.typeArguments
145                             ?.forEach { argument(it) }
146                     }
147                 }
148             },
149             context,
150         )
151     }
152 
153     /** Get a [KotlinTypeInfo] that represents a suspend function's `Continuation` parameter. */
154     fun forSyntheticContinuationParameter(returnType: KtType): KotlinTypeInfo {
155         // This cast is safe as this will only be called for a lambda function whose context will
156         // be [KtFunction].
157         val ktElement = context as KtElement
158         return analyze(ktElement) { syntheticContinuationParameter(context, returnType) }
159     }
160 
161     /** Get a [KotlinTypeInfo] that represents `Any?`. */
162     fun nullableAny(): KotlinTypeInfo {
163         // This cast is safe as this will only be called for a lambda function whose context will
164         // be [KtFunction].
165         val ktElement = context as KtElement
166         return analyze(ktElement) { KotlinTypeInfo(this, builtinTypes.NULLABLE_ANY, context) }
167     }
168 
169     companion object {
170         /**
171          * Creates a [KotlinTypeInfo] instance from the given [context], with null values if the
172          * [KtType] for the [context] can't be resolved.
173          */
174         fun fromContext(context: PsiElement): KotlinTypeInfo {
175             return if (context is KtElement) {
176                 fromKtElement(context, context)
177             } else {
178                 when (val sourcePsi = (context as? UElement)?.sourcePsi) {
179                     is KtElement -> fromKtElement(sourcePsi, context)
180                     else -> {
181                         typeFromSyntheticElement(context)
182                     }
183                 }
184             }
185                 ?: KotlinTypeInfo(context)
186         }
187 
188         /**
189          * Try and compute [KotlinTypeInfo] from a [KtElement].
190          *
191          * Multiple different [PsiElement] subclasses can be generated from the same [KtElement] and
192          * require different views of its types. The [context] is provided to differentiate between
193          * them.
194          */
195         private fun fromKtElement(ktElement: KtElement, context: PsiElement): KotlinTypeInfo? =
196             when (ktElement) {
197                 is KtProperty -> {
198                     analyze(ktElement) {
199                         val ktType =
200                             when {
201                                 // If the context is the backing field then use the type of the
202                                 // delegate, if any.
203                                 context is UField -> ktElement.delegateExpression?.getKtType()
204                                 else -> null
205                             }
206                                 ?: ktElement.getReturnKtType()
207                         KotlinTypeInfo(this, ktType, ktElement)
208                     }
209                 }
210                 is KtCallableDeclaration -> {
211                     analyze(ktElement) {
212                         val ktType =
213                             if (ktElement is KtFunction && ktElement.isSuspend()) {
214                                 // A suspend function is transformed by Kotlin to return Any?
215                                 // instead of its actual return type.
216                                 builtinTypes.NULLABLE_ANY
217                             } else {
218                                 ktElement.getReturnKtType()
219                             }
220                         KotlinTypeInfo(this, ktType, ktElement)
221                     }
222                 }
223                 is KtTypeReference ->
224                     analyze(ktElement) { KotlinTypeInfo(this, ktElement.getKtType(), ktElement) }
225                 is KtPropertyAccessor ->
226                     analyze(ktElement) {
227                         KotlinTypeInfo(this, ktElement.getReturnKtType(), ktElement)
228                     }
229                 is KtClass -> {
230                     analyze(ktElement) {
231                         // If this is a named class or object then return a KotlinTypeInfo for the
232                         // class. If it is generic then the type parameters will be used as the
233                         // type arguments.
234                         (ktElement.getSymbol() as? KtNamedClassOrObjectSymbol)?.let { symbol ->
235                             KotlinTypeInfo(this, symbol.buildSelfClassType(), ktElement)
236                         }
237                     }
238                 }
239                 else -> null
240             }
241 
242         /**
243          * Try and compute the type from a synthetic elements, e.g. a property setter.
244          *
245          * In order to get this far the [context] is either not a [UElement], or it has a null
246          * [UElement.sourcePsi]. That means it is most likely a parameter in a synthetic method
247          * created for use by code that operates on a "Psi" view of the source, i.e. java code. This
248          * method will attempt to reverse engineer the "Kt" -> "Psi" mapping to find the real Kotlin
249          * types.
250          */
251         private fun typeFromSyntheticElement(context: PsiElement): KotlinTypeInfo? {
252             // If this is not a UParameter in a UMethod then it is an unknown synthetic element so
253             // just return.
254             val containingMethod = (context as? UParameter)?.getContainingUMethod() ?: return null
255 
256             // Get the parameter index from the containing methods `uastParameters` as the parameter
257             // is a `UParameter`.
258             val parameterIndex = containingMethod.uastParameters.indexOf(context)
259 
260             return when (val sourcePsi = containingMethod.sourcePsi) {
261                 is KtProperty -> {
262                     // This is the parameter of a synthetic setter, so get its type from the
263                     // containing method.
264                     fromContext(containingMethod)
265                 }
266                 is KtParameter -> {
267                     // The underlying source representation of the synthetic method is a parameter,
268                     // most likely a parameter of the primary constructor. In which case the
269                     // synthetic method is most like a property setter. Whatever it may be, use the
270                     // type of the parameter as it is most likely to be the correct type.
271                     fromKtElement(sourcePsi, context)
272                 }
273                 is KtClass -> {
274                     // The underlying source representation of the synthetic method is a whole
275                     // class.
276                     typeFromKtClass(parameterIndex, containingMethod, sourcePsi)
277                 }
278                 is KtFunction -> {
279                     if (
280                         sourcePsi.isSuspend() &&
281                             parameterIndex == containingMethod.parameters.size - 1
282                     ) {
283                         // Compute the [KotlinTypeInfo] for the suspend function's synthetic
284                         // [kotlin.coroutines.Continuation] parameter.
285                         analyze(sourcePsi) {
286                             val returnKtType = sourcePsi.getReturnKtType()
287                             syntheticContinuationParameter(sourcePsi, returnKtType)
288                         }
289                     } else null
290                 }
291                 is KtPropertyAccessor ->
292                     analyze(sourcePsi) {
293                         // Getters and setters are always the same type as the property so use its
294                         // type.
295                         fromKtElement(sourcePsi.property, context)
296                     }
297                 else -> null
298             }
299         }
300 
301         /** Check if this is a `suspend` function. */
302         private fun KtFunction.isSuspend() = modifierList?.hasSuspendModifier() == true
303 
304         /**
305          * Create a [KotlinTypeInfo] that represents the continuation parameter of a `suspend`
306          * function with [returnKtType].
307          *
308          * Ideally, this would create a [KtNonErrorClassType] for `Continuation<$returnType$>`,
309          * where `$returnType$` is the return type of the Kotlin suspend function but while that
310          * works in K2 it fails in K1 as it cannot resolve the `Continuation` type even though it is
311          * in the Kotlin stdlib which will be on the classpath.
312          *
313          * Fortunately, doing that is not strictly necessary as the [KtType] is only used to
314          * retrieve nullability for the `Continuation` type and its type argument. So, this uses
315          * non-nullable [Any] as the fake type for `Continuation` (as that is always non-nullable)
316          * and stores the suspend function's return type in [KotlinTypeInfo.overrideTypeArguments]
317          * from where it will be retrieved.
318          */
319         internal fun KtAnalysisSession.syntheticContinuationParameter(
320             context: PsiElement,
321             returnKtType: KtType
322         ): KotlinTypeInfo {
323             val returnTypeInfo = KotlinTypeInfo(this, returnKtType, context)
324             val fakeContinuationKtType = builtinTypes.ANY
325             return KotlinTypeInfo(this, fakeContinuationKtType, context, listOf(returnTypeInfo))
326         }
327 
328         /** Try and get the type for [parameterIndex] in [containingMethod] from the [ktClass]. */
329         private fun typeFromKtClass(
330             parameterIndex: Int,
331             containingMethod: UMethod,
332             ktClass: KtClass
333         ) =
334             when {
335                 ktClass.isData() && containingMethod.name == "copy" -> {
336                     // The parameters in the copy constructor correspond to the parameters in the
337                     // primary constructor so find the corresponding parameter in the primary
338                     // constructor and use its type.
339                     ktClass.primaryConstructor?.let { primaryConstructor ->
340                         val ktParameter = primaryConstructor.valueParameters[parameterIndex]
341                         analyze(ktParameter) {
342                             KotlinTypeInfo(
343                                 this,
344                                 ktParameter.getReturnKtType(),
345                                 ktParameter,
346                             )
347                         }
348                     }
349                 }
350                 else -> null
351             }
352 
353         // Mimic `hasInheritedGenericType` in `...uast.kotlin.FirKotlinUastResolveProviderService`
354         fun KtAnalysisSession.isInheritedGenericType(ktType: KtType): Boolean {
355             return ktType is KtTypeParameterType &&
356                 // explicitly nullable, e.g., T?
357                 !ktType.isMarkedNullable &&
358                 // non-null upper bound, e.g., T : Any
359                 ktType.canBeNull
360         }
361     }
362 }
363