1 /* <lambda>null2 * Copyright (C) 2024 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.SdkConstants 20 import com.android.tools.lint.UastEnvironment 21 import com.android.tools.lint.annotations.Extractor 22 import com.android.tools.metalava.model.ANDROIDX_NONNULL 23 import com.android.tools.metalava.model.ANDROIDX_NULLABLE 24 import com.android.tools.metalava.model.AnnotationItem 25 import com.android.tools.metalava.model.BaseModifierList 26 import com.android.tools.metalava.model.ClassItem 27 import com.android.tools.metalava.model.ClassKind 28 import com.android.tools.metalava.model.ClassOrigin 29 import com.android.tools.metalava.model.ClassTypeItem 30 import com.android.tools.metalava.model.Item 31 import com.android.tools.metalava.model.JAVA_PACKAGE_INFO 32 import com.android.tools.metalava.model.MutableModifierList 33 import com.android.tools.metalava.model.PackageFilter 34 import com.android.tools.metalava.model.PackageItem 35 import com.android.tools.metalava.model.TypeParameterScope 36 import com.android.tools.metalava.model.VisibilityLevel 37 import com.android.tools.metalava.model.addDefaultRetentionPolicyAnnotation 38 import com.android.tools.metalava.model.hasAnnotation 39 import com.android.tools.metalava.model.isRetention 40 import com.android.tools.metalava.model.item.CodebaseAssembler 41 import com.android.tools.metalava.model.item.DefaultPackageItem 42 import com.android.tools.metalava.model.item.MutablePackageDoc 43 import com.android.tools.metalava.model.item.PackageDoc 44 import com.android.tools.metalava.model.item.PackageDocs 45 import com.android.tools.metalava.model.psi.PsiConstructorItem.Companion.isPrimaryConstructor 46 import com.android.tools.metalava.model.source.SourceSet 47 import com.android.tools.metalava.model.source.utils.gatherPackageJavadoc 48 import com.android.tools.metalava.reporter.Issues 49 import com.android.utils.associateByNotNull 50 import com.intellij.openapi.application.ApplicationManager 51 import com.intellij.openapi.project.Project 52 import com.intellij.psi.JavaPsiFacade 53 import com.intellij.psi.JavaRecursiveElementVisitor 54 import com.intellij.psi.PsiAnnotation 55 import com.intellij.psi.PsiArrayType 56 import com.intellij.psi.PsiClass 57 import com.intellij.psi.PsiClassOwner 58 import com.intellij.psi.PsiClassType 59 import com.intellij.psi.PsiCodeBlock 60 import com.intellij.psi.PsiElement 61 import com.intellij.psi.PsiEllipsisType 62 import com.intellij.psi.PsiErrorElement 63 import com.intellij.psi.PsiFile 64 import com.intellij.psi.PsiImportStatement 65 import com.intellij.psi.PsiJavaFile 66 import com.intellij.psi.PsiManager 67 import com.intellij.psi.PsiMethod 68 import com.intellij.psi.PsiPackage 69 import com.intellij.psi.PsiParameter 70 import com.intellij.psi.PsiSubstitutor 71 import com.intellij.psi.PsiType 72 import com.intellij.psi.PsiTypeParameter 73 import com.intellij.psi.TypeAnnotationProvider 74 import com.intellij.psi.impl.file.PsiPackageImpl 75 import com.intellij.psi.javadoc.PsiDocComment 76 import com.intellij.psi.search.GlobalSearchScope 77 import com.intellij.psi.util.PsiTreeUtil 78 import java.io.File 79 import java.io.IOException 80 import java.util.zip.ZipFile 81 import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability 82 import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade 83 import org.jetbrains.kotlin.name.FqName 84 import org.jetbrains.kotlin.name.JvmStandardClassIds 85 import org.jetbrains.kotlin.psi.KtClassOrObject 86 import org.jetbrains.kotlin.psi.KtDeclaration 87 import org.jetbrains.kotlin.psi.KtFunction 88 import org.jetbrains.kotlin.psi.KtParameter 89 import org.jetbrains.kotlin.psi.KtProperty 90 import org.jetbrains.kotlin.psi.KtPropertyAccessor 91 import org.jetbrains.kotlin.psi.KtTypeAlias 92 import org.jetbrains.kotlin.psi.KtTypeReference 93 import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter 94 import org.jetbrains.uast.UClass 95 import org.jetbrains.uast.UFile 96 import org.jetbrains.uast.UMethod 97 import org.jetbrains.uast.UParameter 98 import org.jetbrains.uast.UastFacade 99 import org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService 100 import org.jetbrains.uast.toUElement 101 102 internal class PsiCodebaseAssembler( 103 private val uastEnvironment: UastEnvironment, 104 codebaseFactory: (PsiCodebaseAssembler) -> PsiBasedCodebase 105 ) : CodebaseAssembler { 106 107 internal val codebase = codebaseFactory(this) 108 109 internal val globalTypeItemFactory = PsiTypeItemFactory(this, TypeParameterScope.empty) 110 111 internal val project: Project = uastEnvironment.ideaProject 112 113 private val reporter 114 get() = codebase.reporter 115 116 /** 117 * Map from qualified class name to the heavyweight [PsiClass] implementations corresponding to 118 * a source class. 119 * 120 * Psi can represent classes with a number of different implementations of [PsiClass] that have 121 * different capabilities and provide different, and inconsistent, information. This keeps track 122 * of the heavyweight [PsiClass] implementations for source classes which do not contribute 123 * directly to an API surface (and so do not have a [ClassItem] created in the initialization of 124 * the [PsiBasedCodebase]) but which may contribute indirectly, e.g. through inherited methods. 125 * If a [ClassItem] needs to be created during processing, e.g. because it is a super type, then 126 * the [PsiClass] corresponding to it will be removed from this map (if it exists) and used. If 127 * it does not exist then it will be looked up using [JavaPsiFacade]. 128 */ 129 private val deferredHeavyweightPsiClasses = mutableMapOf<String, PsiClass>() 130 131 fun dispose() { 132 uastEnvironment.dispose() 133 } 134 135 private fun getFactory() = JavaPsiFacade.getElementFactory(project) 136 137 internal fun getClassType(cls: PsiClass): PsiClassType = 138 getFactory().createType(cls, PsiSubstitutor.EMPTY) 139 140 internal fun getComment(documentation: String, parent: PsiElement? = null): PsiDocComment = 141 getFactory().createDocCommentFromText(documentation, parent) 142 143 internal fun createPsiType(s: String, parent: PsiElement? = null): PsiType = 144 getFactory().createTypeFromText(s, parent) 145 146 private fun createPsiAnnotation(s: String, parent: PsiElement? = null): PsiAnnotation = 147 getFactory().createAnnotationFromText(s, parent) 148 149 internal fun findPsiPackage(pkgName: String): PsiPackage? { 150 return JavaPsiFacade.getInstance(project).findPackage(pkgName) 151 } 152 153 override fun createPackageItem( 154 packageName: String, 155 packageDoc: PackageDoc, 156 containingPackage: PackageItem? 157 ): DefaultPackageItem { 158 val psiPackage = 159 findPsiPackage(packageName) 160 ?: run { 161 // This can happen if a class's package statement does not match its file path. 162 // In that case, this fakes up a PsiPackageImpl that matches the package 163 // statement as that is the source of truth. 164 val manager = PsiManager.getInstance(codebase.project) 165 PsiPackageImpl(manager, packageName) 166 } 167 return PsiPackageItem.create( 168 codebase = codebase, 169 psiPackage = psiPackage, 170 packageDoc = packageDoc, 171 containingPackage = containingPackage, 172 ) 173 } 174 175 override fun createClassFromUnderlyingModel(qualifiedName: String) = 176 findOrCreateClass(qualifiedName) 177 178 /** Check if the [BaseModifierList] is accsssible. */ 179 private val BaseModifierList.isAccessible 180 get() = 181 when (getVisibilityLevel()) { 182 VisibilityLevel.PUBLIC, 183 VisibilityLevel.PROTECTED -> true 184 VisibilityLevel.INTERNAL -> annotations().any { it.showability.show() } 185 else -> false 186 } 187 188 /** 189 * Create a possible API class, i.e. a class that has a possibility of being part of an API 190 * surface. 191 * 192 * This will ignore any class that is inaccessible as it cannot be part of the API. A 193 * [ClassItem] may be created for it later if needed, e.g. if it is a super class of an 194 * accessible class. 195 */ 196 private fun createPossibleApiClass( 197 psiClass: PsiClass, 198 origin: ClassOrigin, 199 ): ClassItem? { 200 if (psiClass.containingClass != null) error("$psiClass is not a top level class") 201 202 // Ignore inaccessible classes. 203 val modifiers = PsiModifierItem.create(codebase, psiClass) 204 if (!modifiers.isAccessible) { 205 deferredHeavyweightPsiClasses[psiClass.qualifiedName!!] = psiClass 206 return null 207 } 208 209 return createTopLevelClassAndContents(psiClass, origin, modifiers) 210 } 211 212 /** Create a top level class, their inner classes and all the other members. */ 213 private fun createTopLevelClassAndContents( 214 psiClass: PsiClass, 215 origin: ClassOrigin, 216 modifiers: MutableModifierList = PsiModifierItem.create(codebase, psiClass), 217 ): ClassItem { 218 if (psiClass.containingClass != null) error("$psiClass is not a top level class") 219 return createClass( 220 psiClass, 221 null, 222 globalTypeItemFactory, 223 origin, 224 modifiers = modifiers, 225 ) 226 } 227 228 private fun createClass( 229 psiClass: PsiClass, 230 containingClassItem: ClassItem?, 231 enclosingClassTypeItemFactory: PsiTypeItemFactory, 232 origin: ClassOrigin, 233 modifiers: MutableModifierList = PsiModifierItem.create(codebase, psiClass), 234 ): ClassItem { 235 val packageName = getPackageName(psiClass) 236 237 // If the package could not be found then report an error. 238 findPsiPackage(packageName) 239 ?: run { 240 val directory = 241 psiClass.containingFile.containingDirectory.virtualFile.canonicalPath 242 reporter.report( 243 Issues.INVALID_PACKAGE, 244 psiClass, 245 "Could not find package $packageName for class ${psiClass.qualifiedName}." + 246 " This is most likely due to a mismatch between the package statement" + 247 " and the directory $directory" 248 ) 249 } 250 251 val packageItem = codebase.packageTracker.findOrCreatePackage(packageName) 252 253 if (psiClass is PsiTypeParameter) { 254 error( 255 "Must not be called with PsiTypeParameter; use PsiTypeParameterItem.create(...) instead" 256 ) 257 } 258 val qualifiedName = psiClass.classQualifiedName 259 val classKind = getClassKind(psiClass) 260 val isKotlin = psiClass.isKotlin() 261 if ( 262 classKind == ClassKind.ANNOTATION_TYPE && 263 !hasExplicitRetention(modifiers, psiClass, isKotlin) 264 ) { 265 modifiers.addDefaultRetentionPolicyAnnotation(codebase, isKotlin) 266 } 267 // Create the TypeParameterList for this before wrapping any of the other types used by 268 // it as they may reference a type parameter in the list. 269 val (typeParameterList, classTypeItemFactory) = 270 PsiTypeParameterList.create( 271 codebase, 272 enclosingClassTypeItemFactory, 273 "class $qualifiedName", 274 psiClass 275 ) 276 val (superClassType, interfaceTypes) = 277 computeSuperTypes(psiClass, classKind, classTypeItemFactory) 278 val classItem = 279 PsiClassItem( 280 codebase = codebase, 281 psiClass = psiClass, 282 modifiers = modifiers, 283 documentationFactory = PsiItemDocumentation.factory(psiClass, codebase), 284 classKind = classKind, 285 containingClass = containingClassItem, 286 containingPackage = packageItem, 287 qualifiedName = qualifiedName, 288 typeParameterList = typeParameterList, 289 origin = origin, 290 superClassType = superClassType, 291 interfaceTypes = interfaceTypes, 292 ) 293 // Construct the children 294 val psiMethods = psiClass.methods 295 // create methods 296 for (psiMethod in psiMethods) { 297 if (psiMethod.isConstructor) { 298 // Kotlin value class primary constructors must have exactly one parameter. If the 299 // parameter is optional, K1 generates an additional no-args constructor for Java. 300 // However, this constructor can't actually be called from Java, and the constructor 301 // with an optional arg is sufficient for Kotlin API tracking, so filter the no-args 302 // constructor out (this is consistent with K2). 303 if ( 304 classItem.modifiers.isValue() && 305 (psiMethod as UMethod).isPrimaryConstructor && 306 psiMethod.parameters.isEmpty() 307 ) 308 continue 309 310 val constructor = 311 PsiConstructorItem.create( 312 codebase, 313 classItem, 314 psiMethod, 315 classTypeItemFactory, 316 ) 317 addOverloadedKotlinCallablesIfNecessary( 318 classItem, 319 classTypeItemFactory, 320 constructor 321 ) 322 classItem.addConstructor(constructor) 323 } else { 324 // With K1, value class property accessors are present as [PsiMethod]s and with K2 325 // they are not. These accessor methods can't actually be used from Java, so this 326 // forces the K2 behavior and filters them out for K1. 327 if ( 328 classItem.modifiers.isValue() && psiMethod.sourceElement is KtPropertyAccessor 329 ) { 330 continue 331 } 332 333 val method = 334 PsiMethodItem.create(codebase, classItem, psiMethod, classTypeItemFactory) 335 if (!method.isEnumSyntheticMethod()) { 336 addOverloadedKotlinCallablesIfNecessary(classItem, classTypeItemFactory, method) 337 classItem.addMethod(method) 338 } 339 } 340 } 341 342 // With K2, value class constructors are not present on the PsiClass (b/369846185#comment6) 343 // because they can't be used from Java code. They can still be found on the KtClass, and we 344 // track them for Kotlin source compatibility. 345 // Value classes must have a primary constructor, so if none of the constructors are primary 346 // this must be K2, and the primary constructor needs to be added. 347 val ktClass = (psiClass as? UClass)?.sourcePsi as? KtClassOrObject 348 if (classItem.modifiers.isValue() && classItem.constructors().none { it.isPrimary }) { 349 val ktConstructor = ktClass?.primaryConstructor?.toUElement() as? PsiMethod 350 if (ktConstructor != null) { 351 val primaryConstructor = 352 PsiConstructorItem.create( 353 codebase, 354 classItem, 355 ktConstructor, 356 classTypeItemFactory 357 ) 358 classItem.addConstructor(primaryConstructor) 359 } 360 } 361 362 // Note that this is dependent on the constructor filtering above. UAST sometimes 363 // reports duplicate primary constructors, e.g.: the implicit no-arg constructor 364 // If the primary constructor has optional arguments, `isPrimary` will be true for all 365 // overloads, so there won't be one constructor selected as the class primary constructor. 366 val constructors = classItem.constructors() 367 constructors.singleOrNull { it.isPrimary }?.let { classItem.primaryConstructor = it } 368 val hasImplicitDefaultConstructor = hasImplicitDefaultConstructor(classItem) 369 if (hasImplicitDefaultConstructor) { 370 assert(constructors.isEmpty()) 371 classItem.addConstructor(classItem.createDefaultConstructor()) 372 } 373 val psiFields = psiClass.fields 374 if (psiFields.isNotEmpty()) { 375 for (psiField in psiFields) { 376 val fieldItem = 377 PsiFieldItem.create(codebase, classItem, psiField, classTypeItemFactory) 378 classItem.addField(fieldItem) 379 } 380 } 381 382 // Find all properties defined on the class 383 if (classItem.isKotlin()) { 384 // Collect all accessor methods, backing fields, and constructor parameters that could 385 // be associated with the class properties. 386 val accessors = 387 classItem.methods().filterIsInstance<PsiMethodItem>().groupBy { 388 it.psiMethod.propertyForAccessor() 389 } 390 val backingFields = 391 classItem 392 .fields() 393 .map { it as PsiFieldItem } 394 .associateByNotNull { it.psi().sourceElement as? KtDeclaration } 395 396 // Properties can either be declared directly as properties or as constructor params. 397 // First find all property declarations. 398 // For a file facade class containing top-level property definitions, the KtClass won't 399 // exist, so get top level definitions from the file(s). 400 val declarations = ktClass?.declarations ?: topLevelDeclarations(psiClass) 401 val ktProperties = declarations.filterIsInstance<KtProperty>() 402 for (ktProperty in ktProperties) { 403 val property = 404 PsiPropertyItem.create( 405 codebase = codebase, 406 ktDeclaration = ktProperty, 407 containingClass = classItem, 408 containingTypeItemFactory = classTypeItemFactory, 409 accessors = accessors[ktProperty] ?: emptyList(), 410 constructorParameter = null, 411 backingField = backingFields[ktProperty], 412 ) 413 ?: continue 414 classItem.addProperty(property) 415 } 416 417 // Find all properties declared as constructor params 418 if (ktClass != null) { 419 val constructorParameters = 420 classItem.primaryConstructor 421 ?.parameters() 422 ?.map { it as PsiParameterItem } 423 ?.filter { (it.sourcePsi as? KtParameter)?.isPropertyParameter() ?: false } 424 ?.associateBy { it.name() } 425 .orEmpty() 426 427 val ktParameters = 428 ktClass.primaryConstructor 429 ?.valueParameters 430 ?.filter { it.isPropertyParameter() } 431 .orEmpty() 432 for (ktParameter in ktParameters) { 433 val property = 434 PsiPropertyItem.create( 435 codebase = codebase, 436 ktDeclaration = ktParameter, 437 containingClass = classItem, 438 containingTypeItemFactory = classTypeItemFactory, 439 accessors = accessors[ktParameter] ?: emptyList(), 440 constructorParameter = constructorParameters[ktParameter.name], 441 backingField = backingFields[ktParameter], 442 ) 443 ?: continue 444 classItem.addProperty(property) 445 } 446 } 447 } 448 // This actually gets all nested classes not just inner, i.e. non-static nested, 449 // classes. 450 val psiNestedClasses = psiClass.innerClasses 451 for (psiNestedClass in psiNestedClasses) { 452 createClass( 453 psiClass = psiNestedClass, 454 containingClassItem = classItem, 455 enclosingClassTypeItemFactory = classTypeItemFactory, 456 origin = origin, 457 ) 458 } 459 return classItem 460 } 461 462 /** Returns the property or parameter declaration associated with the method, if one exists. */ 463 private fun PsiMethod.propertyForAccessor(): KtDeclaration? { 464 return when (val sourceElement = sourceElement) { 465 is KtProperty -> sourceElement 466 is KtPropertyAccessor -> sourceElement.property 467 is KtParameter -> sourceElement 468 else -> null 469 } 470 } 471 472 private fun hasExplicitRetention( 473 modifiers: BaseModifierList, 474 psiClass: PsiClass, 475 isKotlin: Boolean 476 ): Boolean { 477 if (modifiers.hasAnnotation(AnnotationItem::isRetention)) { 478 return true 479 } 480 if (isKotlin && psiClass is UClass) { 481 // In Kotlin some annotations show up on the Java facade only; for example, 482 // a @DslMarker annotation will imply a runtime annotation which is present 483 // in the java facade, not in the source list of annotations 484 val modifierList = psiClass.modifierList 485 if ( 486 modifierList != null && 487 modifierList.annotations.any { isRetention(it.qualifiedName) } 488 ) { 489 return true 490 } 491 } 492 return false 493 } 494 495 /** 496 * Compute the super types for the class. 497 * 498 * Returns a pair of the optional super class type and the possibly empty list of interface 499 * types. 500 */ 501 private fun computeSuperTypes( 502 psiClass: PsiClass, 503 classKind: ClassKind, 504 classTypeItemFactory: PsiTypeItemFactory 505 ): Pair<ClassTypeItem?, List<ClassTypeItem>> { 506 507 // A map from the qualified type name to the corresponding [KtTypeReference]. This is 508 // empty for non-Kotlin code, otherwise it maps from the qualified type name of a 509 // super type to the associated [KtTypeReference]. The qualified name is used to map 510 // between them because Kotlin does not differentiate between `implements` and `extends` 511 // lists and just has one super type list. The qualified name is safe because a class 512 // cannot implement/extend the same generic type multiple times with different type 513 // arguments so the qualified name should be unique among the super type list. 514 // The [KtTypeReference] is needed to access the type nullability of the generic type 515 // arguments. 516 val qualifiedNameToKt = 517 if (psiClass is UClass) { 518 psiClass.uastSuperTypes.associateBy({ it.getQualifiedName() }) { 519 it.sourcePsi as KtTypeReference 520 } 521 } else emptyMap() 522 523 // Get the [KtTypeReference], if any, associated with ths [PsiType] which must be a 524 // [PsiClassType] as that is the only type allowed in an extends/implements list. 525 fun PsiType.ktTypeReference(): KtTypeReference? { 526 val qualifiedName = (this as PsiClassType).computeQualifiedName() 527 return qualifiedNameToKt[qualifiedName] 528 } 529 530 // Construct the super class type if needed and available. 531 val superClassType = 532 if (classKind != ClassKind.INTERFACE) { 533 val superClassPsiType = psiClass.superClassType as? PsiType 534 superClassPsiType?.let { superClassType -> 535 val ktTypeRef = superClassType.ktTypeReference() 536 classTypeItemFactory.getSuperClassType(PsiTypeInfo(superClassType, ktTypeRef)) 537 } 538 } else null 539 540 // Get the interfaces from the appropriate list. 541 val interfaces = 542 if (classKind == ClassKind.INTERFACE || classKind == ClassKind.ANNOTATION_TYPE) { 543 // An interface uses "extends <interfaces>", either explicitly for normal 544 // interfaces or implicitly for annotations. 545 psiClass.extendsListTypes 546 } else { 547 // A class uses "extends <interfaces>". 548 psiClass.implementsListTypes 549 } 550 551 // Map them to PsiTypeItems. 552 val interfaceTypes = 553 interfaces.map { interfaceType -> 554 val ktTypeRef = interfaceType.ktTypeReference() 555 classTypeItemFactory.getInterfaceType(PsiTypeInfo(interfaceType, ktTypeRef)) 556 } 557 return Pair(superClassType, interfaceTypes) 558 } 559 560 private fun getClassKind(psiClass: PsiClass): ClassKind { 561 return when { 562 psiClass.isAnnotationType -> ClassKind.ANNOTATION_TYPE 563 psiClass.isInterface -> ClassKind.INTERFACE 564 psiClass.isEnum -> ClassKind.ENUM 565 psiClass is PsiTypeParameter -> 566 error("Must not call this with a PsiTypeParameter - $psiClass") 567 else -> ClassKind.CLASS 568 } 569 } 570 571 private fun hasImplicitDefaultConstructor(classItem: PsiClassItem): Boolean { 572 if (classItem.simpleName().startsWith("-")) { 573 // Deliberately hidden; see examples like 574 // @file:JvmName("-ViewModelExtensions") // Hide from Java sources in the IDE. 575 return false 576 } 577 578 val psiClass = classItem.psiClass 579 if (psiClass is UClass && psiClass.sourcePsi == null) { 580 // Top level kt classes (FooKt for Foo.kt) do not have implicit default constructor 581 return false 582 } 583 584 val constructors = classItem.constructors() 585 return constructors.isEmpty() && classItem.isClass() 586 } 587 588 /** 589 * Returns true if overloads of this callable should be created separately. 590 * 591 * This works around the issue of actual callable not generating overloads for @JvmOverloads 592 * annotation when the default is specified on expect side 593 * (https://youtrack.jetbrains.com/issue/KT-57537). 594 */ 595 private fun PsiCallableItem.shouldExpandOverloads(): Boolean { 596 val ktFunction = (psiMethod as? UMethod)?.sourcePsi as? KtFunction ?: return false 597 return modifiers.isActual() && 598 psiMethod.hasAnnotation(JvmStandardClassIds.JVM_OVERLOADS_FQ_NAME.asString()) && 599 // It is /technically/ invalid to have actual functions with default values, but 600 // some places suppress the compiler error, so we should handle it here too. 601 ktFunction.valueParameters.none { it.hasDefaultValue() } && 602 parameters().any { it.hasDefaultValue() } 603 } 604 605 /** 606 * Add overloads of [callable] if necessary. 607 * 608 * Workaround for https://youtrack.jetbrains.com/issue/KT-57537. 609 * 610 * For each parameter with a default value in [callable] this adds a [PsiCallableItem] that 611 * excludes that parameter and all following parameters with default values. 612 */ 613 private fun addOverloadedKotlinCallablesIfNecessary( 614 classItem: PsiClassItem, 615 enclosingClassTypeItemFactory: PsiTypeItemFactory, 616 callable: PsiCallableItem, 617 ) { 618 if (!callable.shouldExpandOverloads()) { 619 return 620 } 621 622 val parameters = callable.parameters() 623 624 // Create an overload of the constructor for each parameter that has a default value. The 625 // constructor will exclude that parameter and all following parameters that have default 626 // values. 627 for (currentParameterIndex in parameters.indices) { 628 val currentParameter = parameters[currentParameterIndex] 629 // There is no need to create an overload if the parameter does not have default value. 630 if (!currentParameter.hasDefaultValue()) continue 631 632 val psiParameters = 633 parameters.mapIndexedNotNull { index, parameterItem -> 634 // Ignore the current parameter as well as any following parameters 635 // with default values. 636 if (index >= currentParameterIndex && parameterItem.hasDefaultValue()) null 637 else (parameterItem as PsiParameterItem).psiParameter 638 } 639 // Create an overloaded callable. 640 when (callable) { 641 is PsiConstructorItem -> { 642 val overloadConstructor = 643 PsiConstructorItem.create( 644 codebase, 645 classItem, 646 callable.psiMethod, 647 enclosingClassTypeItemFactory, 648 psiParameters, 649 ) 650 651 classItem.addConstructor(overloadConstructor) 652 } 653 is PsiMethodItem -> { 654 val overloadMethod = 655 PsiMethodItem.create( 656 codebase, 657 classItem, 658 callable.psiMethod, 659 enclosingClassTypeItemFactory, 660 psiParameters, 661 ) 662 663 classItem.addMethod(overloadMethod) 664 } 665 } 666 } 667 } 668 669 private fun findOrCreateClass(qualifiedName: String): ClassItem? { 670 // Check to see if the class has already been seen and if so return it immediately. 671 codebase.findClass(qualifiedName)?.let { 672 return it 673 } 674 675 // Create the ClassItem from a heavyweight PsiClass, if available. 676 deferredHeavyweightPsiClasses.remove(qualifiedName)?.let { 677 return findOrCreateClass(it) 678 } 679 680 // The following cannot find a class whose name does not correspond to the file name, e.g. 681 // in Java a class that is a second top level class. 682 val finder = JavaPsiFacade.getInstance(project) 683 val psiClass = 684 finder.findClass(qualifiedName, GlobalSearchScope.allScope(project)) ?: return null 685 return findOrCreateClass(psiClass) 686 } 687 688 /** 689 * Identifies a point in the [ClassItem] nesting structure where new [ClassItem]s need 690 * inserting. 691 */ 692 data class NewClassInsertionPoint( 693 /** 694 * The [PsiClass] that is the root of the nested classes that need creation, is a top level 695 * class if [containingClassItem] is `null`. 696 */ 697 val missingPsiClass: PsiClass, 698 699 /** The containing class item, or `null` if the top level. */ 700 val containingClassItem: ClassItem?, 701 ) 702 703 /** 704 * Called when no [ClassItem] was found by [PsiBasedCodebase.findClass]`([PsiClass]) when called 705 * on [psiClass]. 706 * 707 * The purpose of this is to find where a new [ClassItem] should be inserted in the nested class 708 * structure. It finds the outermost [PsiClass] with no associated [ClassItem] but which is 709 * either a top level class or whose containing [PsiClass] does have an associated [ClassItem]. 710 * That is the point where new classes need to be created. 711 * 712 * e.g. if the nesting structure is `A.B.C` and `A` has already been created then the insertion 713 * point would consist of [ClassItem] for `A` (the containing class item) and the [PsiClass] for 714 * `B` (the outermost [PsiClass] with no associated item). 715 * 716 * If none had already been created then it would return an insertion point consisting of no 717 * containing class item and the [PsiClass] for `A`. 718 */ 719 private fun findNewClassInsertionPoint(psiClass: PsiClass): NewClassInsertionPoint { 720 var current = psiClass 721 do { 722 // If the current has no containing class then it has reached the top level class so 723 // return an insertion point that has no containing class item and the current class. 724 val containing = current.containingClass ?: return NewClassInsertionPoint(current, null) 725 726 // If the containing class has a matching class item then return an insertion point that 727 // uses that containing class item and the current class. 728 codebase.findClass(containing)?.let { containingClassItem -> 729 return NewClassInsertionPoint(current, containingClassItem) 730 } 731 current = containing 732 } while (true) 733 } 734 735 internal fun findOrCreateClass(psiClass: PsiClass): ClassItem { 736 if (psiClass is PsiTypeParameter) { 737 error( 738 "Must not be called with PsiTypeParameter; call findOrCreateTypeParameter(...) instead" 739 ) 740 } 741 742 // If it has already been created then return it. 743 codebase.findClass(psiClass)?.let { 744 return it 745 } 746 747 // Otherwise, find an insertion point at which new classes should be created. 748 val (missingPsiClass, containingClassItem) = findNewClassInsertionPoint(psiClass) 749 750 // Create a top level or nested class as appropriate. 751 val createdClassItem = 752 if (containingClassItem == null) { 753 // Try and determine the origin of the class. 754 val containingFile = missingPsiClass.containingFile 755 val origin = 756 if (containingFile == null || containingFile.name.endsWith(".class")) 757 ClassOrigin.CLASS_PATH 758 else ClassOrigin.SOURCE_PATH 759 760 createTopLevelClassAndContents( 761 missingPsiClass, 762 origin, 763 ) 764 } else { 765 createClass( 766 missingPsiClass, 767 containingClassItem, 768 globalTypeItemFactory.from(containingClassItem), 769 origin = containingClassItem.origin, 770 ) 771 } 772 773 // Select the class item to return. 774 return if (missingPsiClass == psiClass) { 775 // The created class item was what was requested so just return it. 776 createdClassItem 777 } else { 778 // Otherwise, a nested class was requested so find it. It was created when its 779 // containing class was created. 780 codebase.findClass(psiClass)!! 781 } 782 } 783 784 private fun getPackageName(clz: PsiClass): String { 785 var top: PsiClass? = clz 786 while (top?.containingClass != null) { 787 top = top.containingClass 788 } 789 top ?: return "" 790 791 val simpleName = top.simpleName 792 val qualifiedName = top.classQualifiedName 793 794 if (simpleName == qualifiedName) { 795 return "" 796 } 797 798 return qualifiedName.substring(0, qualifiedName.length - 1 - simpleName.length) 799 } 800 801 internal fun createAnnotation( 802 source: String, 803 context: Item?, 804 ): AnnotationItem? { 805 val psiAnnotation = createPsiAnnotation(source, (context as? PsiItem)?.psi()) 806 return PsiAnnotationItem.create(codebase, psiAnnotation) 807 } 808 809 fun getPsiTypeForPsiParameter(psiParameter: PsiParameter): PsiType { 810 // UAST workaround: nullity of element type in last `vararg` parameter's array type 811 val psiType = psiParameter.type 812 return if ( 813 psiParameter is UParameter && 814 psiParameter.sourcePsi is KtParameter && 815 psiParameter.isVarArgs && // last `vararg` 816 psiType is PsiArrayType 817 ) { 818 val ktParameter = psiParameter.sourcePsi as KtParameter 819 val annotationProvider = 820 when (uastResolveService?.nullability(ktParameter)) { 821 KaTypeNullability.NON_NULLABLE -> getNonNullAnnotationProvider() 822 KaTypeNullability.NULLABLE -> getNullableAnnotationProvider() 823 else -> null 824 } 825 val annotatedType = 826 if (annotationProvider != null) { 827 psiType.componentType.annotate(annotationProvider) 828 } else { 829 psiType.componentType 830 } 831 PsiEllipsisType(annotatedType, annotatedType.annotationProvider) 832 } else { 833 psiType 834 } 835 } 836 837 private val uastResolveService: BaseKotlinUastResolveProviderService? by lazy { 838 ApplicationManager.getApplication() 839 .getService(BaseKotlinUastResolveProviderService::class.java) 840 } 841 842 private var nonNullAnnotationProvider: TypeAnnotationProvider? = null 843 private var nullableAnnotationProvider: TypeAnnotationProvider? = null 844 845 /** Type annotation provider which provides androidx.annotation.NonNull */ 846 private fun getNonNullAnnotationProvider(): TypeAnnotationProvider { 847 return nonNullAnnotationProvider 848 ?: run { 849 val provider = 850 TypeAnnotationProvider.Static.create( 851 arrayOf(createPsiAnnotation("@$ANDROIDX_NONNULL")) 852 ) 853 nonNullAnnotationProvider = provider 854 provider 855 } 856 } 857 858 /** Type annotation provider which provides androidx.annotation.Nullable */ 859 private fun getNullableAnnotationProvider(): TypeAnnotationProvider { 860 return nullableAnnotationProvider 861 ?: run { 862 val provider = 863 TypeAnnotationProvider.Static.create( 864 arrayOf(createPsiAnnotation("@$ANDROIDX_NULLABLE")) 865 ) 866 nullableAnnotationProvider = provider 867 provider 868 } 869 } 870 871 internal fun initializeFromJar(jarFile: File) { 872 // Extract the list of class names from the jar file. 873 val classNames = buildList { 874 try { 875 ZipFile(jarFile).use { jar -> 876 for (entry in jar.entries().iterator()) { 877 val fileName = entry.name 878 if (fileName.contains("$")) { 879 // skip inner classes 880 continue 881 } 882 if (!fileName.endsWith(SdkConstants.DOT_CLASS)) { 883 // skip entries that are not .class files. 884 continue 885 } 886 887 val qualifiedName = 888 fileName.removeSuffix(SdkConstants.DOT_CLASS).replace('/', '.') 889 if (qualifiedName.endsWith(".package-info")) { 890 // skip package-info files. 891 continue 892 } 893 894 add(qualifiedName) 895 } 896 } 897 } catch (e: IOException) { 898 reporter.report(Issues.IO_ERROR, jarFile, e.message ?: e.toString()) 899 } 900 } 901 902 // Create the initial set of packages that were found in the jar files. When loading from a 903 // jar there is no package documentation so this will only create the root package. 904 codebase.packageTracker.createInitialPackages(PackageDocs.EMPTY) 905 906 // Find all classes referenced from the class 907 val facade = JavaPsiFacade.getInstance(project) 908 val scope = GlobalSearchScope.allScope(project) 909 910 val isFromClassPath = codebase.isFromClassPath() 911 val origin = if (isFromClassPath) ClassOrigin.CLASS_PATH else ClassOrigin.COMMAND_LINE 912 for (className in classNames) { 913 val psiClass = facade.findClass(className, scope) ?: continue 914 915 val classItem = createPossibleApiClass(psiClass, origin) ?: continue 916 codebase.addTopLevelClassFromSource(classItem) 917 } 918 } 919 920 internal fun initializeFromSources( 921 sourceSet: SourceSet, 922 apiPackages: PackageFilter?, 923 ) { 924 // Get the list of `PsiFile`s from the `SourceSet`. 925 val psiFiles = Extractor.createUnitsForFiles(uastEnvironment.ideaProject, sourceSet.sources) 926 927 // Split the `PsiFile`s into `PsiClass`es and `package-info.java` `PsiJavaFile`s. 928 val (packageInfoFiles, psiClasses) = splitPsiFilesIntoClassesAndPackageInfoFiles(psiFiles) 929 930 // Gather all package related javadoc. 931 val packageDocs = 932 gatherPackageJavadoc( 933 reporter, 934 sourceSet, 935 packageNameFilter = { findPsiPackage(it) != null }, 936 packageInfoFiles, 937 packageInfoDocExtractor = { getOptionalPackageDocFromPackageInfoFile(it) }, 938 ) 939 940 // Create the initial set of packages that were found in the source files. 941 codebase.packageTracker.createInitialPackages(packageDocs) 942 943 findTypeAliases(psiClasses, codebase) 944 945 // Process the `PsiClass`es. 946 for (psiClass in psiClasses) { 947 // If a package filter is supplied then ignore any classes that do not match it. 948 if (apiPackages != null) { 949 val packageName = getPackageName(psiClass) 950 if (!apiPackages.matches(packageName)) continue 951 } 952 953 val classItem = 954 createPossibleApiClass( 955 psiClass, 956 // Sources always come from the command line. 957 ClassOrigin.COMMAND_LINE, 958 ) 959 ?: continue 960 codebase.addTopLevelClassFromSource(classItem) 961 } 962 } 963 964 /** 965 * Finds all type aliases declared in the [KtFile]s underlying any file facade classes in 966 * [psiClasses] and adds them to the codebase. 967 */ 968 private fun findTypeAliases(psiClasses: List<PsiClass>, codebase: PsiBasedCodebase) { 969 val typeAliases = 970 psiClasses.flatMap { topLevelDeclarations(it) }.filterIsInstance<KtTypeAlias>() 971 for (typeAlias in typeAliases) { 972 PsiTypeAliasItem.create(typeAlias, codebase) 973 } 974 } 975 976 /** 977 * Returns a list of declarations from the [fileFacadeClass]. If [fileFacadeClass] is not 978 * actually a file facade class, returns an empty list. 979 */ 980 private fun topLevelDeclarations(fileFacadeClass: PsiClass): List<KtDeclaration> { 981 return ((fileFacadeClass as? UClass)?.javaPsi as? KtLightClassForFacade)?.files?.flatMap { 982 it.declarations 983 } 984 ?: emptyList() 985 } 986 987 /** 988 * Split the [psiFiles] into separate `package-info.java` [PsiJavaFile]s and [PsiClass]es. 989 * 990 * During the processing this checks each [PsiFile] for unresolved imports and each [PsiClass] 991 * for syntax errors. 992 */ 993 private fun splitPsiFilesIntoClassesAndPackageInfoFiles( 994 psiFiles: List<PsiFile> 995 ): Pair<List<PsiJavaFile>, List<PsiClass>> { 996 // A set to track `@JvmMultifileClass`es that have already been added to psiClasses. 997 val multiFileClassNames = HashSet<FqName>() 998 999 val psiClasses = mutableListOf<PsiClass>() 1000 val packageInfoFiles = mutableListOf<PsiJavaFile>() 1001 1002 // Make sure we only process the files once; sometimes there's overlap in the source lists 1003 for (psiFile in psiFiles.asSequence().distinct()) { 1004 checkForUnresolvedImports(psiFile) 1005 1006 val classes = getPsiClassesFromPsiFile(psiFile) 1007 when { 1008 classes.isEmpty() && psiFile is PsiJavaFile -> { 1009 if (psiFile.name == JAVA_PACKAGE_INFO) { 1010 packageInfoFiles.add(psiFile) 1011 } 1012 } 1013 else -> { 1014 for (psiClass in classes) { 1015 checkForSyntaxErrors(psiClass) 1016 1017 // Multi file classes appear identically from each file they're defined in, 1018 // don't add duplicates 1019 val multiFileClassName = getOptionalMultiFileClassName(psiClass) 1020 if (multiFileClassName != null) { 1021 if (multiFileClassName in multiFileClassNames) { 1022 continue 1023 } else { 1024 multiFileClassNames.add(multiFileClassName) 1025 } 1026 } 1027 1028 psiClasses.add(psiClass) 1029 } 1030 } 1031 } 1032 } 1033 return Pair(packageInfoFiles, psiClasses) 1034 } 1035 1036 /** Check to see if [psiFile] contains any unresolved imports. */ 1037 private fun checkForUnresolvedImports(psiFile: PsiFile?) { 1038 // Visiting psiFile directly would eagerly load the entire file even though we only need 1039 // the importList here. 1040 (psiFile as? PsiJavaFile) 1041 ?.importList 1042 ?.accept( 1043 object : JavaRecursiveElementVisitor() { 1044 override fun visitImportStatement(element: PsiImportStatement) { 1045 super.visitImportStatement(element) 1046 if (element.resolve() == null) { 1047 reporter.report( 1048 Issues.UNRESOLVED_IMPORT, 1049 element, 1050 "Unresolved import: `${element.qualifiedName}`" 1051 ) 1052 } 1053 } 1054 } 1055 ) 1056 } 1057 1058 /** Get, the possibly empty, list of [PsiClass]es from the [psiFile]. */ 1059 private fun getPsiClassesFromPsiFile(psiFile: PsiFile): List<PsiClass> { 1060 // First, check for Java classes, return any that are found. 1061 (psiFile as? PsiClassOwner)?.classes?.toList()?.let { if (it.isNotEmpty()) return it } 1062 1063 // Then, check for Kotlin classes, returning any that are found, or an empty list. 1064 val uFile = UastFacade.convertElementWithParent(psiFile, UFile::class.java) as? UFile? 1065 return uFile?.classes?.map { it }?.toList() ?: emptyList() 1066 } 1067 1068 /** 1069 * Get the optional [MutablePackageDoc] from [psiFile]. 1070 * 1071 * @param psiFile must be a `package-info.java` file. 1072 */ 1073 private fun getOptionalPackageDocFromPackageInfoFile(psiFile: PsiJavaFile): MutablePackageDoc? { 1074 val packageStatement = psiFile.packageStatement ?: return null 1075 val packageName = packageStatement.packageName 1076 1077 // Make sure that this is actually a package. 1078 findPsiPackage(packageName) ?: return null 1079 1080 // Look for javadoc on the package statement; this is NOT handed to us on the PsiPackage! 1081 val comment = PsiTreeUtil.getPrevSiblingOfType(packageStatement, PsiDocComment::class.java) 1082 if (comment != null) { 1083 return MutablePackageDoc( 1084 qualifiedName = packageName, 1085 fileLocation = PsiFileLocation.fromPsiElement(psiFile), 1086 commentFactory = 1087 PsiItemDocumentation.factory(packageStatement, codebase, comment.text), 1088 ) 1089 } 1090 1091 // No comment could be found. 1092 return null 1093 } 1094 1095 /** Check the [psiClass] for any syntax errors. */ 1096 private fun checkForSyntaxErrors(psiClass: PsiClass) { 1097 psiClass.accept( 1098 object : JavaRecursiveElementVisitor() { 1099 override fun visitErrorElement(element: PsiErrorElement) { 1100 super.visitErrorElement(element) 1101 reporter.report( 1102 Issues.INVALID_SYNTAX, 1103 element, 1104 "Syntax error: `${element.errorDescription}`" 1105 ) 1106 } 1107 1108 override fun visitCodeBlock(block: PsiCodeBlock) { 1109 // Ignore to avoid eagerly parsing all method bodies. 1110 } 1111 1112 override fun visitDocComment(comment: PsiDocComment) { 1113 // Ignore to avoid eagerly parsing all doc comments. 1114 // Doc comments cannot contain error elements. 1115 } 1116 } 1117 ) 1118 } 1119 1120 /** Get the optional multi file class name. */ 1121 private fun getOptionalMultiFileClassName(psiClass: PsiClass): FqName? { 1122 val ktLightClass = (psiClass as? UClass)?.javaPsi as? KtLightClassForFacade 1123 val multiFileClassName = 1124 if (ktLightClass?.multiFileClass == true) { 1125 ktLightClass.facadeClassFqName 1126 } else { 1127 null 1128 } 1129 return multiFileClassName 1130 } 1131 } 1132 1133 /** 1134 * Get the simple name of a named class or type parameter. 1135 * 1136 * A [PsiClass] is used to represent named classes, type parameters, anonymous and local classes. 1137 * So, its [PsiClass.getName] can sometimes be `null`. However, Metalava only gets the name for 1138 * named classes and type parameters which never return `null`. So, this extension property forces 1139 * it to be non-null. 1140 */ 1141 internal val PsiClass.simpleName 1142 get() = name!! 1143 1144 /** 1145 * Get the qualified name of a name class. 1146 * 1147 * A [PsiClass] is used to represent named classes, type parameters, anonymous and local classes. 1148 * So, its [PsiClass.getQualifiedName] can sometimes be `null`. However, Metalava only gets the 1149 * qualified name for name classes which never return `null`. So, this extension property forces it 1150 * to be non-null. 1151 */ 1152 internal val PsiClass.classQualifiedName 1153 get() = qualifiedName!! 1154