1 /*
<lambda>null2  * Copyright (C) 2024 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.ClassItem
20 import com.android.tools.metalava.model.ClassTypeItem
21 import com.android.tools.metalava.model.MethodItem
22 import com.android.tools.metalava.model.PrimitiveTypeItem
23 import com.android.tools.metalava.model.ReferenceTypeItem
24 import com.android.tools.metalava.model.TypeArgumentTypeItem
25 import com.android.tools.metalava.model.TypeItem
26 import com.android.tools.metalava.model.TypeModifiers
27 import com.android.tools.metalava.model.TypeNullability
28 import com.android.tools.metalava.model.TypeParameterItem
29 import com.android.tools.metalava.model.TypeParameterScope
30 import com.android.tools.metalava.model.VariableTypeItem
31 import com.android.tools.metalava.model.WildcardTypeItem
32 import com.android.tools.metalava.model.type.ContextNullability
33 import com.android.tools.metalava.model.type.DefaultTypeItemFactory
34 import com.android.tools.metalava.model.type.DefaultTypeModifiers
35 import com.intellij.psi.PsiArrayType
36 import com.intellij.psi.PsiClassType
37 import com.intellij.psi.PsiElement
38 import com.intellij.psi.PsiEllipsisType
39 import com.intellij.psi.PsiNameHelper
40 import com.intellij.psi.PsiPrimitiveType
41 import com.intellij.psi.PsiType
42 import com.intellij.psi.PsiTypeParameter
43 import com.intellij.psi.PsiTypes
44 import com.intellij.psi.PsiWildcardType
45 import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType
46 import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
47 import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode
48 import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
49 import org.jetbrains.uast.kotlin.isKotlin
50 
51 /**
52  * Encapsulates a [PsiType] and an optional context [PsiElement] for use with [PsiTypeItemFactory].
53  */
54 data class PsiTypeInfo(val psiType: PsiType, val context: PsiElement? = null)
55 
56 /**
57  * Creates [PsiTypeItem]s from [PsiType]s and an optional context [PsiElement], encapsulated within
58  * [PsiTypeInfo].
59  */
60 internal class PsiTypeItemFactory(
61     val codebase: PsiBasedCodebase,
62     typeParameterScope: TypeParameterScope
63 ) : DefaultTypeItemFactory<PsiTypeInfo, PsiTypeItemFactory>(typeParameterScope) {
64 
65     /** Construct a [PsiTypeItemFactory] suitable for creating types within [classItem]. */
66     fun from(classItem: ClassItem): PsiTypeItemFactory {
67         val scope = TypeParameterScope.from(classItem)
68         return if (scope.isEmpty()) this else PsiTypeItemFactory(codebase, scope)
69     }
70 
71     /** Construct a [PsiTypeItemFactory] suitable for creating types within [methodItem]. */
72     fun from(methodItem: MethodItem): PsiTypeItemFactory {
73         val scope = TypeParameterScope.from(methodItem)
74         return if (scope.isEmpty()) this else PsiTypeItemFactory(codebase, scope)
75     }
76 
77     override fun self() = this
78 
79     override fun createNestedFactory(scope: TypeParameterScope) =
80         PsiTypeItemFactory(codebase, scope)
81 
82     override fun getType(
83         underlyingType: PsiTypeInfo,
84         contextNullability: ContextNullability,
85         // The isVarArg is unused here as that information is encoded in the [PsiType] using the
86         // [PsiEllipsisType] extension of [PsiArrayType].
87         isVarArg: Boolean,
88     ): PsiTypeItem {
89         return getType(underlyingType.psiType, underlyingType.context, contextNullability)
90     }
91 
92     /**
93      * Returns a [PsiTypeItem] representing the [psiType]. The [context] is used to get nullability
94      * information for Kotlin types.
95      */
96     internal fun getType(
97         psiType: PsiType,
98         context: PsiElement? = null,
99         contextNullability: ContextNullability = ContextNullability.none,
100     ): PsiTypeItem {
101         val kotlinTypeInfo =
102             if (context != null && isKotlin(context)) {
103                 KotlinTypeInfo.fromContext(context)
104             } else {
105                 null
106             }
107 
108         // Note: We do *not* cache these; it turns out that storing PsiType instances
109         // in a map is bad for performance; it has a very expensive equals operation
110         // for some type comparisons (and we sometimes end up with unexpected results,
111         // e.g. where we fetch an "equals" type from the map but its representation
112         // is slightly different to what was intended
113         return createTypeItem(psiType, kotlinTypeInfo, contextNullability)
114     }
115 
116     /** Get a [PsiClassTypeItem] to represent the [PsiClassItem]. */
117     fun getClassTypeForClass(psiClassItem: PsiClassItem): PsiClassTypeItem {
118         // Create a PsiType for the class. Specifies `PsiSubstitutor.EMPTY` so that if the class
119         // has any type parameters then the PsiType will include references to those parameters.
120         val psiTypeWithTypeParametersIfAny = codebase.getClassType(psiClassItem.psiClass)
121         // Create a PsiTypeItemFactory that will correctly resolve any references to the class's
122         // type parameters.
123         val classTypeItemFactory = from(psiClassItem)
124         return classTypeItemFactory.createTypeItem(
125             psiTypeWithTypeParametersIfAny,
126             KotlinTypeInfo.fromContext(psiClassItem.psiClass),
127             contextNullability = ContextNullability.forceNonNull,
128             creatingClassTypeForClass = true,
129         ) as PsiClassTypeItem
130     }
131 
132     /** Get a [VariableTypeItem] to represent [PsiTypeParameterItem]. */
133     fun getVariableTypeForTypeParameter(
134         psiTypeParameterItem: PsiTypeParameterItem
135     ): VariableTypeItem {
136         val psiTypeParameter = psiTypeParameterItem.psi()
137         val psiType = codebase.getClassType(psiTypeParameter)
138         return createVariableTypeItem(
139             psiType,
140             null,
141             psiTypeParameterItem,
142             ContextNullability.forceUndefined,
143         )
144     }
145 
146     // PsiTypeItem factory methods
147 
148     /** Creates modifiers based on the annotations of the [type]. */
149     private fun createTypeModifiers(
150         type: PsiType,
151         kotlinType: KotlinTypeInfo?,
152         contextNullability: ContextNullability,
153     ): TypeModifiers {
154         val typeAnnotations = type.annotations.map { PsiAnnotationItem.create(codebase, it) }
155         // Compute the nullability, factoring in any context nullability, kotlin types and
156         // type annotations.
157         val nullability = contextNullability.compute(kotlinType?.nullability(), typeAnnotations)
158         return DefaultTypeModifiers.create(typeAnnotations.toMutableList(), nullability)
159     }
160 
161     /** Create a [PsiTypeItem]. */
162     private fun createTypeItem(
163         psiType: PsiType,
164         kotlinType: KotlinTypeInfo?,
165         contextNullability: ContextNullability = ContextNullability.none,
166         creatingClassTypeForClass: Boolean = false,
167     ): PsiTypeItem {
168         return when (psiType) {
169             is PsiPrimitiveType ->
170                 createPrimitiveTypeItem(
171                     psiType = psiType,
172                     kotlinType = kotlinType,
173                 )
174             is PsiArrayType ->
175                 createArrayTypeItem(
176                     psiType = psiType,
177                     kotlinType = kotlinType,
178                     contextNullability = contextNullability,
179                 )
180             is PsiClassType -> {
181                 val typeParameterItem =
182                     when (val psiClass = psiType.resolve()) {
183                         // If the type resolves to a PsiTypeParameter then the TypeParameterItem
184                         // must exist.
185                         is PsiTypeParameter -> {
186                             val name = psiClass.qualifiedName ?: psiType.name
187                             typeParameterScope.getTypeParameter(name)
188                         }
189                         // If the type could not be resolved then the TypeParameterItem might
190                         // exist.
191                         null ->
192                             psiType.className?.let { name ->
193                                 typeParameterScope.findTypeParameter(name)
194                             }
195                         // Else it is not a TypeParameterItem.
196                         else -> null
197                     }
198 
199                 if (typeParameterItem != null) {
200                     // The type parameters of a class type for the class definition don't have
201                     // defined nullability (their bounds might).
202                     val correctedContextNullability =
203                         if (creatingClassTypeForClass) {
204                             ContextNullability.forceUndefined
205                         } else {
206                             contextNullability
207                         }
208                     createVariableTypeItem(
209                         psiType = psiType,
210                         kotlinType = kotlinType,
211                         typeParameterItem = typeParameterItem,
212                         contextNullability = correctedContextNullability,
213                     )
214                 } else {
215                     if (kotlinType?.ktType is KtFunctionalType) {
216                         createLambdaTypeItem(
217                             psiType = psiType,
218                             kotlinType = kotlinType,
219                             contextNullability = contextNullability,
220                         )
221                     } else {
222                         createClassTypeItem(
223                             psiType = psiType,
224                             kotlinType = kotlinType,
225                             contextNullability = contextNullability,
226                             creatingClassTypeForClass = creatingClassTypeForClass,
227                         )
228                     }
229                 }
230             }
231             is PsiWildcardType ->
232                 createWildcardTypeItem(
233                     psiType = psiType,
234                     kotlinType = kotlinType,
235                 )
236             // There are other [PsiType]s, but none can appear in API surfaces.
237             else ->
238                 throw IllegalStateException(
239                     "Invalid type in API surface: $psiType${
240                     if (kotlinType != null) {
241                         " in file " + kotlinType.context.containingFile.name
242                     } else ""
243                 }"
244                 )
245         }
246     }
247 
248     /** Create a [PsiPrimitiveTypeItem]. */
249     private fun createPrimitiveTypeItem(
250         psiType: PsiPrimitiveType,
251         kotlinType: KotlinTypeInfo?,
252     ) =
253         PsiPrimitiveTypeItem(
254             psiType = psiType,
255             kind = getKind(psiType),
256             modifiers = createTypeModifiers(psiType, kotlinType, ContextNullability.forceNonNull),
257         )
258 
259     /** Get the [PrimitiveTypeItem.Primitive] enum from the [PsiPrimitiveType]. */
260     private fun getKind(type: PsiPrimitiveType): PrimitiveTypeItem.Primitive {
261         return when (type) {
262             PsiTypes.booleanType() -> PrimitiveTypeItem.Primitive.BOOLEAN
263             PsiTypes.byteType() -> PrimitiveTypeItem.Primitive.BYTE
264             PsiTypes.charType() -> PrimitiveTypeItem.Primitive.CHAR
265             PsiTypes.doubleType() -> PrimitiveTypeItem.Primitive.DOUBLE
266             PsiTypes.floatType() -> PrimitiveTypeItem.Primitive.FLOAT
267             PsiTypes.intType() -> PrimitiveTypeItem.Primitive.INT
268             PsiTypes.longType() -> PrimitiveTypeItem.Primitive.LONG
269             PsiTypes.shortType() -> PrimitiveTypeItem.Primitive.SHORT
270             PsiTypes.voidType() -> PrimitiveTypeItem.Primitive.VOID
271             else ->
272                 throw java.lang.IllegalStateException(
273                     "Invalid primitive type in API surface: $type"
274                 )
275         }
276     }
277 
278     /** Create a [PsiArrayTypeItem]. */
279     private fun createArrayTypeItem(
280         psiType: PsiArrayType,
281         kotlinType: KotlinTypeInfo?,
282         contextNullability: ContextNullability,
283     ) =
284         PsiArrayTypeItem(
285             psiType = psiType,
286             componentType =
287                 createTypeItem(
288                     psiType.componentType,
289                     kotlinType?.forArrayComponentType(),
290                     //  Pass in the [ContextNullability.forComponentType] just in case this is the
291                     // return type of an annotation method, or in other words the type of an
292                     // annotation attribute.
293                     contextNullability.forComponentType(),
294                 ),
295             isVarargs = psiType is PsiEllipsisType,
296             modifiers = createTypeModifiers(psiType, kotlinType, contextNullability),
297         )
298 
299     /** Create a [PsiClassTypeItem]. */
300     private fun createClassTypeItem(
301         psiType: PsiClassType,
302         kotlinType: KotlinTypeInfo?,
303         contextNullability: ContextNullability,
304         creatingClassTypeForClass: Boolean = false,
305     ): PsiClassTypeItem {
306         val qualifiedName = psiType.computeQualifiedName()
307         return PsiClassTypeItem(
308             codebase = codebase,
309             psiType = psiType,
310             qualifiedName = qualifiedName,
311             arguments =
312                 computeTypeArguments(
313                     psiType,
314                     kotlinType,
315                     creatingClassTypeForClass,
316                 ),
317             outerClassType =
318                 computeOuterClass(
319                     psiType,
320                     kotlinType,
321                     creatingClassTypeForClass = true,
322                 ),
323             // This should be able to use `psiType.name`, but that sometimes returns null.
324             className = ClassTypeItem.computeClassName(qualifiedName),
325             modifiers = createTypeModifiers(psiType, kotlinType, contextNullability),
326         )
327     }
328 
329     /** Compute the [PsiClassTypeItem.arguments]. */
330     private fun computeTypeArguments(
331         psiType: PsiClassType,
332         kotlinType: KotlinTypeInfo?,
333         creatingClassTypeForClass: Boolean = false,
334     ): List<TypeArgumentTypeItem> {
335         val psiParameters =
336             psiType.parameters.toList().ifEmpty {
337                 // Sometimes, when a PsiClassType's arguments are empty it is not because there
338                 // are no arguments but due to a bug in Psi somewhere. Check to see if the
339                 // kotlin type info has a different set of type arguments and if it has then use
340                 // that to fix the type, otherwise just assume it should be empty.
341                 kotlinType?.ktType?.let { ktType ->
342                     (ktType as? KtNonErrorClassType)?.ownTypeArguments?.ifNotEmpty {
343                         fixUpPsiTypeMissingTypeArguments(psiType, kotlinType)
344                     }
345                 }
346                     ?: emptyList()
347             }
348 
349         return psiParameters.mapIndexed { i, param ->
350             val forTypeArgument = kotlinType?.forTypeArgument(i)
351             createTypeItem(
352                 param,
353                 forTypeArgument,
354                 creatingClassTypeForClass = creatingClassTypeForClass
355             )
356                 as TypeArgumentTypeItem
357         }
358     }
359 
360     /**
361      * Fix up a [PsiClassType] that is missing type arguments.
362      *
363      * This seems to happen in a very limited situation. The example that currently fails, but there
364      * may be more, appears to be due to an impedance mismatch between Kotlin collections and Java
365      * collections.
366      *
367      * Assume the following Kotlin and Java classes from the standard libraries:
368      * ```
369      * package kotlin.collections
370      * public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
371      *     ...
372      *     public fun addAll(elements: Collection<E>): Boolean
373      *     public fun containsAll(elements: Collection<E>): Boolean
374      *     public fun removeAll(elements: Collection<E>): Boolean
375      *     public fun retainAll(elements: Collection<E>): Boolean
376      *     ...
377      * }
378      *
379      * package java.util;
380      * public interface Collection<E> extends Iterable<E> {
381      *     boolean addAll(Collection<? extends E> c);
382      *     boolean containsAll(Collection<?> c);
383      *     boolean removeAll(Collection<?> c);
384      *     boolean retainAll(Collection<?> c);
385      * }
386      * ```
387      *
388      * The given the following class this function is called for the types of the parameters of the
389      * `removeAll`, `retainAll` and `containsAll` methods but not for the `addAll` method.
390      *
391      * ```
392      * abstract class Foo<Z> : MutableCollection<Z> {
393      *     override fun addAll(elements: Collection<Z>): Boolean = true
394      *     override fun containsAll(elements: Collection<Z>): Boolean = true
395      *     override fun removeAll(elements: Collection<Z>): Boolean = true
396      *     override fun retainAll(elements: Collection<Z>): Boolean = true
397      * }
398      * ```
399      *
400      * Metalava and/or the underlying Psi model, appears to treat the `MutableCollection` in `Foo`
401      * as if it was a `java.util.Collection`, even though it is referring to
402      * `kotlin.collections.Collection`. So, both `Foo` and `MutableCollection` are reported as
403      * extending `java.util.Collection`.
404      *
405      * So, you have the following two methods (mapped into Java classes):
406      *
407      * From `java.util.Collection` itself:
408      * ```
409      *      boolean containsAll(java.util.Collection<?> c);
410      * ```
411      *
412      * And from `kotlin.collections.MutableCollection`:
413      * ```
414      *     public fun containsAll(elements: java.util.Collection<E>): Boolean
415      * ```
416      *
417      * But, strictly speaking that is not allowed for a couple of reasons:
418      * 1. `java.util.Collection` is not covariant because it is mutable. However,
419      *    `kotlin.collections.Collection` is covariant because it immutable.
420      * 2. `Collection<Z>` is more restrictive than `Collection<?>`. Java will let you try and remove
421      *    a collection of `Number` from a collection of `String` even though it is meaningless.
422      *    Kotlin's approach is more correct but only possible because its `Collection` is immutable.
423      *
424      * The [kotlinType] seems to have handled that issue reasonably well producing a type of
425      * `java.util.Collection<? extends Z>`. Unfortunately, when that is converted to a `PsiType` the
426      * `PsiType` for `Z` does not resolve to a `PsiTypeParameter`.
427      *
428      * The wildcard is correct.
429      */
430     private fun fixUpPsiTypeMissingTypeArguments(
431         psiType: PsiClassType,
432         kotlinType: KotlinTypeInfo
433     ): List<PsiType> {
434         if (kotlinType.analysisSession == null || kotlinType.ktType == null) return emptyList()
435 
436         val ktType = kotlinType.ktType as KtNonErrorClassType
437 
438         // Restrict this fix to the known issue.
439         val className = psiType.className
440         if (className != "Collection") {
441             return emptyList()
442         }
443 
444         // Convert the KtType to PsiType.
445         //
446         // Convert the whole type rather than extracting the type parameters and converting them
447         // separately because the result depends on the parameterized class, i.e.
448         // `java.util.Collection` in this case. Also, type arguments can be wildcards but
449         // wildcards cannot exist on their own. It will probably be relying on undefined
450         // behavior to try and convert a wildcard on their own.
451         val psiTypeFromKotlin =
452             kotlinType.analysisSession.run {
453                 // Use the default mode so that the resulting psiType is
454                 // `java.util.Collection<? extends Z>`.
455                 val mode = KtTypeMappingMode.DEFAULT
456                 ktType.asPsiType(kotlinType.context, false, mode = mode)
457             } as? PsiClassType
458         return psiTypeFromKotlin?.parameters?.toList() ?: emptyList()
459     }
460 
461     /** Compute the [PsiClassTypeItem.outerClassType]. */
462     private fun computeOuterClass(
463         psiType: PsiClassType,
464         kotlinType: KotlinTypeInfo?,
465         creatingClassTypeForClass: Boolean = false,
466     ): PsiClassTypeItem? {
467         // TODO(b/300081840): this drops annotations on the outer class
468         return PsiNameHelper.getOuterClassReference(psiType.canonicalText).let { outerClassName ->
469             // [PsiNameHelper.getOuterClassReference] returns an empty string if there is no
470             // outer class reference. If the type is not an inner type, it returns the package
471             // name (e.g. for "java.lang.String" it returns "java.lang").
472             if (outerClassName == "" || codebase.findPsiPackage(outerClassName) != null) {
473                 null
474             } else {
475                 val psiOuterClassType =
476                     codebase.createPsiType(
477                         outerClassName,
478                         // The context psi element allows variable types to be resolved (with no
479                         // context, they would be interpreted as class types). The [psiContext]
480                         // works in most cases, but is null when creating a type directly from a
481                         // class declaration, so the resolved [psiType] provides context then.
482                         psiType.psiContext ?: psiType.resolve()
483                     )
484                 createTypeItem(
485                     psiOuterClassType,
486                     kotlinType?.forOuterClass(),
487                     // An outer class reference can't be null.
488                     contextNullability = ContextNullability.forceNonNull,
489                     creatingClassTypeForClass = creatingClassTypeForClass,
490                 )
491                     as PsiClassTypeItem
492             }
493         }
494     }
495 
496     /** Support mapping from boxed types back to their primitive type. */
497     private val boxedToPsiPrimitiveType =
498         mapOf(
499             "java.lang.Byte" to PsiTypes.byteType(),
500             "java.lang.Double" to PsiTypes.doubleType(),
501             "java.lang.Float" to PsiTypes.floatType(),
502             "java.lang.Integer" to PsiTypes.intType(),
503             "java.lang.Long" to PsiTypes.longType(),
504             "java.lang.Short" to PsiTypes.shortType(),
505             "java.lang.Boolean" to PsiTypes.booleanType(),
506             // This is not strictly speaking a boxed -> unboxed mapping, but it fits in nicely
507             // with the others.
508             "kotlin.Unit" to PsiTypes.voidType(),
509         )
510 
511     /** If the type item is not nullable and is a boxed type then map it to the unboxed type. */
512     private fun unboxTypeWherePossible(typeItem: TypeItem): TypeItem {
513         if (
514             typeItem is ClassTypeItem && typeItem.modifiers.nullability() == TypeNullability.NONNULL
515         ) {
516             boxedToPsiPrimitiveType[typeItem.qualifiedName]?.let { psiPrimitiveType ->
517                 return createPrimitiveTypeItem(psiPrimitiveType, null)
518             }
519         }
520         return typeItem
521     }
522 
523     /** An input parameter of type X is represented as a "? super X" in the `Function<X>` class. */
524     private fun unwrapInputType(typeItem: TypeItem): TypeItem {
525         return unboxTypeWherePossible((typeItem as? WildcardTypeItem)?.superBound ?: typeItem)
526     }
527 
528     /**
529      * The return type of type X can be represented as a "? extends X" in the `Function<X>` class.
530      */
531     private fun unwrapOutputType(typeItem: TypeItem): TypeItem {
532         return unboxTypeWherePossible((typeItem as? WildcardTypeItem)?.extendsBound ?: typeItem)
533     }
534 
535     /**
536      * Create a [PsiLambdaTypeItem].
537      *
538      * Extends a [PsiClassTypeItem] and then deconstructs the type arguments of Kotlin `Function<N>`
539      * to extract the receiver, input and output types. This makes heavy use of the
540      * [KotlinTypeInfo.ktType] property of [kotlinType] which must be a [KtFunctionalType]. That has
541      * the information necessary to determine which of the Kotlin `Function<N>` class's type
542      * arguments are the receiver (if any) and which are input parameters. The last type argument is
543      * always the return type.
544      */
545     private fun createLambdaTypeItem(
546         psiType: PsiClassType,
547         kotlinType: KotlinTypeInfo,
548         contextNullability: ContextNullability,
549     ): PsiLambdaTypeItem {
550         val qualifiedName = psiType.computeQualifiedName()
551 
552         val ktType = kotlinType.ktType as KtFunctionalType
553 
554         val isSuspend = ktType.isSuspend
555 
556         val actualKotlinType =
557             kotlinType.copy(
558                 overrideTypeArguments =
559                     // Compute a set of [KtType]s corresponding to the type arguments in the
560                     // underlying `kotlin.jvm.functions.Function*`.
561                     buildList {
562                         // The optional lambda receiver is the first type argument.
563                         ktType.receiverType?.let { add(kotlinType.copy(ktType = it)) }
564                         // The lambda's explicit parameters appear next.
565                         ktType.parameterTypes.mapTo(this) { kotlinType.copy(ktType = it) }
566                         // A `suspend` lambda is transformed by Kotlin in the same way that a
567                         // `suspend` function is, i.e. an additional continuation parameter is added
568                         // at the end of the explicit parameters that encapsulates the return type
569                         // and the return type is changed to `Any?`.
570                         if (isSuspend) {
571                             // Create a KotlinTypeInfo for the continuation parameter that
572                             // encapsulates the actual return type.
573                             add(kotlinType.forSyntheticContinuationParameter(ktType.returnType))
574                             // Add the `Any?` for the return type.
575                             add(kotlinType.nullableAny())
576                         } else {
577                             // As it is not a `suspend` lambda add the return type last.
578                             add(kotlinType.copy(ktType = ktType.returnType))
579                         }
580                     }
581             )
582 
583         // Get the type arguments for the kotlin.jvm.functions.Function<X> class.
584         val typeArguments = computeTypeArguments(psiType, actualKotlinType)
585 
586         // If the function has a receiver then it is the first type argument.
587         var firstParameterTypeIndex = 0
588         val receiverType =
589             if (ktType.hasReceiver) {
590                 // The first parameter type is now the second type argument.
591                 firstParameterTypeIndex = 1
592                 unwrapInputType(typeArguments[0])
593             } else {
594                 null
595             }
596 
597         // The last type argument is always the return type.
598         val returnType = unwrapOutputType(typeArguments.last())
599         val lastParameterTypeIndex = typeArguments.size - 1
600 
601         // Get the parameter types, excluding the optional receiver and the return type.
602         val parameterTypes =
603             typeArguments
604                 .subList(firstParameterTypeIndex, lastParameterTypeIndex)
605                 .map { unwrapInputType(it) }
606                 .toList()
607 
608         return PsiLambdaTypeItem(
609             codebase = codebase,
610             psiType = psiType,
611             qualifiedName = qualifiedName,
612             arguments = typeArguments,
613             outerClassType = computeOuterClass(psiType, actualKotlinType),
614             // This should be able to use `psiType.name`, but that sometimes returns null.
615             className = ClassTypeItem.computeClassName(qualifiedName),
616             modifiers = createTypeModifiers(psiType, actualKotlinType, contextNullability),
617             isSuspend = isSuspend,
618             receiverType = receiverType,
619             parameterTypes = parameterTypes,
620             returnType = returnType,
621         )
622     }
623 
624     /** Create a [PsiVariableTypeItem]. */
625     private fun createVariableTypeItem(
626         psiType: PsiClassType,
627         kotlinType: KotlinTypeInfo?,
628         typeParameterItem: TypeParameterItem,
629         contextNullability: ContextNullability,
630     ) =
631         PsiVariableTypeItem(
632             psiType = psiType,
633             modifiers = createTypeModifiers(psiType, kotlinType, contextNullability),
634             asTypeParameter = typeParameterItem,
635         )
636 
637     /** Create a [PsiWildcardTypeItem]. */
638     private fun createWildcardTypeItem(
639         psiType: PsiWildcardType,
640         kotlinType: KotlinTypeInfo?,
641     ) =
642         PsiWildcardTypeItem(
643             psiType = psiType,
644             extendsBound =
645                 createBound(
646                     psiType.extendsBound,
647                     // The kotlinType only applies to an explicit bound, not an implicit bound, so
648                     // only pass it through if this has an explicit `extends` bound.
649                     kotlinType.takeIf { psiType.isExtends },
650                     // If this is a Kotlin wildcard type with an implicit Object extends bound, the
651                     // Object bound should be nullable, not platform nullness like in Java.
652                     contextNullability =
653                         if (kotlinType != null && !psiType.isExtends) {
654                             ContextNullability(TypeNullability.NULLABLE)
655                         } else {
656                             ContextNullability.none
657                         }
658                 ),
659             superBound =
660                 createBound(
661                     psiType.superBound,
662                     // The kotlinType only applies to an explicit bound, not an implicit bound, so
663                     // only pass it through if this has an explicit `super` bound.
664                     kotlinType.takeIf { psiType.isSuper },
665                 ),
666             modifiers = createTypeModifiers(psiType, kotlinType, ContextNullability.forceUndefined),
667         )
668 
669     /**
670      * Create a [PsiWildcardTypeItem.extendsBound] or [PsiWildcardTypeItem.superBound].
671      *
672      * If a [PsiWildcardType] doesn't have a bound, the bound is represented as the null [PsiType]
673      * instead of just `null`.
674      */
675     private fun createBound(
676         bound: PsiType,
677         kotlinType: KotlinTypeInfo?,
678         contextNullability: ContextNullability = ContextNullability.none
679     ): ReferenceTypeItem? {
680         return if (bound == PsiTypes.nullType()) {
681             null
682         } else {
683             // Use the same Kotlin type, because the wildcard isn't its own level in the KtType.
684             createTypeItem(bound, kotlinType, contextNullability) as ReferenceTypeItem
685         }
686     }
687 }
688 
689 /** Compute the qualified name for a [PsiClassType].. */
computeQualifiedNamenull690 internal fun PsiClassType.computeQualifiedName(): String {
691     // It should be possible to do `psiType.rawType().canonicalText` instead, but this does not
692     // always work if psi is unable to resolve the reference.
693     // See https://youtrack.jetbrains.com/issue/KTIJ-27093 for more details.
694     return PsiNameHelper.getQualifiedClassName(canonicalText, true)
695 }
696