• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.lint.detector.api.getInternalName
20 import com.android.tools.metalava.compatibility
21 import com.android.tools.metalava.model.AnnotationItem
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.Item
24 import com.android.tools.metalava.model.MemberItem
25 import com.android.tools.metalava.model.MethodItem
26 import com.android.tools.metalava.model.TypeItem
27 import com.android.tools.metalava.model.TypeParameterItem
28 import com.intellij.psi.JavaTokenType
29 import com.intellij.psi.PsiArrayType
30 import com.intellij.psi.PsiCapturedWildcardType
31 import com.intellij.psi.PsiClass
32 import com.intellij.psi.PsiClassType
33 import com.intellij.psi.PsiCompiledElement
34 import com.intellij.psi.PsiDisjunctionType
35 import com.intellij.psi.PsiElement
36 import com.intellij.psi.PsiEllipsisType
37 import com.intellij.psi.PsiIntersectionType
38 import com.intellij.psi.PsiJavaCodeReferenceElement
39 import com.intellij.psi.PsiJavaToken
40 import com.intellij.psi.PsiLambdaExpressionType
41 import com.intellij.psi.PsiPrimitiveType
42 import com.intellij.psi.PsiRecursiveElementVisitor
43 import com.intellij.psi.PsiReferenceList
44 import com.intellij.psi.PsiType
45 import com.intellij.psi.PsiTypeElement
46 import com.intellij.psi.PsiTypeParameter
47 import com.intellij.psi.PsiTypeParameterList
48 import com.intellij.psi.PsiTypeVisitor
49 import com.intellij.psi.PsiWhiteSpace
50 import com.intellij.psi.PsiWildcardType
51 import com.intellij.psi.util.PsiTypesUtil
52 import com.intellij.psi.util.TypeConversionUtil
53 import org.jetbrains.kotlin.asJava.elements.KtLightTypeParameter
54 import java.util.function.Predicate
55 
56 /** Represents a type backed by PSI */
57 class PsiTypeItem private constructor(
58     private val codebase: PsiBasedCodebase,
59     var psiType: PsiType
60 ) : TypeItem {
61     private var toString: String? = null
62     private var toAnnotatedString: String? = null
63     private var toInnerAnnotatedString: String? = null
64     private var toErasedString: String? = null
65     private var asClass: PsiClassItem? = null
66 
toStringnull67     override fun toString(): String {
68         return toTypeString()
69     }
70 
toTypeStringnull71     override fun toTypeString(
72         outerAnnotations: Boolean,
73         innerAnnotations: Boolean,
74         erased: Boolean,
75         kotlinStyleNulls: Boolean,
76         context: Item?,
77         filter: Predicate<Item>?
78     ): String {
79         assert(innerAnnotations || !outerAnnotations) // Can't supply outer=true,inner=false
80 
81         if (filter != null) {
82             // No caching when specifying filter.
83             // TODO: When we support type use annotations, here we need to deal with markRecent
84             //  and clearAnnotations not really having done their job.
85             return toTypeString(
86                 codebase = codebase,
87                 type = psiType,
88                 outerAnnotations = outerAnnotations,
89                 innerAnnotations = innerAnnotations,
90                 erased = erased,
91                 kotlinStyleNulls = kotlinStyleNulls,
92                 context = context,
93                 filter = filter
94             )
95         }
96 
97         return if (erased) {
98             if (kotlinStyleNulls && (innerAnnotations || outerAnnotations)) {
99                 // Not cached: Not common
100                 toTypeString(
101                     codebase = codebase,
102                     type = psiType,
103                     outerAnnotations = outerAnnotations,
104                     innerAnnotations = innerAnnotations,
105                     erased = erased,
106                     kotlinStyleNulls = kotlinStyleNulls,
107                     context = context,
108                     filter = filter
109                 )
110             } else {
111                 if (toErasedString == null) {
112                     toErasedString = toTypeString(
113                         codebase = codebase,
114                         type = psiType,
115                         outerAnnotations = outerAnnotations,
116                         innerAnnotations = innerAnnotations,
117                         erased = erased,
118                         kotlinStyleNulls = kotlinStyleNulls,
119                         context = context,
120                         filter = filter
121                     )
122                 }
123                 toErasedString!!
124             }
125         } else {
126             when {
127                 kotlinStyleNulls && outerAnnotations -> {
128                     if (toAnnotatedString == null) {
129                         toAnnotatedString = toTypeString(
130                             codebase = codebase,
131                             type = psiType,
132                             outerAnnotations = outerAnnotations,
133                             innerAnnotations = innerAnnotations,
134                             erased = erased,
135                             kotlinStyleNulls = kotlinStyleNulls,
136                             context = context,
137                             filter = filter
138                         )
139                     }
140                     toAnnotatedString!!
141                 }
142                 kotlinStyleNulls && innerAnnotations -> {
143                     if (toInnerAnnotatedString == null) {
144                         toInnerAnnotatedString = toTypeString(
145                             codebase = codebase,
146                             type = psiType,
147                             outerAnnotations = outerAnnotations,
148                             innerAnnotations = innerAnnotations,
149                             erased = erased,
150                             kotlinStyleNulls = kotlinStyleNulls,
151                             context = context,
152                             filter = filter
153                         )
154                     }
155                     toInnerAnnotatedString!!
156                 }
157                 else -> {
158                     if (toString == null) {
159                         toString = TypeItem.formatType(
160                             getCanonicalText(
161                                 codebase = codebase,
162                                 owner = context,
163                                 type = psiType,
164                                 annotated = false,
165                                 mapAnnotations = false,
166                                 kotlinStyleNulls = kotlinStyleNulls,
167                                 filter = filter
168                             )
169                         )
170                     }
171                     toString!!
172                 }
173             }
174         }
175     }
176 
toErasedTypeStringnull177     override fun toErasedTypeString(context: Item?): String {
178         return toTypeString(
179             outerAnnotations = false,
180             innerAnnotations = false,
181             erased = true,
182             kotlinStyleNulls = false,
183             context = context
184         )
185     }
186 
arrayDimensionsnull187     override fun arrayDimensions(): Int {
188         return psiType.arrayDimensions
189     }
190 
internalNamenull191     override fun internalName(): String {
192         if (primitive) {
193             val signature = getPrimitiveSignature(toString())
194             if (signature != null) {
195                 return signature
196             }
197         }
198         val sb = StringBuilder()
199         appendJvmSignature(sb, psiType)
200         return sb.toString()
201     }
202 
equalsnull203     override fun equals(other: Any?): Boolean {
204         if (this === other) return true
205 
206         return when (other) {
207             is TypeItem -> TypeItem.equalsWithoutSpace(toTypeString(), other.toTypeString())
208             else -> false
209         }
210     }
211 
asClassnull212     override fun asClass(): PsiClassItem? {
213         if (primitive) {
214             return null
215         }
216         if (asClass == null) {
217             asClass = codebase.findClass(psiType)
218         }
219         return asClass
220     }
221 
asTypeParameternull222     override fun asTypeParameter(context: MemberItem?): TypeParameterItem? {
223         val cls = asClass() ?: return null
224         return cls as? PsiTypeParameterItem
225     }
226 
hashCodenull227     override fun hashCode(): Int {
228         return psiType.hashCode()
229     }
230 
231     override val primitive: Boolean
232         get() = psiType is PsiPrimitiveType
233 
defaultValuenull234     override fun defaultValue(): Any? {
235         return PsiTypesUtil.getDefaultValue(psiType)
236     }
237 
defaultValueStringnull238     override fun defaultValueString(): String {
239         return PsiTypesUtil.getDefaultValueOfType(psiType)
240     }
241 
typeArgumentClassesnull242     override fun typeArgumentClasses(): List<ClassItem> {
243         if (primitive) {
244             return emptyList()
245         }
246 
247         val classes = mutableListOf<ClassItem>()
248         psiType.accept(object : PsiTypeVisitor<PsiType>() {
249             override fun visitType(type: PsiType?): PsiType? {
250                 return type
251             }
252 
253             override fun visitClassType(classType: PsiClassType): PsiType? {
254                 codebase.findClass(classType)?.let {
255                     if (!it.isTypeParameter && !classes.contains(it)) {
256                         classes.add(it)
257                     }
258                 }
259                 for (type in classType.parameters) {
260                     type.accept(this)
261                 }
262                 return classType
263             }
264 
265             override fun visitWildcardType(wildcardType: PsiWildcardType): PsiType? {
266                 if (wildcardType.isExtends) {
267                     wildcardType.extendsBound.accept(this)
268                 }
269                 if (wildcardType.isSuper) {
270                     wildcardType.superBound.accept(this)
271                 }
272                 if (wildcardType.isBounded) {
273                     wildcardType.bound?.accept(this)
274                 }
275                 return wildcardType
276             }
277 
278             override fun visitPrimitiveType(primitiveType: PsiPrimitiveType): PsiType? {
279                 return primitiveType
280             }
281 
282             override fun visitEllipsisType(ellipsisType: PsiEllipsisType): PsiType? {
283                 ellipsisType.componentType.accept(this)
284                 return ellipsisType
285             }
286 
287             override fun visitArrayType(arrayType: PsiArrayType): PsiType? {
288                 arrayType.componentType.accept(this)
289                 return arrayType
290             }
291 
292             override fun visitLambdaExpressionType(lambdaExpressionType: PsiLambdaExpressionType): PsiType? {
293                 for (superType in lambdaExpressionType.superTypes) {
294                     superType.accept(this)
295                 }
296                 return lambdaExpressionType
297             }
298 
299             override fun visitCapturedWildcardType(capturedWildcardType: PsiCapturedWildcardType): PsiType? {
300                 capturedWildcardType.upperBound.accept(this)
301                 return capturedWildcardType
302             }
303 
304             override fun visitDisjunctionType(disjunctionType: PsiDisjunctionType): PsiType? {
305                 for (type in disjunctionType.disjunctions) {
306                     type.accept(this)
307                 }
308                 return disjunctionType
309             }
310 
311             override fun visitIntersectionType(intersectionType: PsiIntersectionType): PsiType? {
312                 for (type in intersectionType.conjuncts) {
313                     type.accept(this)
314                 }
315                 return intersectionType
316             }
317         })
318 
319         return classes
320     }
321 
convertTypenull322     override fun convertType(replacementMap: Map<String, String>?, owner: Item?): TypeItem {
323         val s = convertTypeString(replacementMap)
324         return create(codebase, codebase.createPsiType(s, owner?.psi()))
325     }
326 
hasTypeArgumentsnull327     override fun hasTypeArguments(): Boolean {
328         val type = psiType
329         return type is PsiClassType && type.hasParameters()
330     }
331 
markRecentnull332     override fun markRecent() {
333         val source = toTypeString(outerAnnotations = true, innerAnnotations = true)
334             .replace(".NonNull", ".RecentlyNonNull")
335         // TODO: Pass in a context!
336         psiType = codebase.createPsiType(source)
337         toAnnotatedString = null
338         toInnerAnnotatedString = null
339     }
340 
scrubAnnotationsnull341     override fun scrubAnnotations() {
342         toAnnotatedString = toTypeString(outerAnnotations = false, innerAnnotations = false)
343         toInnerAnnotatedString = toAnnotatedString
344     }
345 
346     companion object {
getPrimitiveSignaturenull347         private fun getPrimitiveSignature(typeName: String): String? = when (typeName) {
348             "boolean" -> "Z"
349             "byte" -> "B"
350             "char" -> "C"
351             "short" -> "S"
352             "int" -> "I"
353             "long" -> "J"
354             "float" -> "F"
355             "double" -> "D"
356             "void" -> "V"
357             else -> null
358         }
359 
appendJvmSignaturenull360         private fun appendJvmSignature(
361             buffer: StringBuilder,
362             type: PsiType?
363         ): Boolean {
364             if (type == null) {
365                 return false
366             }
367 
368             val psiType = TypeConversionUtil.erasure(type)
369 
370             when (psiType) {
371                 is PsiArrayType -> {
372                     buffer.append('[')
373                     appendJvmSignature(buffer, psiType.componentType)
374                 }
375                 is PsiClassType -> {
376                     val resolved = psiType.resolve() ?: return false
377                     if (!appendJvmTypeName(buffer, resolved)) {
378                         return false
379                     }
380                 }
381                 is PsiPrimitiveType -> buffer.append(getPrimitiveSignature(psiType.canonicalText))
382                 else -> return false
383             }
384             return true
385         }
386 
appendJvmTypeNamenull387         private fun appendJvmTypeName(
388             signature: StringBuilder,
389             outerClass: PsiClass
390         ): Boolean {
391             val className = getInternalName(outerClass) ?: return false
392             signature.append('L').append(className).append(';')
393             return true
394         }
395 
toTypeStringnull396         fun toTypeString(
397             codebase: PsiBasedCodebase,
398             type: PsiType,
399             outerAnnotations: Boolean,
400             innerAnnotations: Boolean,
401             erased: Boolean,
402             kotlinStyleNulls: Boolean,
403             context: Item?,
404             filter: Predicate<Item>?
405         ): String {
406             if (erased) {
407                 // Recurse with raw type and erase=false
408                 return toTypeString(
409                     codebase,
410                     TypeConversionUtil.erasure(type),
411                     outerAnnotations,
412                     innerAnnotations,
413                     false,
414                     kotlinStyleNulls,
415                     context,
416                     filter
417                 )
418             }
419 
420             val typeString =
421                 if (kotlinStyleNulls && (innerAnnotations || outerAnnotations)) {
422                     try {
423                         getCanonicalText(codebase, context, type, true, true, kotlinStyleNulls, filter)
424                     } catch (ignore: Throwable) {
425                         type.canonicalText
426                     }
427                 } else {
428                     type.canonicalText
429                 }
430 
431             return TypeItem.formatType(typeString)
432         }
433 
getCanonicalTextnull434         private fun getCanonicalText(
435             codebase: PsiBasedCodebase,
436             owner: Item?,
437             type: PsiType,
438             annotated: Boolean,
439             mapAnnotations: Boolean,
440             kotlinStyleNulls: Boolean,
441             filter: Predicate<Item>?
442         ): String {
443             return try {
444                 if (annotated && kotlinStyleNulls) {
445                     // Any nullness annotations on the element to merge in? When we have something like
446                     //  @Nullable String foo
447                     // the Nullable annotation can be on the element itself rather than the type,
448                     // so if we print the type without knowing the nullness annotation on the
449                     // element, we'll think it's unannotated and we'll display it as "String!".
450                     val nullness = owner?.modifiers?.annotations()?.firstOrNull { it.isNullnessAnnotation() }
451                     var elementAnnotations = if (nullness != null) { listOf(nullness) } else null
452 
453                     val implicitNullness = if (owner != null) AnnotationItem.getImplicitNullness(owner) else null
454                     val annotatedType = if (implicitNullness != null) {
455                         val provider = if (implicitNullness == true) {
456                             codebase.getNullableAnnotationProvider()
457                         } else {
458                             codebase.getNonNullAnnotationProvider()
459                         }
460 
461                         if (implicitNullness == false &&
462                             owner is MethodItem &&
463                             owner.containingClass().isAnnotationType() &&
464                             type is PsiArrayType
465                         ) {
466                             // For arrays in annotations not only is the method itself non null but so
467                             // is the component type
468                             type.componentType.annotate(provider).createArrayType().annotate(provider)
469                         } else {
470                             type.annotate(provider)
471                         }
472                     } else if (nullness != null && owner.modifiers.isVarArg() && owner.isKotlin() && type is PsiEllipsisType) {
473                         // Varargs the annotation applies to the component type instead
474                         val nonNullProvider = codebase.getNonNullAnnotationProvider()
475                         val provider = if (nullness.isNonNull()) {
476                             nonNullProvider
477                         } else codebase.getNullableAnnotationProvider()
478                         val componentType = type.componentType.annotate(provider)
479                         elementAnnotations = null
480                         PsiEllipsisType(componentType, nonNullProvider)
481                     } else {
482                         type
483                     }
484                     val printer = PsiTypePrinter(codebase, filter, mapAnnotations, kotlinStyleNulls)
485 
486                     printer.getAnnotatedCanonicalText(
487                         annotatedType,
488                         elementAnnotations
489                     )
490                 } else if (annotated) {
491                     type.getCanonicalText(true)
492                 } else {
493                     type.getCanonicalText(false)
494                 }
495             } catch (e: Throwable) {
496                 return type.getCanonicalText(false)
497             }
498         }
499 
createnull500         fun create(codebase: PsiBasedCodebase, psiType: PsiType): PsiTypeItem {
501             return PsiTypeItem(codebase, psiType)
502         }
503 
createnull504         fun create(codebase: PsiBasedCodebase, original: PsiTypeItem): PsiTypeItem {
505             return PsiTypeItem(codebase, original.psiType)
506         }
507 
typeParameterListnull508         fun typeParameterList(typeList: PsiTypeParameterList?): String? {
509             if (typeList != null && typeList.typeParameters.isNotEmpty()) {
510                 // TODO: Filter the type list classes? Try to construct a typelist of a private API!
511                 // We can't just use typeList.text here, because that just
512                 // uses the declaration from the source, which may not be
513                 // fully qualified - e.g. we might get
514                 //    <T extends View> instead of <T extends android.view.View>
515                 // Therefore, we'll need to compute it ourselves; I can't find
516                 // a utility for this
517                 val sb = StringBuilder()
518                 typeList.accept(object : PsiRecursiveElementVisitor() {
519                     override fun visitElement(element: PsiElement) {
520                         if (element is PsiTypeParameterList) {
521                             val typeParameters = element.typeParameters
522                             if (typeParameters.isEmpty()) {
523                                 return
524                             }
525                             sb.append("<")
526                             var first = true
527                             for (parameter in typeParameters) {
528                                 if (!first) {
529                                     sb.append(", ")
530                                 }
531                                 first = false
532                                 visitElement(parameter)
533                             }
534                             sb.append(">")
535                             return
536                         } else if (element is PsiTypeParameter) {
537                             if (element is KtLightTypeParameter && element.kotlinOrigin.text.startsWith("reified")) {
538                                 sb.append("reified ")
539                             }
540                             sb.append(element.name)
541                             // TODO: How do I get super -- e.g. "Comparable<? super T>"
542                             val extendsList = element.extendsList
543                             val refList = extendsList.referenceElements
544                             if (refList.isNotEmpty()) {
545                                 sb.append(" extends ")
546                                 var first = true
547                                 for (refElement in refList) {
548                                     if (!first) {
549                                         sb.append(" & ")
550                                     } else {
551                                         first = false
552                                     }
553 
554                                     if (refElement is PsiJavaCodeReferenceElement) {
555                                         visitElement(refElement)
556                                         continue
557                                     }
558                                     val resolved = refElement.resolve()
559                                     if (resolved is PsiClass) {
560                                         sb.append(resolved.qualifiedName ?: resolved.name)
561                                         resolved.typeParameterList?.accept(this)
562                                     } else {
563                                         sb.append(refElement.referenceName)
564                                     }
565                                 }
566                             } else {
567                                 val extendsListTypes = element.extendsListTypes
568                                 if (extendsListTypes.isNotEmpty()) {
569                                     sb.append(" extends ")
570                                     var first = true
571                                     for (type in extendsListTypes) {
572                                         if (!first) {
573                                             sb.append(" & ")
574                                         } else {
575                                             first = false
576                                         }
577                                         val resolved = type.resolve()
578                                         if (resolved == null) {
579                                             sb.append(type.className)
580                                         } else {
581                                             sb.append(resolved.qualifiedName ?: resolved.name)
582                                             resolved.typeParameterList?.accept(this)
583                                         }
584                                     }
585                                 }
586                             }
587                             return
588                         } else if (element is PsiJavaCodeReferenceElement) {
589                             val resolved = element.resolve()
590                             if (resolved is PsiClass) {
591                                 if (resolved.qualifiedName == null) {
592                                     sb.append(resolved.name)
593                                 } else {
594                                     sb.append(resolved.qualifiedName)
595                                 }
596                                 val typeParameters = element.parameterList
597                                 if (typeParameters != null) {
598                                     val typeParameterElements = typeParameters.typeParameterElements
599                                     if (typeParameterElements.isEmpty()) {
600                                         return
601                                     }
602 
603                                     // When reading in this from bytecode, the order is sometimes wrong
604                                     // (for example, for
605                                     //    public interface BaseStream<T, S extends BaseStream<T, S>>
606                                     // the extends type BaseStream<T, S> will return the typeParameterElements
607                                     // as [S,T] instead of [T,S]. However, the typeParameters.typeArguments
608                                     // list is correct, so order the elements by the typeArguments array instead
609 
610                                     // Special case: just one type argument: no sorting issue
611                                     if (typeParameterElements.size == 1) {
612                                         sb.append("<")
613                                         var first = true
614                                         for (parameter in typeParameterElements) {
615                                             if (!first) {
616                                                 sb.append(", ")
617                                             }
618                                             first = false
619                                             visitElement(parameter)
620                                         }
621                                         sb.append(">")
622                                         return
623                                     }
624 
625                                     // More than one type argument
626 
627                                     val typeArguments = typeParameters.typeArguments
628                                     if (typeArguments.isNotEmpty()) {
629                                         sb.append("<")
630                                         var first = true
631                                         for (parameter in typeArguments) {
632                                             if (!first) {
633                                                 sb.append(", ")
634                                             }
635                                             first = false
636                                             // Try to match up a type parameter element
637                                             var found = false
638                                             for (typeElement in typeParameterElements) {
639                                                 if (parameter == typeElement.type) {
640                                                     found = true
641                                                     visitElement(typeElement)
642                                                     break
643                                                 }
644                                             }
645                                             if (!found) {
646                                                 // No type element matched: use type instead
647                                                 val classType = PsiTypesUtil.getPsiClass(parameter)
648                                                 if (classType != null) {
649                                                     visitElement(classType)
650                                                 } else {
651                                                     sb.append(parameter.canonicalText)
652                                                 }
653                                             }
654                                         }
655                                         sb.append(">")
656                                     }
657                                 }
658                                 return
659                             }
660                         } else if (element is PsiTypeElement) {
661                             val type = element.type
662                             if (type is PsiWildcardType) {
663                                 sb.append("?")
664                                 if (type.isBounded) {
665                                     if (type.isExtends) {
666                                         sb.append(" extends ")
667                                         sb.append(type.extendsBound.canonicalText)
668                                     }
669                                     if (type.isSuper) {
670                                         sb.append(" super ")
671                                         sb.append(type.superBound.canonicalText)
672                                     }
673                                 }
674                                 return
675                             }
676                             sb.append(type.canonicalText)
677                             return
678                         } else if (element is PsiJavaToken && element.tokenType == JavaTokenType.COMMA) {
679                             sb.append(",")
680                             if (compatibility.spaceAfterCommaInTypes) {
681                                 if (element.nextSibling == null || element.nextSibling !is PsiWhiteSpace) {
682                                     sb.append(" ")
683                                 }
684                             }
685                             return
686                         }
687                         if (element.firstChild == null) { // leaf nodes only
688                             if (element is PsiCompiledElement) {
689                                 if (element is PsiReferenceList) {
690                                     val referencedTypes = element.referencedTypes
691                                     var first = true
692                                     for (referenceType in referencedTypes) {
693                                         if (first) {
694                                             first = false
695                                         } else {
696                                             sb.append(", ")
697                                         }
698                                         sb.append(referenceType.canonicalText)
699                                     }
700                                 }
701                             } else {
702                                 sb.append(element.text)
703                             }
704                         }
705                         super.visitElement(element)
706                     }
707                 })
708 
709                 val typeString = sb.toString()
710                 return TypeItem.cleanupGenerics(typeString)
711             }
712 
713             return null
714         }
715 
typeParameterClassesnull716         fun typeParameterClasses(codebase: PsiBasedCodebase, typeList: PsiTypeParameterList?): List<ClassItem> {
717             if (typeList != null && typeList.typeParameters.isNotEmpty()) {
718                 val list = mutableListOf<ClassItem>()
719                 typeList.accept(object : PsiRecursiveElementVisitor() {
720                     override fun visitElement(element: PsiElement) {
721                         if (element is PsiTypeParameterList) {
722                             val typeParameters = element.typeParameters
723                             for (parameter in typeParameters) {
724                                 visitElement(parameter)
725                             }
726                             return
727                         } else if (element is PsiTypeParameter) {
728                             val extendsList = element.extendsList
729                             val refList = extendsList.referenceElements
730                             if (refList.isNotEmpty()) {
731                                 for (refElement in refList) {
732                                     if (refElement is PsiJavaCodeReferenceElement) {
733                                         visitElement(refElement)
734                                         continue
735                                     }
736                                     val resolved = refElement.resolve()
737                                     if (resolved is PsiClass) {
738                                         addRealClass(
739                                             list,
740                                             codebase.findOrCreateClass(resolved)
741                                         )
742                                         resolved.typeParameterList?.accept(this)
743                                     }
744                                 }
745                             } else {
746                                 val extendsListTypes = element.extendsListTypes
747                                 if (extendsListTypes.isNotEmpty()) {
748                                     for (type in extendsListTypes) {
749                                         val resolved = type.resolve()
750                                         if (resolved != null) {
751                                             addRealClass(
752                                                 list, codebase.findOrCreateClass(resolved)
753                                             )
754                                             resolved.typeParameterList?.accept(this)
755                                         }
756                                     }
757                                 }
758                             }
759                             return
760                         } else if (element is PsiJavaCodeReferenceElement) {
761                             val resolved = element.resolve()
762                             if (resolved is PsiClass) {
763                                 addRealClass(
764                                     list,
765                                     codebase.findOrCreateClass(resolved)
766                                 )
767                                 element.parameterList?.accept(this)
768                                 return
769                             }
770                         } else if (element is PsiTypeElement) {
771                             val type = element.type
772                             if (type is PsiWildcardType) {
773                                 if (type.isBounded) {
774                                     addRealClass(
775                                         codebase,
776                                         list, type.bound
777                                     )
778                                 }
779                                 if (type.isExtends) {
780                                     addRealClass(
781                                         codebase,
782                                         list, type.extendsBound
783                                     )
784                                 }
785                                 if (type.isSuper) {
786                                     addRealClass(
787                                         codebase,
788                                         list, type.superBound
789                                     )
790                                 }
791                                 return
792                             }
793                             return
794                         }
795                         super.visitElement(element)
796                     }
797                 })
798 
799                 return list
800             } else {
801                 return emptyList()
802             }
803         }
804 
addRealClassnull805         private fun addRealClass(codebase: PsiBasedCodebase, classes: MutableList<ClassItem>, type: PsiType?) {
806             codebase.findClass(type ?: return)?.let {
807                 addRealClass(classes, it)
808             }
809         }
810 
addRealClassnull811         private fun addRealClass(classes: MutableList<ClassItem>, cls: ClassItem) {
812             if (!cls.isTypeParameter && !classes.contains(cls)) { // typically small number of items, don't need Set
813                 classes.add(cls)
814             }
815         }
816     }
817 }
818