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 18 19 import com.android.tools.metalava.model.TypeItem.Companion.equals 20 import java.util.Objects 21 import java.util.function.Predicate 22 23 /** 24 * Whether metalava supports type use annotations. Note that you can't just turn this flag back on; 25 * you have to also add TYPE_USE back to the handful of nullness annotations in 26 * stub-annotations/src/main/java/. 27 */ 28 const val SUPPORT_TYPE_USE_ANNOTATIONS = false 29 30 /** 31 * Represents a {@link https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Type.html Type} 32 */ 33 @MetalavaApi 34 interface TypeItem { 35 /** Modifiers for the type. Contains type-use annotation information. */ 36 val modifiers: TypeModifiers 37 38 fun accept(visitor: TypeVisitor) 39 40 fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) 41 42 /** 43 * Whether this type is equal to [other], not considering modifiers. 44 * 45 * This is implemented on each sub-interface of [TypeItem] instead of [equals] because 46 * interfaces are not allowed to implement [equals]. An [equals] implementation is provided by 47 * [DefaultTypeItem]. 48 */ 49 fun equalToType(other: TypeItem?): Boolean 50 51 /** 52 * Hashcode for the type. 53 * 54 * This is implemented on each sub-interface of [TypeItem] instead of [hashCode] because 55 * interfaces are not allowed to implement [hashCode]. A [hashCode] implementation is provided 56 * by [DefaultTypeItem]. 57 */ 58 fun hashCodeForType(): Int 59 60 /** 61 * Provide a helpful description of the type, for use in error messages. 62 * 63 * This is not suitable for use in signature or stubs as while it defaults to [toTypeString] for 64 * most types it is overridden by others to provide additional information. 65 */ 66 fun description(): String = toTypeString() 67 68 /** 69 * Generates a string for this type. 70 * 71 * @param annotations For a type like this: @Nullable java.util.List<@NonNull java.lang.String>, 72 * [annotations] controls whether the annotations like @Nullable and @NonNull are included. 73 * @param kotlinStyleNulls Controls whether it should return "@Nullable List<String>" as 74 * "List<String!>?". 75 * @param filter Specifies a filter to apply to the type annotations, if any. 76 * @param spaceBetweenParameters Controls whether there should be a space between class type 77 * parameters, e.g. "java.util.Map<java.lang.Integer, java.lang.Number>" or 78 * "java.util.Map<java.lang.Integer,java.lang.Number>". 79 */ 80 fun toTypeString( 81 annotations: Boolean = false, 82 kotlinStyleNulls: Boolean = false, 83 filter: Predicate<Item>? = null, 84 spaceBetweenParameters: Boolean = false 85 ): String 86 87 /** 88 * Get a string representation of the erased type. 89 * 90 * Implements the behavior described 91 * [here](https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html). 92 * 93 * One point to note is that vararg parameters are represented using standard array syntax, i.e. 94 * `[]`, not the special source `...` syntax. The reason for that is that the erased type is 95 * mainly used at runtime which treats a vararg parameter as a standard array type. 96 */ 97 @MetalavaApi fun toErasedTypeString(): String 98 99 /** Returns the internal name of the type, as seen in bytecode. */ 100 fun internalName(): String 101 102 fun asClass(): ClassItem? 103 104 fun toSimpleType(): String { 105 return stripJavaLangPrefix(toTypeString()) 106 } 107 108 /** 109 * Helper methods to compare types, especially types from signature files with types from 110 * parsing, which may have slightly different formats, e.g. varargs ("...") versus arrays 111 * ("[]"), java.lang. prefixes removed in wildcard signatures, etc. 112 */ 113 fun toCanonicalType(): String { 114 var s = toTypeString() 115 while (s.contains(JAVA_LANG_PREFIX)) { 116 s = s.replace(JAVA_LANG_PREFIX, "") 117 } 118 if (s.contains("...")) { 119 s = s.replace("...", "[]") 120 } 121 122 return s 123 } 124 125 /** 126 * Makes substitutions to the type based on the [typeParameterBindings]. For instance, if the 127 * [typeParameterBindings] contains `{T -> String}`, calling this method on `T` would return 128 * `String`, and calling it on `List<T>` would return `List<String>` (in both cases the 129 * modifiers on the `String` will be independently mutable from the `String` in the 130 * [typeParameterBindings]). Calling it on an unrelated type like `int` would return a duplicate 131 * of that type. 132 * 133 * This method is intended to be used in conjunction with [ClassItem.mapTypeVariables], 134 */ 135 fun convertType(typeParameterBindings: TypeParameterBindings): TypeItem 136 137 fun convertType(from: ClassItem, to: ClassItem): TypeItem { 138 val map = from.mapTypeVariables(to) 139 if (map.isNotEmpty()) { 140 return convertType(map) 141 } 142 143 return this 144 } 145 146 /** Returns `true` if `this` type can be assigned from `other` without unboxing the other. */ 147 fun isAssignableFromWithoutUnboxing(other: TypeItem): Boolean { 148 // Limited text based check 149 if (this == other) return true 150 val bounds = 151 (other as? VariableTypeItem)?.asTypeParameter?.typeBounds()?.map { it.toTypeString() } 152 ?: emptyList() 153 return bounds.contains(toTypeString()) 154 } 155 156 fun isJavaLangObject(): Boolean = false 157 158 fun isString(): Boolean = false 159 160 fun defaultValue(): Any? = null 161 162 fun defaultValueString(): String = "null" 163 164 /** Creates an identical type, with a copy of this type's modifiers, so they can be mutated. */ 165 fun duplicate(): TypeItem 166 167 /** 168 * Creates an identical type, with a copy of this type's modifiers with the specified 169 * [withNullability] that can be modified further if needed. 170 */ 171 fun duplicate(withNullability: TypeNullability) = 172 duplicate().apply { modifiers.setNullability(withNullability) } 173 174 companion object { 175 /** Shortens types, if configured */ 176 fun shortenTypes(type: String): String { 177 var cleaned = type 178 if (cleaned.contains("@androidx.annotation.")) { 179 cleaned = cleaned.replace("@androidx.annotation.", "@") 180 } 181 return stripJavaLangPrefix(cleaned) 182 } 183 184 /** 185 * Removes java.lang. prefixes from types, unless it's in a subpackage such as 186 * java.lang.reflect. For simplicity we may also leave inner classes in the java.lang 187 * package untouched. 188 * 189 * NOTE: We only remove this from the front of the type; e.g. we'll replace 190 * java.lang.Class<java.lang.String> with Class<java.lang.String>. This is because the 191 * signature parsing of types is not 100% accurate and we don't want to run into trouble 192 * with more complicated generic type signatures where we end up not mapping the simplified 193 * types back to the real fully qualified type names. 194 */ 195 fun stripJavaLangPrefix(type: String): String { 196 if (type.startsWith(JAVA_LANG_PREFIX)) { 197 // Replacing java.lang is harder, since we don't want to operate in sub packages, 198 // e.g. java.lang.String -> String, but java.lang.reflect.Method -> unchanged 199 val start = JAVA_LANG_PREFIX.length 200 val end = type.length 201 for (index in start until end) { 202 if (type[index] == '<') { 203 return type.substring(start) 204 } else if (type[index] == '.') { 205 return type 206 } 207 } 208 209 return type.substring(start) 210 } 211 212 return type 213 } 214 215 /** 216 * Create a [Comparator] that when given two [TypeItem] will treat them as equal if either 217 * returns `null` from [TypeItem.asClass] and will otherwise compare the two [ClassItem]s 218 * using [comparator]. 219 * 220 * This only defines a partial ordering over [TypeItem]. 221 */ 222 private fun typeItemAsClassComparator( 223 comparator: Comparator<ClassItem> 224 ): Comparator<TypeItem> { 225 return Comparator { type1, type2 -> 226 val cls1 = type1.asClass() 227 val cls2 = type2.asClass() 228 if (cls1 != null && cls2 != null) { 229 comparator.compare(cls1, cls2) 230 } else { 231 0 232 } 233 } 234 } 235 236 /** A total ordering over [TypeItem] comparing [TypeItem.toTypeString]. */ 237 private val typeStringComparator = 238 Comparator.comparing<TypeItem, String> { it.toTypeString() } 239 240 /** 241 * A total ordering over [TypeItem] comparing [TypeItem.asClass] using 242 * [ClassItem.fullNameThenQualifierComparator] and then comparing [TypeItem.toTypeString]. 243 */ 244 val totalComparator: Comparator<TypeItem> = 245 typeItemAsClassComparator(ClassItem.fullNameThenQualifierComparator) 246 .thenComparing(typeStringComparator) 247 248 @Deprecated( 249 "" + 250 "this should not be used as it only defines a partial ordering which means that the " + 251 "source order will affect the result" 252 ) 253 val partialComparator: Comparator<TypeItem> = Comparator { type1, type2 -> 254 val cls1 = type1.asClass() 255 val cls2 = type2.asClass() 256 if (cls1 != null && cls2 != null) { 257 ClassItem.fullNameComparator.compare(cls1, cls2) 258 } else { 259 type1.toTypeString().compareTo(type2.toTypeString()) 260 } 261 } 262 263 /** 264 * Convert a type string containing to its lambda representation or return the original. 265 * 266 * E.g.: `"kotlin.jvm.functions.Function1<Integer, String>"` to `"(Integer) -> String"`. 267 */ 268 fun toLambdaFormat(typeName: String): String { 269 // Bail if this isn't a Kotlin function type 270 if (!typeName.startsWith(KOTLIN_FUNCTION_PREFIX)) { 271 return typeName 272 } 273 274 // Find the first character after the first opening angle bracket. This will either be 275 // the first character of the paramTypes of the lambda if it has parameters. 276 val paramTypesStart = 277 typeName.indexOf('<', startIndex = KOTLIN_FUNCTION_PREFIX.length) + 1 278 279 // The last type param is always the return type. We find and set these boundaries with 280 // the push down loop below. 281 var paramTypesEnd = -1 282 var returnTypeStart = -1 283 284 // Get the exclusive end of the return type parameter by finding the last closing 285 // angle bracket. 286 val returnTypeEnd = typeName.lastIndexOf('>') 287 288 // Bail if an an unexpected format broke the indexOf's above. 289 if (paramTypesStart <= 0 || paramTypesStart >= returnTypeEnd) { 290 return typeName 291 } 292 293 // This loop looks for the last comma that is not inside the type parameters of a type 294 // parameter. It's a simple push down state machine that stores its depth as a counter 295 // instead of a stack. It runs backwards from the last character of the type parameters 296 // just before the last closing angle bracket to the beginning just before the first 297 // opening angle bracket. 298 var depth = 0 299 for (i in returnTypeEnd - 1 downTo paramTypesStart) { 300 val c = typeName[i] 301 302 // Increase or decrease stack depth on angle brackets 303 when (c) { 304 '>' -> depth++ 305 '<' -> depth-- 306 } 307 308 when { 309 depth == 0 -> 310 when { // At the top level 311 c == ',' -> { 312 // When top level comma is found, mark it as the exclusive end of 313 // the 314 // parameter types and end the loop 315 paramTypesEnd = i 316 break 317 } 318 !c.isWhitespace() -> { 319 // Keep moving the start of the return type back until whitespace 320 returnTypeStart = i 321 } 322 } 323 depth < 0 -> return typeName // Bail, unbalanced nesting 324 } 325 } 326 327 // Bail if some sort of unbalanced nesting occurred or the indices around the comma 328 // appear grossly incorrect. 329 if (depth > 0 || returnTypeStart < 0 || returnTypeStart <= paramTypesEnd) { 330 return typeName 331 } 332 333 return buildString(typeName.length) { 334 append("(") 335 336 // Slice param types, if any, and append them between the parenthesis 337 if (paramTypesEnd > 0) { 338 append(typeName, paramTypesStart, paramTypesEnd) 339 } 340 341 append(") -> ") 342 343 // Slice out the return type param and append it after the arrow 344 append(typeName, returnTypeStart, returnTypeEnd) 345 } 346 } 347 348 /** Prefix of Kotlin JVM function types, used for lambdas. */ 349 private const val KOTLIN_FUNCTION_PREFIX = "kotlin.jvm.functions.Function" 350 } 351 } 352 353 /** 354 * A mapping from one class's type parameters to the types provided for those type parameters in a 355 * possibly indirect subclass. 356 * 357 * e.g. Given `Map<K, V>` and a subinterface `StringToIntMap extends Map<String, Integer>` then this 358 * would contain a mapping from `K -> String` and `V -> Integer`. 359 * 360 * Although a `ClassTypeItem`'s arguments can be `WildcardTypeItem`s as well as 361 * `ReferenceTypeItem`s, a `ClassTypeItem` used in an extends or implements list cannot have a 362 * `WildcardTypeItem` as an argument so this cast is safe. See 363 * https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-Superclass 364 */ 365 typealias TypeParameterBindings = Map<TypeParameterItem, ReferenceTypeItem> 366 367 abstract class DefaultTypeItem( 368 final override val modifiers: TypeModifiers, 369 ) : TypeItem { 370 371 private lateinit var cachedDefaultType: String 372 private lateinit var cachedErasedType: String 373 toStringnull374 override fun toString(): String = toTypeString() 375 376 override fun toTypeString( 377 annotations: Boolean, 378 kotlinStyleNulls: Boolean, 379 filter: Predicate<Item>?, 380 spaceBetweenParameters: Boolean 381 ): String { 382 return toTypeString( 383 TypeStringConfiguration(annotations, kotlinStyleNulls, filter, spaceBetweenParameters) 384 ) 385 } 386 toTypeStringnull387 private fun toTypeString(configuration: TypeStringConfiguration): String { 388 // Cache the default type string. Other configurations are less likely to be reused. 389 return if (configuration.isDefault) { 390 if (!::cachedDefaultType.isInitialized) { 391 cachedDefaultType = buildString { 392 appendTypeString(this@DefaultTypeItem, configuration) 393 } 394 } 395 cachedDefaultType 396 } else { 397 buildString { appendTypeString(this@DefaultTypeItem, configuration) } 398 } 399 } 400 toErasedTypeStringnull401 override fun toErasedTypeString(): String { 402 if (!::cachedErasedType.isInitialized) { 403 cachedErasedType = buildString { appendErasedTypeString(this@DefaultTypeItem) } 404 } 405 return cachedErasedType 406 } 407 internalNamenull408 override fun internalName(): String { 409 // Default implementation; PSI subclass is more accurate 410 return toSlashFormat(toErasedTypeString()) 411 } 412 equalsnull413 override fun equals(other: Any?): Boolean { 414 if (other !is TypeItem) return false 415 return equalToType(other) 416 } 417 hashCodenull418 override fun hashCode(): Int = hashCodeForType() 419 420 companion object { 421 /** 422 * Configuration options for how to represent a type as a string. 423 * 424 * @param annotations Whether to include annotations on the type. 425 * @param kotlinStyleNulls Whether to represent nullability with Kotlin-style suffixes: `?` 426 * for nullable, no suffix for non-null, and `!` for platform nullability. For example, 427 * the Java type `@Nullable List<String>` would be represented as `List<String!>?`. 428 * @param filter A filter to apply to the type annotations, if any. 429 * @param spaceBetweenParameters Whether to include a space between class type params. 430 */ 431 private data class TypeStringConfiguration( 432 val annotations: Boolean = false, 433 val kotlinStyleNulls: Boolean = false, 434 val filter: Predicate<Item>? = null, 435 val spaceBetweenParameters: Boolean = false, 436 ) { 437 val isDefault = 438 !annotations && !kotlinStyleNulls && filter == null && !spaceBetweenParameters 439 } 440 441 private fun StringBuilder.appendTypeString( 442 type: TypeItem, 443 configuration: TypeStringConfiguration 444 ) { 445 when (type) { 446 is PrimitiveTypeItem -> { 447 if (configuration.annotations) { 448 appendAnnotations(type.modifiers, configuration) 449 } 450 append(type.kind.primitiveName) 451 // Primitives must be non-null. 452 } 453 is ArrayTypeItem -> { 454 // The ordering of array annotations means this can't just use a recursive 455 // approach for annotated multi-dimensional arrays, but it can if annotations 456 // aren't included. 457 if (configuration.annotations) { 458 var deepComponentType = type.componentType 459 val arrayModifiers = mutableListOf(type.modifiers) 460 while (deepComponentType is ArrayTypeItem) { 461 arrayModifiers.add(deepComponentType.modifiers) 462 deepComponentType = deepComponentType.componentType 463 } 464 val suffixes = arrayModifiers.map { it.nullability().suffix }.reversed() 465 466 // Print the innermost component type. 467 appendTypeString(deepComponentType, configuration) 468 469 // Print modifiers from the outermost array type in, and the array suffixes. 470 arrayModifiers.zip(suffixes).forEachIndexed { index, (modifiers, suffix) -> 471 appendAnnotations(modifiers, configuration, leadingSpace = true) 472 // Only the outermost array can be varargs. 473 if (index < arrayModifiers.size - 1 || !type.isVarargs) { 474 append("[]") 475 } else { 476 append("...") 477 } 478 if (configuration.kotlinStyleNulls) { 479 append(suffix) 480 } 481 } 482 } else { 483 // Non-annotated case: just recur to the component 484 appendTypeString(type.componentType, configuration) 485 if (type.isVarargs) { 486 append("...") 487 } else { 488 append("[]") 489 } 490 if (configuration.kotlinStyleNulls) { 491 append(type.modifiers.nullability().suffix) 492 } 493 } 494 } 495 is ClassTypeItem -> { 496 if (type.outerClassType != null) { 497 appendTypeString(type.outerClassType!!, configuration) 498 append('.') 499 if (configuration.annotations) { 500 appendAnnotations(type.modifiers, configuration) 501 } 502 append(type.className) 503 } else { 504 if (configuration.annotations) { 505 append(type.qualifiedName.substringBeforeLast(type.className)) 506 appendAnnotations(type.modifiers, configuration) 507 append(type.className) 508 } else { 509 append(type.qualifiedName) 510 } 511 } 512 513 if (type.arguments.isNotEmpty()) { 514 append("<") 515 type.arguments.forEachIndexed { index, parameter -> 516 appendTypeString(parameter, configuration) 517 if (index != type.arguments.size - 1) { 518 append(",") 519 if (configuration.spaceBetweenParameters) { 520 append(" ") 521 } 522 } 523 } 524 append(">") 525 } 526 if (configuration.kotlinStyleNulls) { 527 append(type.modifiers.nullability().suffix) 528 } 529 } 530 is VariableTypeItem -> { 531 if (configuration.annotations) { 532 appendAnnotations(type.modifiers, configuration) 533 } 534 append(type.name) 535 if (configuration.kotlinStyleNulls) { 536 append(type.modifiers.nullability().suffix) 537 } 538 } 539 is WildcardTypeItem -> { 540 if (configuration.annotations) { 541 appendAnnotations(type.modifiers, configuration) 542 } 543 append("?") 544 545 type.superBound?.let { 546 append(" super ") 547 appendTypeString(it, configuration) 548 // If there's a super bound, don't also print an object extends bound. 549 return 550 } 551 552 type.extendsBound?.let { 553 if (shouldIncludeExtendsBound(it, configuration)) { 554 append(" extends ") 555 appendTypeString(it, configuration) 556 } 557 } 558 559 // It doesn't make sense to have a nullness suffix on a wildcard, this should be 560 // handled by the bound. 561 } 562 } 563 } 564 565 /** 566 * Returns whether the [extendsBound] should be included in the type string based on the 567 * [configuration]. 568 */ 569 private fun shouldIncludeExtendsBound( 570 extendsBound: ReferenceTypeItem, 571 configuration: TypeStringConfiguration 572 ): Boolean { 573 // Non-object bounds should always be included. 574 if (!extendsBound.isJavaLangObject()) return true 575 576 // If the bound is Object, it should only be included when the nullability isn't implied 577 // by the configuration. If both kotlinStyleNulls and annotations are false, no 578 // nullability information is included anyway. 579 if (!configuration.kotlinStyleNulls && !configuration.annotations) return false 580 581 // When nullability information is included, excluded bounds imply non-null when 582 // kotlinStyleNulls is true and platform when it is false. 583 val nullability = extendsBound.modifiers.nullability() 584 if (configuration.kotlinStyleNulls && nullability == TypeNullability.NONNULL) 585 return false 586 if (!configuration.kotlinStyleNulls && nullability == TypeNullability.PLATFORM) 587 return false 588 return true 589 } 590 591 private fun StringBuilder.appendAnnotations( 592 modifiers: TypeModifiers, 593 configuration: TypeStringConfiguration, 594 leadingSpace: Boolean = false, 595 trailingSpace: Boolean = true 596 ) { 597 val annotations = 598 modifiers.annotations().filter { annotation -> 599 // If Kotlin-style nulls are printed, nullness annotations shouldn't be. 600 if (configuration.kotlinStyleNulls && annotation.isNullnessAnnotation()) { 601 return@filter false 602 } 603 604 val filter = configuration.filter ?: return@filter true 605 val qualifiedName = annotation.qualifiedName ?: return@filter true 606 val annotationClass = 607 annotation.codebase.findClass(qualifiedName) ?: return@filter true 608 filter.test(annotationClass) 609 } 610 if (annotations.isEmpty()) return 611 612 if (leadingSpace) { 613 append(' ') 614 } 615 annotations.forEachIndexed { index, annotation -> 616 append(annotation.toSource()) 617 if (index != annotations.size - 1) { 618 append(' ') 619 } 620 } 621 if (trailingSpace) { 622 append(' ') 623 } 624 } 625 626 private fun StringBuilder.appendErasedTypeString(type: TypeItem) { 627 when (type) { 628 is PrimitiveTypeItem -> append(type.kind.primitiveName) 629 is ArrayTypeItem -> { 630 appendErasedTypeString(type.componentType) 631 append("[]") 632 } 633 is ClassTypeItem -> append(type.qualifiedName) 634 is VariableTypeItem -> 635 type.asTypeParameter.asErasedType()?.let { appendErasedTypeString(it) } 636 ?: append(JAVA_LANG_OBJECT) 637 else -> 638 throw IllegalStateException( 639 "should never visit $type of type ${type.javaClass} while generating erased type string" 640 ) 641 } 642 } 643 644 // Copied from doclava1 645 private fun toSlashFormat(typeName: String): String { 646 var name = typeName 647 var dimension = "" 648 while (name.endsWith("[]")) { 649 dimension += "[" 650 name = name.substring(0, name.length - 2) 651 } 652 653 val base: String 654 base = 655 when (name) { 656 "void" -> "V" 657 "byte" -> "B" 658 "boolean" -> "Z" 659 "char" -> "C" 660 "short" -> "S" 661 "int" -> "I" 662 "long" -> "J" 663 "float" -> "F" 664 "double" -> "D" 665 else -> "L" + getInternalName(name) + ";" 666 } 667 668 return dimension + base 669 } 670 671 /** 672 * Computes the internal class name of the given fully qualified class name. For example, it 673 * converts foo.bar.Foo.Bar into foo/bar/Foo$Bar 674 * 675 * @param qualifiedName the fully qualified class name 676 * @return the internal class name 677 */ 678 private fun getInternalName(qualifiedName: String): String { 679 if (qualifiedName.indexOf('.') == -1) { 680 return qualifiedName 681 } 682 683 // If class name contains $, it's not an ambiguous inner class name. 684 if (qualifiedName.indexOf('$') != -1) { 685 return qualifiedName.replace('.', '/') 686 } 687 // Let's assume that components that start with Caps are class names. 688 return buildString { 689 var prev: String? = null 690 for (part in qualifiedName.split(".")) { 691 if (!prev.isNullOrEmpty()) { 692 if (Character.isUpperCase(prev[0])) { 693 append('$') 694 } else { 695 append('/') 696 } 697 } 698 append(part) 699 prev = part 700 } 701 } 702 } 703 } 704 } 705 706 /** 707 * The type for [ClassTypeItem.arguments]. 708 * 709 * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeArgument. 710 */ 711 interface TypeArgumentTypeItem : TypeItem { 712 /** Override to specialize the return type. */ convertTypenull713 override fun convertType(typeParameterBindings: TypeParameterBindings): TypeArgumentTypeItem 714 715 /** Override to specialize the return type. */ 716 override fun duplicate(): TypeArgumentTypeItem 717 } 718 719 /** 720 * The type for a reference. 721 * 722 * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-ReferenceType. 723 */ 724 interface ReferenceTypeItem : TypeItem, TypeArgumentTypeItem { 725 /** Override to specialize the return type. */ 726 override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem 727 728 /** Override to specialize the return type. */ 729 override fun duplicate(): ReferenceTypeItem 730 } 731 732 /** 733 * The type of [TypeParameterItem]'s type bounds. 734 * 735 * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeBound 736 */ 737 interface BoundsTypeItem : TypeItem, ReferenceTypeItem 738 739 /** 740 * The type of [MethodItem.throwsTypes]'s. 741 * 742 * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-ExceptionType. 743 */ 744 sealed interface ExceptionTypeItem : TypeItem, ReferenceTypeItem { 745 /** 746 * Get the erased [ClassItem], if any. 747 * 748 * The erased [ClassItem] is the one which would be used by Java at runtime after the generic 749 * types have been erased. This will cause an error if it is called on a [VariableTypeItem] 750 * whose [TypeParameterItem]'s upper bound is not a [ExceptionTypeItem]. However, that should 751 * never happen as it would be a compile time error. 752 */ 753 val erasedClass: ClassItem? 754 755 /** 756 * The best guess of the full name, i.e. the qualified class name without the package but 757 * including the outer class names. 758 * 759 * This is not something that can be accurately determined solely by examining the reference or 760 * even the import as there is no distinction made between a package name and a class name. Java 761 * naming conventions do say that package names should start with a lower case letter and class 762 * names should start with an upper case letter, but they are not enforced so cannot be fully 763 * relied upon. 764 * 765 * It is possible that in some contexts a model could provide a better full name than guessing 766 * from the fully qualified name, e.g. a reference within the same package, however that is not 767 * something that will be supported by all models and so attempting to use that could lead to 768 * subtle model differences that could break users of the models. 769 * 770 * The only way to fully determine the full name is to resolve the class and extract it from 771 * there but this avoids resolving a class as it can be expensive. Instead, this just makes the 772 * best guess assuming normal Java conventions. 773 */ 774 @Deprecated( 775 "Do not use as full name is only ever a best guess based on naming conventions; use the full type string instead", 776 ReplaceWith("toTypeString()") 777 ) fullNamenull778 fun fullName(): String = bestGuessAtFullName(toTypeString()) 779 780 companion object { 781 /** A partial ordering over [ExceptionTypeItem] comparing [ExceptionTypeItem] full names. */ 782 val fullNameComparator: Comparator<ExceptionTypeItem> = 783 Comparator.comparing { @Suppress("DEPRECATION") it.fullName() } 784 } 785 } 786 787 /** Represents a primitive type, like int or boolean. */ 788 interface PrimitiveTypeItem : TypeItem { 789 /** The kind of [Primitive] this type is. */ 790 val kind: Primitive 791 792 /** The possible kinds of primitives. */ 793 enum class Primitive( 794 val primitiveName: String, 795 val defaultValue: Any?, 796 val defaultValueString: String 797 ) { 798 BOOLEAN("boolean", false, "false"), 799 BYTE("byte", 0.toByte(), "0"), 800 CHAR("char", 0.toChar(), "0"), 801 DOUBLE("double", 0.0, "0"), 802 FLOAT("float", 0F, "0"), 803 INT("int", 0, "0"), 804 LONG("long", 0L, "0"), 805 SHORT("short", 0.toShort(), "0"), 806 VOID("void", null, "null") 807 } 808 defaultValuenull809 override fun defaultValue(): Any? = kind.defaultValue 810 811 override fun defaultValueString(): String = kind.defaultValueString 812 813 override fun accept(visitor: TypeVisitor) { 814 visitor.visit(this) 815 } 816 acceptnull817 override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) { 818 visitor.visit(this, other) 819 } 820 duplicatenull821 override fun duplicate(): PrimitiveTypeItem 822 823 override fun convertType(typeParameterBindings: TypeParameterBindings): PrimitiveTypeItem { 824 return duplicate() 825 } 826 equalToTypenull827 override fun equalToType(other: TypeItem?): Boolean { 828 return (other as? PrimitiveTypeItem)?.kind == kind 829 } 830 hashCodeForTypenull831 override fun hashCodeForType(): Int = kind.hashCode() 832 833 override fun asClass(): ClassItem? = null 834 } 835 836 /** Represents an array type, including vararg types. */ 837 interface ArrayTypeItem : TypeItem, ReferenceTypeItem { 838 /** The array's inner type (which for multidimensional arrays is another array type). */ 839 val componentType: TypeItem 840 841 /** Whether this array type represents a varargs parameter. */ 842 val isVarargs: Boolean 843 844 override fun accept(visitor: TypeVisitor) { 845 visitor.visit(this) 846 } 847 848 override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) { 849 visitor.visit(this, other) 850 } 851 852 override fun duplicate(): ArrayTypeItem = duplicate(componentType.duplicate()) 853 854 /** 855 * Duplicates this type (including duplicating the modifiers so they can be independently 856 * mutated), but substituting in the provided [componentType] in place of this type's component. 857 */ 858 fun duplicate(componentType: TypeItem): ArrayTypeItem 859 860 override fun convertType(typeParameterBindings: TypeParameterBindings): ArrayTypeItem { 861 return duplicate(componentType.convertType(typeParameterBindings)) 862 } 863 864 override fun equalToType(other: TypeItem?): Boolean { 865 if (other !is ArrayTypeItem) return false 866 return isVarargs == other.isVarargs && componentType.equalToType(other.componentType) 867 } 868 869 override fun hashCodeForType(): Int = Objects.hash(isVarargs, componentType) 870 871 override fun asClass(): ClassItem? = componentType.asClass() 872 } 873 874 /** Represents a class type. */ 875 interface ClassTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem { 876 /** The qualified name of this class, e.g. "java.lang.String". */ 877 val qualifiedName: String 878 879 /** 880 * The class type's arguments, empty if it has none. 881 * 882 * i.e. The specific types that this class type assigns to each of the referenced [ClassItem]'s 883 * type parameters. 884 */ 885 val arguments: List<TypeArgumentTypeItem> 886 887 /** The outer class type of this class, if it is an inner type. */ 888 val outerClassType: ClassTypeItem? 889 890 /** 891 * The name of the class, e.g. "String" for "java.lang.String" and "Inner" for 892 * "test.pkg.Outer.Inner". 893 */ 894 val className: String 895 896 override val erasedClass: ClassItem? 897 get() = asClass() 898 acceptnull899 override fun accept(visitor: TypeVisitor) { 900 visitor.visit(this) 901 } 902 acceptnull903 override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) { 904 visitor.visit(this, other) 905 } 906 907 /** 908 * Check to see whether this type has any type arguments. 909 * 910 * It will return `true` for say `List<T>`, but `false` for `String`. 911 */ hasTypeArgumentsnull912 fun hasTypeArguments() = arguments.isNotEmpty() 913 914 override fun isString(): Boolean = qualifiedName == JAVA_LANG_STRING 915 916 override fun isJavaLangObject(): Boolean = qualifiedName == JAVA_LANG_OBJECT 917 918 override fun duplicate(): ClassTypeItem = 919 duplicate(outerClassType?.duplicate(), arguments.map { it.duplicate() }) 920 921 /** 922 * Duplicates this type (including duplicating the modifiers, so they can be independently 923 * mutated), but substituting in the provided [outerClass] and [arguments] in place of this 924 * instance's [outerClass] and [arguments]. 925 */ duplicatenull926 fun duplicate(outerClass: ClassTypeItem?, arguments: List<TypeArgumentTypeItem>): ClassTypeItem 927 928 override fun convertType(typeParameterBindings: TypeParameterBindings): ClassTypeItem { 929 return duplicate( 930 outerClassType?.convertType(typeParameterBindings), 931 arguments.map { it.convertType(typeParameterBindings) } 932 ) 933 } 934 equalToTypenull935 override fun equalToType(other: TypeItem?): Boolean { 936 if (other !is ClassTypeItem) return false 937 return qualifiedName == other.qualifiedName && 938 arguments.size == other.arguments.size && 939 arguments.zip(other.arguments).all { (p1, p2) -> p1.equalToType(p2) } && 940 ((outerClassType == null && other.outerClassType == null) || 941 outerClassType?.equalToType(other.outerClassType) == true) 942 } 943 hashCodeForTypenull944 override fun hashCodeForType(): Int = Objects.hash(qualifiedName, outerClassType, arguments) 945 946 companion object { 947 /** Computes the simple name of a class from a qualified class name. */ 948 fun computeClassName(qualifiedName: String): String { 949 val lastDotIndex = qualifiedName.lastIndexOf('.') 950 return if (lastDotIndex == -1) { 951 qualifiedName 952 } else { 953 qualifiedName.substring(lastDotIndex + 1) 954 } 955 } 956 } 957 } 958 959 /** 960 * Represents a kotlin lambda type. 961 * 962 * This extends [ClassTypeItem] out of necessity because that is how lambdas have been represented 963 * in Metalava up until this was created and so until such time as all the code that consumes this 964 * has been updated to handle lambdas specifically it will need to remain a [ClassTypeItem]. 965 */ 966 interface LambdaTypeItem : ClassTypeItem { 967 /** True if the lambda is a suspend function, false otherwise. */ 968 val isSuspend: Boolean 969 970 /** The type of the optional receiver. */ 971 val receiverType: TypeItem? 972 973 /** The parameter types. */ 974 val parameterTypes: List<TypeItem> 975 976 /** The return type. */ 977 val returnType: TypeItem 978 duplicatenull979 override fun duplicate(): LambdaTypeItem = 980 duplicate(outerClassType?.duplicate(), arguments.map { it.duplicate() }) 981 duplicatenull982 override fun duplicate( 983 outerClass: ClassTypeItem?, 984 arguments: List<TypeArgumentTypeItem> 985 ): LambdaTypeItem 986 } 987 988 /** Represents a type variable type. */ 989 interface VariableTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem { 990 /** The name of the type variable */ 991 val name: String 992 993 /** The corresponding type parameter for this type variable. */ 994 val asTypeParameter: TypeParameterItem 995 996 override val erasedClass: ClassItem? 997 get() = (asTypeParameter.asErasedType() as ClassTypeItem).erasedClass 998 999 override fun description() = 1000 "$name (extends ${this.asTypeParameter.asErasedType()?.description() ?: "unknown type"})}" 1001 1002 override fun accept(visitor: TypeVisitor) { 1003 visitor.visit(this) 1004 } 1005 1006 override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) { 1007 visitor.visit(this, other) 1008 } 1009 1010 override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem { 1011 val nullability = modifiers.nullability() 1012 return (typeParameterBindings[asTypeParameter] ?: this).duplicate().apply { 1013 // If this use of the type parameter is marked as nullable, then it overrides the 1014 // nullability of the substituted type. 1015 if (nullability == TypeNullability.NULLABLE) { 1016 modifiers.setNullability(nullability) 1017 } else { 1018 // If the type that is replacing the type parameter has platform nullability, i.e. 1019 // carries no information one way or another about whether it is nullable, then 1020 // use the nullability of the use of the type parameter as while at worst it may 1021 // also have no nullability information, it could have some, e.g. from a declaration 1022 // nullability annotation. 1023 val typeParameterNullability = modifiers.nullability() 1024 if (typeParameterNullability == TypeNullability.PLATFORM) { 1025 modifiers.setNullability(nullability) 1026 } 1027 } 1028 } 1029 } 1030 1031 override fun asClass() = asTypeParameter.asErasedType()?.asClass() 1032 1033 override fun duplicate(): VariableTypeItem 1034 1035 override fun equalToType(other: TypeItem?): Boolean { 1036 return (other as? VariableTypeItem)?.name == name 1037 } 1038 1039 override fun hashCodeForType(): Int = name.hashCode() 1040 } 1041 1042 /** 1043 * Represents a wildcard type, like `?`, `? extends String`, and `? super String` in Java, or `*`, 1044 * `out String`, and `in String` in Kotlin. 1045 */ 1046 interface WildcardTypeItem : TypeItem, TypeArgumentTypeItem { 1047 /** The type this wildcard must extend. If null, the extends bound is implicitly `Object`. */ 1048 val extendsBound: ReferenceTypeItem? 1049 1050 /** The type this wildcard must be a super class of. */ 1051 val superBound: ReferenceTypeItem? 1052 acceptnull1053 override fun accept(visitor: TypeVisitor) { 1054 visitor.visit(this) 1055 } 1056 acceptnull1057 override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) { 1058 visitor.visit(this, other) 1059 } 1060 duplicatenull1061 override fun duplicate(): WildcardTypeItem = 1062 duplicate(extendsBound?.duplicate(), superBound?.duplicate()) 1063 1064 /** 1065 * Duplicates this type (including duplicating the modifiers so they can be independently 1066 * mutated), but substituting in the provided [extendsBound] and [superBound] in place of this 1067 * type's bounds. 1068 */ 1069 fun duplicate( 1070 extendsBound: ReferenceTypeItem?, 1071 superBound: ReferenceTypeItem?, 1072 ): WildcardTypeItem 1073 1074 override fun convertType(typeParameterBindings: TypeParameterBindings): WildcardTypeItem { 1075 return duplicate( 1076 extendsBound?.convertType(typeParameterBindings), 1077 superBound?.convertType(typeParameterBindings) 1078 ) 1079 } 1080 equalToTypenull1081 override fun equalToType(other: TypeItem?): Boolean { 1082 if (other !is WildcardTypeItem) return false 1083 return extendsBound?.equalToType(other.extendsBound) != false && 1084 superBound?.equalToType(other.superBound) != false 1085 } 1086 hashCodeForTypenull1087 override fun hashCodeForType(): Int = Objects.hash(extendsBound, superBound) 1088 1089 override fun asClass(): ClassItem? = null 1090 } 1091 1092 /** 1093 * Attempt to get the full name from the qualified name. 1094 * 1095 * The full name is the qualified name without the package including any outer class names. 1096 * 1097 * It relies on the convention that packages start with a lower case letter and classes start with 1098 * an upper case letter. 1099 */ 1100 fun bestGuessAtFullName(qualifiedName: String): String { 1101 val length = qualifiedName.length 1102 var prev: Char? = null 1103 var lastDotIndex = -1 1104 for (i in 0..length - 1) { 1105 val c = qualifiedName[i] 1106 if (prev == null || prev == '.') { 1107 if (c.isUpperCase()) { 1108 return qualifiedName.substring(i) 1109 } 1110 } 1111 if (c == '.') { 1112 lastDotIndex = i 1113 } 1114 prev = c 1115 } 1116 1117 return if (lastDotIndex == -1) { 1118 qualifiedName 1119 } else { 1120 qualifiedName.substring(lastDotIndex + 1) 1121 } 1122 } 1123