• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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