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