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