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