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