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