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