1 /* <lambda>null2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.model.psi 18 19 import com.android.SdkConstants 20 import com.android.tools.lint.UastEnvironment 21 import com.android.tools.metalava.ANDROIDX_NONNULL 22 import com.android.tools.metalava.ANDROIDX_NULLABLE 23 import com.android.tools.metalava.Issues 24 import com.android.tools.metalava.model.ClassItem 25 import com.android.tools.metalava.model.DefaultCodebase 26 import com.android.tools.metalava.model.FieldItem 27 import com.android.tools.metalava.model.Item 28 import com.android.tools.metalava.model.MethodItem 29 import com.android.tools.metalava.model.PackageDocs 30 import com.android.tools.metalava.model.PackageItem 31 import com.android.tools.metalava.model.PackageList 32 import com.android.tools.metalava.options 33 import com.android.tools.metalava.reporter 34 import com.android.tools.metalava.tick 35 import com.intellij.openapi.project.Project 36 import com.intellij.psi.JavaPsiFacade 37 import com.intellij.psi.JavaRecursiveElementVisitor 38 import com.intellij.psi.PsiAnnotation 39 import com.intellij.psi.PsiArrayType 40 import com.intellij.psi.PsiClass 41 import com.intellij.psi.PsiClassOwner 42 import com.intellij.psi.PsiClassType 43 import com.intellij.psi.PsiCodeBlock 44 import com.intellij.psi.PsiElement 45 import com.intellij.psi.PsiErrorElement 46 import com.intellij.psi.PsiField 47 import com.intellij.psi.PsiFile 48 import com.intellij.psi.PsiImportStatement 49 import com.intellij.psi.PsiJavaCodeReferenceElement 50 import com.intellij.psi.PsiJavaFile 51 import com.intellij.psi.PsiMethod 52 import com.intellij.psi.PsiPackage 53 import com.intellij.psi.PsiSubstitutor 54 import com.intellij.psi.PsiType 55 import com.intellij.psi.TypeAnnotationProvider 56 import com.intellij.psi.javadoc.PsiDocComment 57 import com.intellij.psi.javadoc.PsiDocTag 58 import com.intellij.psi.search.GlobalSearchScope 59 import com.intellij.psi.util.PsiTreeUtil 60 import org.jetbrains.kotlin.psi.KtElement 61 import org.jetbrains.kotlin.resolve.BindingContext 62 import org.jetbrains.uast.UFile 63 import org.jetbrains.uast.UastFacade 64 import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService 65 import java.io.File 66 import java.io.IOException 67 import java.util.zip.ZipFile 68 69 const val PACKAGE_ESTIMATE = 500 70 const val CLASS_ESTIMATE = 15000 71 const val METHOD_ESTIMATE = 1000 72 73 /** 74 * A codebase containing Java, Kotlin, or UAST PSI classes 75 * 76 * After creation, a list of PSI file or a JAR file is passed to [initialize]. This creates package 77 * and class items along with their members. This process is broken into two phases: 78 * 79 * First, [initializing] is set to true, and class items are created from the supplied sources. 80 * These are main classes of the codebase and have [ClassItem.emit] set to true and 81 * [ClassItem.isFromClassPath] set to false. While creating these, package names are reserved and 82 * associated with their classes in [packageClasses]. 83 * 84 * Next, package items are created for source classes based on the contents of [packageClasses] 85 * with [PackageItem.emit] set to true. 86 * 87 * Then [initializing] is set to false and the second pass begins. This path iteratively resolves 88 * supertypes of class items until all are fully resolved, creating new class and package items as 89 * needed. Since all the source class and package items have been created, new items are assumed to 90 * originate from the classpath and have [Item.emit] set to false and [Item.isFromClassPath] set to 91 * true. 92 */ 93 open class PsiBasedCodebase( 94 location: File, 95 override var description: String = "Unknown" 96 ) : DefaultCodebase(location) { 97 lateinit var uastEnvironment: UastEnvironment 98 val project: Project 99 get() = uastEnvironment.ideaProject 100 101 /** Map from class name to class item. Classes are added via [registerClass] */ 102 private val classMap: MutableMap<String, PsiClassItem> = HashMap(CLASS_ESTIMATE) 103 104 /** 105 * Map from classes to the set of methods for each (but only for classes where we've 106 * called [findMethod] 107 */ 108 private lateinit var methodMap: MutableMap<PsiClassItem, MutableMap<PsiMethod, PsiMethodItem>> 109 110 /** Map from package name to the corresponding package item */ 111 private lateinit var packageMap: MutableMap<String, PsiPackageItem> 112 113 /** Map from package name to list of classes in that package. Only used during [initialize]. */ 114 private lateinit var packageClasses: MutableMap<String, MutableList<PsiClassItem>> 115 116 /** A set of packages to hide */ 117 private lateinit var hiddenPackages: MutableMap<String, Boolean?> 118 119 /** 120 * A list of the top-level classes declared in the codebase's source (rather than on its 121 * classpath). 122 */ 123 private lateinit var topLevelClassesFromSource: MutableList<PsiClassItem> 124 125 /** 126 * Set to true in [initialize] for the first pass of creating class items for all classes in 127 * the codebase sources and false for the second pass of creating class items for the 128 * supertypes of the codebase classes. New class items created in the supertypes pass must come 129 * from the classpath (dependencies) since all source classes have been created. 130 * 131 * This information is used in [createClass] to set [ClassItem.emit] to true for source classes 132 * and [ClassItem.isFromClassPath] to true for classpath classes. It is also used in 133 * [registerPackage] to set [PackageItem.emit] to true for source packages. 134 */ 135 private var initializing = false 136 137 override fun trustedApi(): Boolean = false 138 139 private var packageDocs: PackageDocs? = null 140 141 private var hideClassesFromJars = true 142 143 private lateinit var emptyPackage: PsiPackageItem 144 145 fun initialize( 146 uastEnvironment: UastEnvironment, 147 psiFiles: List<PsiFile>, 148 packages: PackageDocs, 149 ) { 150 initializing = true 151 this.units = psiFiles 152 packageDocs = packages 153 154 this.uastEnvironment = uastEnvironment 155 // there are currently ~230 packages in the public SDK, but here we need to account for internal ones too 156 val hiddenPackages: MutableSet<String> = packages.hiddenPackages 157 val packageDocs: MutableMap<String, String> = packages.packageDocs 158 this.hiddenPackages = HashMap(100) 159 for (pkgName in hiddenPackages) { 160 this.hiddenPackages[pkgName] = true 161 } 162 163 packageMap = HashMap(PACKAGE_ESTIMATE) 164 packageClasses = HashMap(PACKAGE_ESTIMATE) 165 packageClasses[""] = ArrayList() 166 this.methodMap = HashMap(METHOD_ESTIMATE) 167 topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE) 168 169 // Make sure we only process the files once; sometimes there's overlap in the source lists 170 for (psiFile in psiFiles.asSequence().distinct()) { 171 tick() // show progress 172 173 // Visiting psiFile directly would eagerly load the entire file even though we only need 174 // the importList here. 175 (psiFile as? PsiJavaFile)?.importList?.accept(object : JavaRecursiveElementVisitor() { 176 override fun visitImportStatement(element: PsiImportStatement) { 177 super.visitImportStatement(element) 178 if (element.resolve() == null) { 179 reporter.report( 180 Issues.UNRESOLVED_IMPORT, 181 element, 182 "Unresolved import: `${element.qualifiedName}`" 183 ) 184 } 185 } 186 }) 187 188 var classes = (psiFile as? PsiClassOwner)?.classes?.toList() ?: emptyList() 189 if (classes.isEmpty()) { 190 val uFile = UastFacade.convertElementWithParent(psiFile, UFile::class.java) as? UFile? 191 classes = uFile?.classes?.map { it }?.toList() ?: emptyList() 192 } 193 when { 194 classes.isEmpty() && psiFile is PsiJavaFile -> { 195 // package-info.java ? 196 val packageStatement = psiFile.packageStatement 197 // Look for javadoc on the package statement; this is NOT handed to us on 198 // the PsiPackage! 199 if (packageStatement != null) { 200 val comment = PsiTreeUtil.getPrevSiblingOfType( 201 packageStatement, 202 PsiDocComment::class.java 203 ) 204 if (comment != null) { 205 val packageName = packageStatement.packageName 206 val text = comment.text 207 if (text.contains("@hide")) { 208 this.hiddenPackages[packageName] = true 209 } 210 if (packageDocs[packageName] != null) { 211 reporter.report( 212 Issues.BOTH_PACKAGE_INFO_AND_HTML, 213 psiFile, 214 "It is illegal to provide both a package-info.java file and " + 215 "a package.html file for the same package" 216 ) 217 } 218 packageDocs[packageName] = text 219 } 220 } 221 } 222 else -> { 223 for (psiClass in classes) { 224 psiClass.accept(object : JavaRecursiveElementVisitor() { 225 override fun visitErrorElement(element: PsiErrorElement) { 226 super.visitErrorElement(element) 227 reporter.report( 228 Issues.INVALID_SYNTAX, 229 element, 230 "Syntax error: `${element.errorDescription}`" 231 ) 232 } 233 234 override fun visitCodeBlock(block: PsiCodeBlock?) { 235 // Ignore to avoid eagerly parsing all method bodies. 236 } 237 238 override fun visitDocComment(comment: PsiDocComment?) { 239 // Ignore to avoid eagerly parsing all doc comments. 240 // Doc comments cannot contain error elements. 241 } 242 }) 243 244 topLevelClassesFromSource += createClass(psiClass) 245 } 246 } 247 } 248 } 249 250 // Next construct packages 251 for ((pkgName, classes) in packageClasses) { 252 tick() // show progress 253 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 254 if (psiPackage == null) { 255 println("Could not find package $pkgName") 256 continue 257 } 258 259 val sortedClasses = classes.toMutableList().sortedWith(ClassItem.fullNameComparator) 260 registerPackage(psiPackage, sortedClasses, packageDocs[pkgName], pkgName) 261 } 262 initializing = false 263 264 emptyPackage = findPackage("")!! 265 266 // Finish initialization 267 val initialPackages = ArrayList(packageMap.values) 268 var registeredCount = packageMap.size // classes added after this point will have indices >= original 269 for (cls in initialPackages) { 270 cls.finishInitialization() 271 } 272 273 // Finish initialization of any additional classes that were registered during 274 // the above initialization (recursively) 275 while (registeredCount < packageMap.size) { 276 val added = packageMap.values.minus(initialPackages) 277 registeredCount = packageMap.size 278 for (pkg in added) { 279 pkg.finishInitialization() 280 } 281 } 282 283 // Point to "parent" packages, since doclava treats packages as nested (e.g. an @hide on 284 // android.foo will also apply to android.foo.bar) 285 addParentPackages(packageMap.values) 286 287 packageClasses.clear() // Not used after this point 288 } 289 290 override fun dispose() { 291 uastEnvironment.dispose() 292 super.dispose() 293 } 294 295 private fun addParentPackages(packages: Collection<PsiPackageItem>) { 296 val missingPackages = packages.mapNotNull { 297 val name = it.qualifiedName() 298 val index = name.lastIndexOf('.') 299 val parent = if (index != -1) { 300 name.substring(0, index) 301 } else { 302 "" 303 } 304 if (packageMap.containsKey(parent)) { 305 // Already registered 306 null 307 } else { 308 parent 309 } 310 }.toSet() 311 312 // Create PackageItems for any packages that weren't in the source 313 for (pkgName in missingPackages) { 314 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) ?: continue 315 val sortedClasses = emptyList<PsiClassItem>() 316 val packageHtml = null 317 registerPackage(psiPackage, sortedClasses, packageHtml, pkgName) 318 } 319 320 // Connect up all the package items 321 for (pkg in packageMap.values) { 322 var name = pkg.qualifiedName() 323 // Find parent package; we have to loop since we don't always find a PSI package 324 // for intermediate elements; e.g. we may jump from java.lang straight up to the default 325 // package 326 while (name.isNotEmpty()) { 327 val index = name.lastIndexOf('.') 328 name = if (index != -1) { 329 name.substring(0, index) 330 } else { 331 "" 332 } 333 val parent = findPackage(name) ?: continue 334 pkg.containingPackageField = parent 335 break 336 } 337 } 338 } 339 340 private fun registerPackage( 341 psiPackage: PsiPackage, 342 sortedClasses: List<PsiClassItem>?, 343 packageHtml: String?, 344 pkgName: String 345 ): PsiPackageItem { 346 val packageItem = PsiPackageItem 347 .create(this, psiPackage, packageHtml, fromClassPath = !initializing) 348 packageItem.emit = initializing 349 350 packageMap[pkgName] = packageItem 351 if (isPackageHidden(pkgName)) { 352 packageItem.hidden = true 353 } 354 355 sortedClasses?.let { packageItem.addClasses(it) } 356 return packageItem 357 } 358 359 fun initialize(uastEnvironment: UastEnvironment, jarFile: File, preFiltered: Boolean = false) { 360 this.preFiltered = preFiltered 361 initializing = true 362 hideClassesFromJars = false 363 364 this.uastEnvironment = uastEnvironment 365 366 // Find all classes referenced from the class 367 val facade = JavaPsiFacade.getInstance(project) 368 val scope = GlobalSearchScope.allScope(project) 369 370 hiddenPackages = HashMap(100) 371 packageMap = HashMap(PACKAGE_ESTIMATE) 372 packageClasses = HashMap(PACKAGE_ESTIMATE) 373 packageClasses[""] = ArrayList() 374 this.methodMap = HashMap(1000) 375 val packageToClasses: MutableMap<String, MutableList<PsiClassItem>> = HashMap( 376 PACKAGE_ESTIMATE 377 ) 378 packageToClasses[""] = ArrayList() // ensure we construct one for the default package 379 380 topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE) 381 382 try { 383 ZipFile(jarFile).use { jar -> 384 val enumeration = jar.entries() 385 while (enumeration.hasMoreElements()) { 386 val entry = enumeration.nextElement() 387 val fileName = entry.name 388 if (fileName.contains("$")) { 389 // skip inner classes 390 continue 391 } 392 if (fileName.endsWith(SdkConstants.DOT_CLASS)) { 393 val qualifiedName = fileName.removeSuffix(SdkConstants.DOT_CLASS).replace('/', '.') 394 if (qualifiedName.endsWith(".package-info")) { 395 // Ensure we register a package for this, even if empty 396 val packageName = qualifiedName.removeSuffix(".package-info") 397 var list = packageToClasses[packageName] 398 if (list == null) { 399 list = mutableListOf() 400 packageToClasses[packageName] = list 401 } 402 continue 403 } else { 404 val psiClass = facade.findClass(qualifiedName, scope) ?: continue 405 406 val classItem = createClass(psiClass) 407 topLevelClassesFromSource.add(classItem) 408 409 val packageName = getPackageName(psiClass) 410 var list = packageToClasses[packageName] 411 if (list == null) { 412 list = mutableListOf(classItem) 413 packageToClasses[packageName] = list 414 } else { 415 list.add(classItem) 416 } 417 } 418 } 419 } 420 } 421 } catch (e: IOException) { 422 reporter.report(Issues.IO_ERROR, jarFile, e.message ?: e.toString()) 423 } 424 425 // Next construct packages 426 for ((pkgName, packageClasses) in packageToClasses) { 427 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 428 if (psiPackage == null) { 429 println("Could not find package $pkgName") 430 continue 431 } 432 433 packageClasses.sortWith(ClassItem.fullNameComparator) 434 // TODO: How do we obtain the package docs? We generally don't have them, but it *would* be 435 // nice if we picked up "overview.html" bundled files and added them. But since the docs 436 // are generally missing for all elements *anyway*, let's not bother. 437 val docs = packageDocs?.packageDocs 438 val packageHtml: String? = 439 if (docs != null) { 440 docs[pkgName] 441 } else { 442 null 443 } 444 registerPackage(psiPackage, packageClasses, packageHtml, pkgName) 445 } 446 447 emptyPackage = findPackage("")!! 448 449 initializing = false 450 hideClassesFromJars = true 451 452 // Finish initialization 453 for (pkg in packageMap.values) { 454 pkg.finishInitialization() 455 } 456 457 packageClasses.clear() // Not used after this point 458 } 459 460 fun dumpStats() { 461 options.stdout.println( 462 "INTERNAL STATS: Size of classMap=${classMap.size} and size of " + 463 "methodMap=${methodMap.size} and size of packageMap=${packageMap.size}, and the " + 464 "size of packageClasses=${packageClasses.size} " 465 ) 466 } 467 468 private fun registerPackageClass(packageName: String, cls: PsiClassItem) { 469 var list = packageClasses[packageName] 470 if (list == null) { 471 list = ArrayList() 472 packageClasses[packageName] = list 473 } 474 475 list.add(cls) 476 } 477 478 private fun isPackageHidden(packageName: String): Boolean { 479 val hidden = hiddenPackages[packageName] 480 if (hidden == true) { 481 return true 482 } else if (hidden == null) { 483 // Compute for all prefixes of this package 484 var pkg = packageName 485 while (true) { 486 if (hiddenPackages[pkg] != null) { 487 hiddenPackages[packageName] = hiddenPackages[pkg] 488 if (hiddenPackages[pkg] == true) { 489 return true 490 } 491 } 492 val last = pkg.lastIndexOf('.') 493 if (last == -1) { 494 hiddenPackages[packageName] = false 495 break 496 } else { 497 pkg = pkg.substring(0, last) 498 } 499 } 500 } 501 502 return false 503 } 504 505 private fun createClass(clz: PsiClass): PsiClassItem { 506 // If initializing is true, this class is from source 507 val classItem = PsiClassItem.create(this, clz, fromClassPath = !initializing) 508 // Set emit to true for source classes but false for classpath classes 509 classItem.emit = initializing 510 511 if (!initializing) { 512 // Workaround: we're pulling in .aidl files from .jar files. These are 513 // marked @hide, but since we only see the .class files we don't know that. 514 if (classItem.simpleName().startsWith("I") && 515 classItem.isFromClassPath() && 516 clz.interfaces.any { it.qualifiedName == "android.os.IInterface" } 517 ) { 518 classItem.hidden = true 519 } 520 } 521 522 if (classItem.classType == ClassType.TYPE_PARAMETER) { 523 // Don't put PsiTypeParameter classes into the registry; e.g. when we're visiting 524 // java.util.stream.Stream<R> 525 // we come across "R" and would try to place it here. 526 classItem.containingPackage = emptyPackage 527 classItem.finishInitialization() 528 return classItem 529 } 530 531 // TODO: Cache for adjacent files! 532 val packageName = getPackageName(clz) 533 registerPackageClass(packageName, classItem) 534 535 if (!initializing) { 536 classItem.finishInitialization() 537 val pkgName = getPackageName(clz) 538 val pkg = findPackage(pkgName) 539 if (pkg == null) { 540 // val packageHtml: String? = packageDocs?.packageDocs!![pkgName] 541 // dynamically discovered packages should NOT be included 542 // val packageHtml = "/** @hide */" 543 val packageHtml = null 544 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 545 if (psiPackage != null) { 546 val packageItem = registerPackage(psiPackage, null, packageHtml, pkgName) 547 packageItem.addClass(classItem) 548 } 549 } else { 550 pkg.addClass(classItem) 551 } 552 } 553 554 return classItem 555 } 556 557 override fun getPackages(): PackageList { 558 // TODO: Sorting is probably not necessary here! 559 return PackageList(this, packageMap.values.toMutableList().sortedWith(PackageItem.comparator)) 560 } 561 562 override fun getPackageDocs(): PackageDocs? { 563 return packageDocs 564 } 565 566 override fun size(): Int { 567 return packageMap.size 568 } 569 570 override fun findPackage(pkgName: String): PsiPackageItem? { 571 return packageMap[pkgName] 572 } 573 574 override fun findClass(className: String): PsiClassItem? { 575 return classMap[className] 576 } 577 578 open fun findClass(psiClass: PsiClass): PsiClassItem? { 579 val qualifiedName: String = psiClass.qualifiedName ?: psiClass.name!! 580 return classMap[qualifiedName] 581 } 582 583 open fun findOrCreateClass(qualifiedName: String): PsiClassItem? { 584 val finder = JavaPsiFacade.getInstance(project) 585 val psiClass = finder.findClass(qualifiedName, GlobalSearchScope.allScope(project)) ?: return null 586 return findOrCreateClass(psiClass) 587 } 588 589 open fun findOrCreateClass(psiClass: PsiClass): PsiClassItem { 590 val existing = findClass(psiClass) 591 if (existing != null) { 592 return existing 593 } 594 595 var curr = psiClass.containingClass 596 if (curr != null && findClass(curr) == null) { 597 // Make sure we construct outer/top level classes first 598 if (findClass(curr) == null) { 599 while (true) { 600 val containing = curr?.containingClass 601 if (containing == null) { 602 break 603 } else { 604 curr = containing 605 } 606 } 607 curr!! 608 createClass(curr) // this will also create inner classes, which should now be in the map 609 val inner = findClass(psiClass) 610 inner!! // should be there now 611 return inner 612 } 613 } 614 615 return existing ?: return createClass(psiClass) 616 } 617 618 fun findClass(psiType: PsiType): PsiClassItem? { 619 if (psiType is PsiClassType) { 620 val cls = psiType.resolve() ?: return null 621 return findOrCreateClass(cls) 622 } else if (psiType is PsiArrayType) { 623 var componentType = psiType.componentType 624 // We repeatedly get the component type because the array may have multiple dimensions 625 while (componentType is PsiArrayType) { 626 componentType = componentType.componentType 627 } 628 if (componentType is PsiClassType) { 629 val cls = componentType.resolve() ?: return null 630 return findOrCreateClass(cls) 631 } 632 } 633 return null 634 } 635 636 fun getClassType(cls: PsiClass): PsiClassType = getFactory().createType(cls, PsiSubstitutor.EMPTY) 637 638 fun getComment(string: String, parent: PsiElement? = null): PsiDocComment = 639 getFactory().createDocCommentFromText(string, parent) 640 641 fun getType(psiType: PsiType): PsiTypeItem { 642 // Note: We do *not* cache these; it turns out that storing PsiType instances 643 // in a map is bad for performance; it has a very expensive equals operation 644 // for some type comparisons (and we sometimes end up with unexpected results, 645 // e.g. where we fetch an "equals" type from the map but its representation 646 // is slightly different than we intended 647 return PsiTypeItem.create(this, psiType) 648 } 649 650 fun getType(psiClass: PsiClass): PsiTypeItem { 651 return PsiTypeItem.create(this, getFactory().createType(psiClass)) 652 } 653 654 private fun getPackageName(clz: PsiClass): String { 655 var top: PsiClass? = clz 656 while (top?.containingClass != null) { 657 top = top.containingClass 658 } 659 top ?: return "" 660 661 val name = top.name 662 val fullName = top.qualifiedName ?: return "" 663 664 if (name == fullName) { 665 return "" 666 } 667 668 return fullName.substring(0, fullName.length - 1 - name!!.length) 669 } 670 671 fun findMethod(method: PsiMethod): PsiMethodItem { 672 val containingClass = method.containingClass 673 val cls = findOrCreateClass(containingClass!!) 674 675 // Ensure initialized/registered via [#registerMethods] 676 if (methodMap[cls] == null) { 677 val map = HashMap<PsiMethod, PsiMethodItem>(40) 678 registerMethods(cls.methods(), map) 679 registerMethods(cls.constructors(), map) 680 methodMap[cls] = map 681 } 682 683 val methods = methodMap[cls]!! 684 val methodItem = methods[method] 685 if (methodItem == null) { 686 // Probably switched psi classes (e.g. used source PsiClass in registry but 687 // found duplicate class in .jar library and we're now pointing to it; in that 688 // case, find the equivalent method by signature 689 val psiClass = cls.psiClass 690 val updatedMethod = psiClass.findMethodBySignature(method, true) 691 val result = methods[updatedMethod!!] 692 if (result == null) { 693 val extra = PsiMethodItem.create(this, cls, updatedMethod) 694 methods[method] = extra 695 methods[updatedMethod] = extra 696 if (!initializing) { 697 extra.finishInitialization() 698 } 699 700 return extra 701 } 702 return result 703 } 704 705 return methodItem 706 } 707 708 fun findField(field: PsiField): FieldItem? { 709 val containingClass = field.containingClass ?: return null 710 val cls = findOrCreateClass(containingClass) 711 return cls.findField(field.name) 712 } 713 714 private fun registerMethods(methods: List<MethodItem>, map: MutableMap<PsiMethod, PsiMethodItem>) { 715 for (method in methods) { 716 val psiMethod = (method as PsiMethodItem).psiMethod 717 map[psiMethod] = method 718 } 719 } 720 721 /** 722 * Returns a list of the top-level classes declared in the codebase's source (rather than on its 723 * classpath). 724 */ 725 fun getTopLevelClassesFromSource(): List<ClassItem> { 726 return topLevelClassesFromSource 727 } 728 729 fun createReferenceFromText(s: String, parent: PsiElement? = null): PsiJavaCodeReferenceElement = 730 getFactory().createReferenceFromText(s, parent) 731 732 fun createPsiMethod(s: String, parent: PsiElement? = null): PsiMethod = 733 getFactory().createMethodFromText(s, parent) 734 735 fun createConstructor(s: String, parent: PsiElement? = null): PsiMethod = 736 getFactory().createConstructor(s, parent) 737 738 fun createPsiType(s: String, parent: PsiElement? = null): PsiType = 739 getFactory().createTypeFromText(s, parent) 740 741 fun createPsiAnnotation(s: String, parent: PsiElement? = null): PsiAnnotation = 742 getFactory().createAnnotationFromText(s, parent) 743 744 fun createDocTagFromText(s: String): PsiDocTag = getFactory().createDocTagFromText(s) 745 746 private fun getFactory() = JavaPsiFacade.getElementFactory(project) 747 748 private var nonNullAnnotationProvider: TypeAnnotationProvider? = null 749 private var nullableAnnotationProvider: TypeAnnotationProvider? = null 750 751 /** Type annotation provider which provides androidx.annotation.NonNull */ 752 fun getNonNullAnnotationProvider(): TypeAnnotationProvider { 753 return nonNullAnnotationProvider ?: run { 754 val provider = TypeAnnotationProvider.Static.create(arrayOf(createPsiAnnotation("@$ANDROIDX_NONNULL"))) 755 nonNullAnnotationProvider 756 provider 757 } 758 } 759 760 /** Type annotation provider which provides androidx.annotation.Nullable */ 761 fun getNullableAnnotationProvider(): TypeAnnotationProvider { 762 return nullableAnnotationProvider ?: run { 763 val provider = TypeAnnotationProvider.Static.create(arrayOf(createPsiAnnotation("@$ANDROIDX_NULLABLE"))) 764 nullableAnnotationProvider 765 provider 766 } 767 } 768 769 override fun createAnnotation( 770 source: String, 771 context: Item?, 772 mapName: Boolean 773 ): PsiAnnotationItem { 774 val psiAnnotation = createPsiAnnotation(source, context?.psi()) 775 return PsiAnnotationItem.create(this, psiAnnotation) 776 } 777 778 override fun supportsDocumentation(): Boolean = true 779 780 override fun toString(): String = description 781 782 /** Add a class to the codebase. Called from [createClass] and [PsiClassItem.create]. */ 783 internal fun registerClass(cls: PsiClassItem) { 784 classMap[cls.qualifiedName()] = cls 785 } 786 787 /** Get a Kotlin [BindingContext] at [element] 788 * 789 * Do not cache returned binding context for longer than the lifetime of this codebase 790 */ 791 fun bindingContext(element: KtElement): BindingContext { 792 return checkNotNull(project.getService(KotlinUastResolveProviderService::class.java)) 793 .getBindingContext(element) 794 } 795 } 796