• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.util.Objects
20 
21 /**
22  * Whether metalava supports type use annotations. Note that you can't just turn this flag back on;
23  * you have to also add TYPE_USE back to the handful of nullness annotations in
24  * stub-annotations/src/main/java/.
25  */
26 const val SUPPORT_TYPE_USE_ANNOTATIONS = false
27 
28 /**
29  * Represents a {@link https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Type.html Type}
30  */
31 @MetalavaApi
32 interface TypeItem {
33     /** Modifiers for the type. Contains type-use annotation information. */
34     val modifiers: TypeModifiers
35 
36     fun accept(visitor: TypeVisitor)
37 
38     fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>)
39 
40     /**
41      * Whether this type is equal to [other], not considering modifiers.
42      *
43      * This is implemented on each sub-interface of [TypeItem] instead of [equals] because
44      * interfaces are not allowed to implement [equals]. An [equals] implementation is provided by
45      * [DefaultTypeItem].
46      */
47     fun equalToType(other: TypeItem?): Boolean
48 
49     /**
50      * Hashcode for the type.
51      *
52      * This is implemented on each sub-interface of [TypeItem] instead of [hashCode] because
53      * interfaces are not allowed to implement [hashCode]. A [hashCode] implementation is provided
54      * by [DefaultTypeItem].
55      */
56     fun hashCodeForType(): Int
57 
58     /**
59      * Provide a helpful description of the type, for use in error messages.
60      *
61      * This is not suitable for use in signature or stubs as while it defaults to [toTypeString] for
62      * most types it is overridden by others to provide additional information.
63      */
64     fun description(): String = toTypeString()
65 
66     /**
67      * Generates a string for this type.
68      *
69      * @see [TypeStringConfiguration] for information on the parameters.
70      */
71     fun toTypeString(
72         configuration: TypeStringConfiguration = TypeStringConfiguration.DEFAULT
73     ): String
74 
75     /**
76      * Get a string representation of the erased type.
77      *
78      * Implements the behavior described
79      * [here](https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html).
80      *
81      * One point to note is that vararg parameters are represented using standard array syntax, i.e.
82      * `[]`, not the special source `...` syntax. The reason for that is that the erased type is
83      * mainly used at runtime which treats a vararg parameter as a standard array type.
84      */
85     @MetalavaApi fun toErasedTypeString(): String
86 
87     /** Returns the internal name of the type, as seen in bytecode. */
88     fun internalName(): String
89 
90     fun asClass(): ClassItem?
91 
92     fun toSimpleType() = toTypeString(SIMPLE_TYPE_CONFIGURATION)
93 
94     /**
95      * Helper methods to compare types, especially types from signature files with types from
96      * parsing, which may have slightly different formats, e.g. varargs ("...") versus arrays
97      * ("[]"), java.lang. prefixes removed in wildcard signatures, etc.
98      */
99     fun toCanonicalType() = toTypeString(CANONICAL_TYPE_CONFIGURATION)
100 
101     /**
102      * Makes substitutions to the type based on the [typeParameterBindings]. For instance, if the
103      * [typeParameterBindings] contains `{T -> String}`, calling this method on `T` would return
104      * `String`, and calling it on `List<T>` would return `List<String>` (in both cases the
105      * modifiers on the `String` will be independently mutable from the `String` in the
106      * [typeParameterBindings]). Calling it on an unrelated type like `int` would return a duplicate
107      * of that type.
108      *
109      * This method is intended to be used in conjunction with [ClassItem.mapTypeVariables],
110      */
111     fun convertType(typeParameterBindings: TypeParameterBindings): TypeItem
112 
113     fun convertType(from: ClassItem, to: ClassItem): TypeItem {
114         val map = from.mapTypeVariables(to)
115         if (map.isNotEmpty()) {
116             return convertType(map)
117         }
118 
119         return this
120     }
121 
122     /** Returns `true` if `this` type can be assigned from `other` without unboxing the other. */
123     fun isAssignableFromWithoutUnboxing(other: TypeItem): Boolean {
124         // Limited text based check
125         if (this == other) return true
126         val bounds =
127             (other as? VariableTypeItem)?.asTypeParameter?.typeBounds()?.map { it.toTypeString() }
128                 ?: emptyList()
129         return bounds.contains(toTypeString())
130     }
131 
132     fun isJavaLangObject(): Boolean = false
133 
134     fun isString(): Boolean = false
135 
136     fun defaultValue(): Any? = null
137 
138     fun defaultValueString(): String = "null"
139 
140     /**
141      * Duplicates this type substituting in the provided [modifiers] in place of this instance's
142      * [modifiers].
143      */
144     fun substitute(modifiers: TypeModifiers): TypeItem
145 
146     /**
147      * Return a [TypeItem] instance identical to this on except its [modifiers]'s
148      * [TypeModifiers.nullability] property is the same as the [nullability] parameter.
149      *
150      * If the parameter is the same as this instance's [modifiers]'s property then it will just
151      * return this instance, otherwise it will return a new instance with a new [TypeModifiers].
152      */
153     fun substitute(nullability: TypeNullability) =
154         if (modifiers.nullability == nullability) this
155         else substitute(modifiers.substitute(nullability))
156 
157     /**
158      * Return a [TypeItem] instance of the same type as this one that was produced by the [TypeItem]
159      * appropriate [TypeTransformer.transform] method.
160      */
161     fun transform(transformer: TypeTransformer): TypeItem
162 
163     /** Whether this type was originally a value class type. Defaults to false if not overridden. */
164     fun isValueClassType(): Boolean = false
165 
166     companion object {
167         /** [TypeStringConfiguration] for [toSimpleType] to pass to [toTypeString]. */
168         private val SIMPLE_TYPE_CONFIGURATION =
169             TypeStringConfiguration(stripJavaLangPrefix = StripJavaLangPrefix.LEGACY)
170 
171         /** [TypeStringConfiguration] for [toCanonicalType] to pass to [toTypeString]. */
172         private val CANONICAL_TYPE_CONFIGURATION =
173             TypeStringConfiguration(
174                 stripJavaLangPrefix = StripJavaLangPrefix.ALWAYS,
175                 treatVarargsAsArray = true,
176             )
177 
178         /** Shortens types, if configured */
179         fun shortenTypes(type: String): String {
180             var cleaned = type
181             if (cleaned.contains("@androidx.annotation.")) {
182                 cleaned = cleaned.replace("@androidx.annotation.", "@")
183             }
184             return cleaned
185         }
186 
187         /**
188          * Create a [Comparator] that when given two [TypeItem] will treat them as equal if either
189          * returns `null` from [TypeItem.asClass] and will otherwise compare the two [ClassItem]s
190          * using [comparator].
191          *
192          * This only defines a partial ordering over [TypeItem].
193          */
194         private fun typeItemAsClassComparator(
195             comparator: Comparator<ClassItem>
196         ): Comparator<TypeItem> {
197             return Comparator { type1, type2 ->
198                 val cls1 = type1.asClass()
199                 val cls2 = type2.asClass()
200                 if (cls1 != null && cls2 != null) {
201                     comparator.compare(cls1, cls2)
202                 } else {
203                     0
204                 }
205             }
206         }
207 
208         /** A total ordering over [TypeItem] comparing [TypeItem.toTypeString]. */
209         private val typeStringComparator =
210             Comparator.comparing<TypeItem, String> { it.toTypeString() }
211 
212         /**
213          * A total ordering over [TypeItem] comparing [TypeItem.asClass] using
214          * [ClassItem.fullNameThenQualifierComparator] and then comparing [TypeItem.toTypeString].
215          */
216         val totalComparator: Comparator<TypeItem> =
217             typeItemAsClassComparator(ClassItem.fullNameThenQualifierComparator)
218                 .thenComparing(typeStringComparator)
219 
220         @Deprecated(
221             "" +
222                 "this should not be used as it only defines a partial ordering which means that the " +
223                 "source order will affect the result"
224         )
225         val partialComparator: Comparator<TypeItem> = Comparator { type1, type2 ->
226             val cls1 = type1.asClass()
227             val cls2 = type2.asClass()
228             if (cls1 != null && cls2 != null) {
229                 ClassItem.fullNameComparator.compare(cls1, cls2)
230             } else {
231                 type1.toTypeString().compareTo(type2.toTypeString())
232             }
233         }
234 
235         /**
236          * Convert a type string containing to its lambda representation or return the original.
237          *
238          * E.g.: `"kotlin.jvm.functions.Function1<Integer, String>"` to `"(Integer) -> String"`.
239          */
240         fun toLambdaFormat(typeName: String): String {
241             // Bail if this isn't a Kotlin function type
242             if (!typeName.startsWith(KOTLIN_FUNCTION_PREFIX)) {
243                 return typeName
244             }
245 
246             // Find the first character after the first opening angle bracket. This will either be
247             // the first character of the paramTypes of the lambda if it has parameters.
248             val paramTypesStart =
249                 typeName.indexOf('<', startIndex = KOTLIN_FUNCTION_PREFIX.length) + 1
250 
251             // The last type param is always the return type. We find and set these boundaries with
252             // the push down loop below.
253             var paramTypesEnd = -1
254             var returnTypeStart = -1
255 
256             // Get the exclusive end of the return type parameter by finding the last closing
257             // angle bracket.
258             val returnTypeEnd = typeName.lastIndexOf('>')
259 
260             // Bail if an an unexpected format broke the indexOf's above.
261             if (paramTypesStart <= 0 || paramTypesStart >= returnTypeEnd) {
262                 return typeName
263             }
264 
265             // This loop looks for the last comma that is not inside the type parameters of a type
266             // parameter. It's a simple push down state machine that stores its depth as a counter
267             // instead of a stack. It runs backwards from the last character of the type parameters
268             // just before the last closing angle bracket to the beginning just before the first
269             // opening angle bracket.
270             var depth = 0
271             for (i in returnTypeEnd - 1 downTo paramTypesStart) {
272                 val c = typeName[i]
273 
274                 // Increase or decrease stack depth on angle brackets
275                 when (c) {
276                     '>' -> depth++
277                     '<' -> depth--
278                 }
279 
280                 when {
281                     depth == 0 ->
282                         when { // At the top level
283                             c == ',' -> {
284                                 // When top level comma is found, mark it as the exclusive end of
285                                 // the
286                                 // parameter types and end the loop
287                                 paramTypesEnd = i
288                                 break
289                             }
290                             !c.isWhitespace() -> {
291                                 // Keep moving the start of the return type back until whitespace
292                                 returnTypeStart = i
293                             }
294                         }
295                     depth < 0 -> return typeName // Bail, unbalanced nesting
296                 }
297             }
298 
299             // Bail if some sort of unbalanced nesting occurred or the indices around the comma
300             // appear grossly incorrect.
301             if (depth > 0 || returnTypeStart < 0 || returnTypeStart <= paramTypesEnd) {
302                 return typeName
303             }
304 
305             return buildString(typeName.length) {
306                 append("(")
307 
308                 // Slice param types, if any, and append them between the parenthesis
309                 if (paramTypesEnd > 0) {
310                     append(typeName, paramTypesStart, paramTypesEnd)
311                 }
312 
313                 append(") -> ")
314 
315                 // Slice out the return type param and append it after the arrow
316                 append(typeName, returnTypeStart, returnTypeEnd)
317             }
318         }
319 
320         /** Prefix of Kotlin JVM function types, used for lambdas. */
321         private const val KOTLIN_FUNCTION_PREFIX = "kotlin.jvm.functions.Function"
322     }
323 }
324 
325 /** Different ways of handling `java.lang.` prefix stripping in [TypeItem.toTypeString]. */
326 enum class StripJavaLangPrefix {
327     /** Never strip java.lang. prefixes when */
328     NEVER,
329 
330     /**
331      * Only strip java.lang. prefixes from the start of the type as long as they are not a generic
332      * varargs parameter.
333      *
334      * This is legacy behavior from when types were treated as strings.
335      */
336     LEGACY,
337 
338     /**
339      * A special value that is only used internally within [TypeItem.toTypeString].
340      *
341      * If [LEGACY] was provided for a varargs type then [LEGACY] will be replaced by this when
342      * processing the [ArrayTypeItem] to indicate to the nested [ClassTypeItem] that it is a varargs
343      * parameter and to only strip the `java.lang.` prefix if it is at the start and is a generic
344      * type.
345      */
346     VARARGS,
347 
348     /** Always strip java.lang. prefixes from the type. */
349     ALWAYS,
350 }
351 
352 /**
353  * A mapping from one class's type parameters to the types provided for those type parameters in a
354  * possibly indirect subclass.
355  *
356  * e.g. Given `Map<K, V>` and a subinterface `StringToIntMap extends Map<String, Integer>` then this
357  * would contain a mapping from `K -> String` and `V -> Integer`.
358  *
359  * Although a `ClassTypeItem`'s arguments can be `WildcardTypeItem`s as well as
360  * `ReferenceTypeItem`s, a `ClassTypeItem` used in an extends or implements list cannot have a
361  * `WildcardTypeItem` as an argument so this cast is safe. See
362  * https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-Superclass
363  */
364 typealias TypeParameterBindings = Map<TypeParameterItem, ReferenceTypeItem>
365 
366 abstract class DefaultTypeItem(
367     final override val modifiers: TypeModifiers,
368 ) : TypeItem {
369 
370     private lateinit var cachedDefaultType: String
371     private lateinit var cachedErasedType: String
372 
toStringnull373     override fun toString(): String = toTypeString()
374 
375     override fun toTypeString(configuration: TypeStringConfiguration): String {
376         // Cache the default type string. Other configurations are less likely to be reused.
377         return if (configuration.isDefault) {
378             if (!::cachedDefaultType.isInitialized) {
379                 cachedDefaultType = generateTypeString(configuration)
380             }
381             cachedDefaultType
382         } else {
383             generateTypeString(configuration)
384         }
385     }
386 
387     /**
388      * Generate a string representation of this type based on [configuration].
389      *
390      * The returned value will be cached if the [configuration] is the default.
391      */
<lambda>null392     private fun generateTypeString(configuration: TypeStringConfiguration) = buildString {
393         appendTypeString(this@DefaultTypeItem, configuration)
394     }
395 
toErasedTypeStringnull396     override fun toErasedTypeString(): String {
397         if (!::cachedErasedType.isInitialized) {
398             cachedErasedType = toTypeString(ERASED_TYPE_STRING_CONFIGURATION)
399         }
400         return cachedErasedType
401     }
402 
internalNamenull403     override fun internalName(): String {
404         // Default implementation; PSI subclass is more accurate
405         return toSlashFormat(toErasedTypeString())
406     }
407 
equalsnull408     override fun equals(other: Any?): Boolean {
409         if (other !is TypeItem) return false
410         return equalToType(other)
411     }
412 
hashCodenull413     override fun hashCode(): Int = hashCodeForType()
414 
415     companion object {
416         private val ERASED_TYPE_STRING_CONFIGURATION =
417             TypeStringConfiguration(
418                 eraseGenerics = true,
419                 treatVarargsAsArray = true,
420             )
421 
422         private fun StringBuilder.appendTypeString(
423             type: TypeItem,
424             configuration: TypeStringConfiguration
425         ) {
426             when (type) {
427                 is PrimitiveTypeItem -> {
428                     if (configuration.annotations) {
429                         appendAnnotations(type.modifiers, configuration)
430                     }
431                     append(type.kind.primitiveName)
432                     // Primitives must be non-null.
433                 }
434                 is ArrayTypeItem -> {
435                     // Get the nested configuration. This replaces StripJavaLangPrefix.LEGACY
436                     // with StripJavaLangPrefix.VARARGS for a varargs type to maintain the legacy
437                     // behavior of NOT stripping java.lang. prefix from varargs parameters unless it
438                     // has type arguments.
439                     val nestedConfiguration =
440                         if (
441                             type.isVarargs &&
442                                 configuration.stripJavaLangPrefix == StripJavaLangPrefix.LEGACY
443                         ) {
444                             configuration.copy(stripJavaLangPrefix = StripJavaLangPrefix.VARARGS)
445                         } else configuration
446 
447                     // Compute the outermost array suffix as that can differ if it is varargs. If
448                     // this is a varargs then it must be the outermost, otherwise the outermost is
449                     // not a varargs so they will all use the same suffix.
450                     val outermostArraySuffix =
451                         if (type.isVarargs && !configuration.treatVarargsAsArray) "..." else "[]"
452 
453                     // The ordering of array annotations means this can't just use a recursive
454                     // approach for annotated multi-dimensional arrays, but it can if annotations
455                     // aren't included.
456                     if (configuration.annotations) {
457                         var deepComponentType = type.componentType
458                         val arrayModifiers = mutableListOf(type.modifiers)
459                         while (deepComponentType is ArrayTypeItem) {
460                             arrayModifiers.add(deepComponentType.modifiers)
461                             deepComponentType = deepComponentType.componentType
462                         }
463                         val suffixes = arrayModifiers.map { it.nullability.suffix }.reversed()
464 
465                         // Print the innermost component type.
466                         appendTypeString(deepComponentType, nestedConfiguration)
467 
468                         // Print modifiers from the outermost array type in, and the array suffixes.
469                         arrayModifiers.zip(suffixes).forEachIndexed { index, (modifiers, suffix) ->
470                             appendAnnotations(modifiers, configuration, leadingSpace = true)
471                             // The array suffix can be different on the outermost array type. The
472                             // outermost is the last in the list.
473                             if (index == arrayModifiers.lastIndex) {
474                                 append(outermostArraySuffix)
475                             } else {
476                                 // Only the outermost array can be varargs.
477                                 append("[]")
478                             }
479                             if (configuration.kotlinStyleNulls) {
480                                 append(suffix)
481                             }
482                         }
483                     } else {
484                         // Non-annotated case: just recur to the component
485                         appendTypeString(type.componentType, nestedConfiguration)
486                         append(outermostArraySuffix)
487                         if (configuration.kotlinStyleNulls) {
488                             append(type.modifiers.nullability.suffix)
489                         }
490                     }
491                 }
492                 is ClassTypeItem -> {
493                     if (type.outerClassType != null) {
494                         // Legacy behavior for stripping java.lang. prefixes is to not strip them
495                         // from nested classes. This replicates that by replacing LEGACY with
496                         // NEVER in the configuration used to append the type string of the
497                         // outermost class which is responsible for stripping the prefix.
498                         val nestedConfiguration =
499                             if (configuration.stripJavaLangPrefix == StripJavaLangPrefix.LEGACY)
500                                 configuration.copy(stripJavaLangPrefix = StripJavaLangPrefix.NEVER)
501                             else configuration
502                         appendTypeString(type.outerClassType!!, nestedConfiguration)
503                         append(configuration.nestedClassSeparator)
504                         if (configuration.annotations) {
505                             appendAnnotations(type.modifiers, configuration)
506                         }
507                         append(type.className)
508                     } else {
509                         // Check to see whether a java.lang. prefix should be stripped from the type
510                         // name.
511                         val stripJavaLangPrefix =
512                             when (configuration.stripJavaLangPrefix) {
513                                 StripJavaLangPrefix.ALWAYS -> true
514                                 StripJavaLangPrefix.LEGACY ->
515                                     // This should only strip if this is at the start.
516                                     isEmpty()
517                                 StripJavaLangPrefix.VARARGS ->
518                                     // This should only strip if this is at the start and is a
519                                     // generic type.
520                                     isEmpty() && type.hasTypeArguments()
521                                 else -> false
522                             }
523 
524                         // Get the class name prefix, i.e. the part before the class's simple name
525                         // where annotations can be placed. e.g. for java.lang.String the simple
526                         // name is `String` and the prefix is `java.lang.`.
527                         val classNamePrefix = type.classNamePrefix
528 
529                         // Append the class name prefix unless it is `java.lang.` and `java.lang.`
530                         // prefixes should be stripped.
531                         if (!(stripJavaLangPrefix && classNamePrefix == JAVA_LANG_PREFIX)) {
532                             append(classNamePrefix)
533                         }
534                         if (configuration.annotations) {
535                             appendAnnotations(type.modifiers, configuration)
536                         }
537                         append(type.className)
538                     }
539 
540                     if (!configuration.eraseGenerics && type.arguments.isNotEmpty()) {
541                         append("<")
542                         type.arguments.forEachIndexed { index, parameter ->
543                             appendTypeString(parameter, configuration)
544                             if (index != type.arguments.size - 1) {
545                                 append(",")
546                                 if (configuration.spaceBetweenTypeArguments) {
547                                     append(" ")
548                                 }
549                             }
550                         }
551                         append(">")
552                     }
553                     if (configuration.kotlinStyleNulls) {
554                         append(type.modifiers.nullability.suffix)
555                     }
556                 }
557                 is VariableTypeItem -> {
558                     if (configuration.annotations) {
559                         appendAnnotations(type.modifiers, configuration)
560                     }
561                     if (configuration.eraseGenerics) {
562                         // Replace the type variable with the bounds of the type parameter.
563                         val typeParameter = type.asTypeParameter
564                         typeParameter.asErasedType()?.let { boundsType ->
565                             appendTypeString(boundsType, configuration)
566                         }
567                         // No explicit bounds were provided so use the default of java.lang.Object.
568                         ?: if (configuration.stripJavaLangPrefix == StripJavaLangPrefix.ALWAYS) {
569                                 append("Object")
570                             } else {
571                                 append(JAVA_LANG_OBJECT)
572                             }
573                     } else {
574                         append(type.name)
575                     }
576                     if (configuration.kotlinStyleNulls) {
577                         append(type.modifiers.nullability.suffix)
578                     }
579                 }
580                 is WildcardTypeItem -> {
581                     if (configuration.annotations) {
582                         appendAnnotations(type.modifiers, configuration)
583                     }
584                     append("?")
585 
586                     type.superBound?.let {
587                         append(" super ")
588                         appendTypeString(it, configuration)
589                         // If there's a super bound, don't also print an object extends bound.
590                         return
591                     }
592 
593                     type.extendsBound?.let {
594                         if (shouldIncludeExtendsBound(it, configuration)) {
595                             append(" extends ")
596                             appendTypeString(it, configuration)
597                         }
598                     }
599 
600                     // It doesn't make sense to have a nullness suffix on a wildcard, this should be
601                     // handled by the bound.
602                 }
603             }
604         }
605 
606         /**
607          * Returns whether the [extendsBound] should be included in the type string based on the
608          * [configuration].
609          */
610         private fun shouldIncludeExtendsBound(
611             extendsBound: ReferenceTypeItem,
612             configuration: TypeStringConfiguration
613         ): Boolean {
614             // Non-object bounds should always be included.
615             if (!extendsBound.isJavaLangObject()) return true
616 
617             // If the bound is Object, it should only be included when the nullability isn't implied
618             // by the configuration. If both kotlinStyleNulls and annotations are false, no
619             // nullability information is included anyway.
620             if (!configuration.kotlinStyleNulls && !configuration.annotations) return false
621 
622             // When nullability information is included, excluded bounds imply non-null when
623             // kotlinStyleNulls is true and platform when it is false.
624             val nullability = extendsBound.modifiers.nullability
625             if (configuration.kotlinStyleNulls && nullability == TypeNullability.NONNULL)
626                 return false
627             if (!configuration.kotlinStyleNulls && nullability == TypeNullability.PLATFORM)
628                 return false
629             return true
630         }
631 
632         private fun StringBuilder.appendAnnotations(
633             modifiers: TypeModifiers,
634             configuration: TypeStringConfiguration,
635             leadingSpace: Boolean = false,
636             trailingSpace: Boolean = true
637         ) {
638             val annotations =
639                 modifiers.annotations.filter { annotation ->
640                     // If Kotlin-style nulls are printed, nullness annotations shouldn't be.
641                     !(configuration.kotlinStyleNulls && annotation.isNullnessAnnotation())
642                 }
643             if (annotations.isEmpty()) return
644 
645             if (leadingSpace) {
646                 append(' ')
647             }
648             annotations.forEachIndexed { index, annotation ->
649                 append(annotation.toSource())
650                 if (index != annotations.size - 1) {
651                     append(' ')
652                 }
653             }
654             if (trailingSpace) {
655                 append(' ')
656             }
657         }
658 
659         // Copied from doclava1
660         private fun toSlashFormat(typeName: String): String {
661             var name = typeName
662             var dimension = ""
663             while (name.endsWith("[]")) {
664                 dimension += "["
665                 name = name.substring(0, name.length - 2)
666             }
667 
668             val base: String
669             base =
670                 when (name) {
671                     "void" -> "V"
672                     "byte" -> "B"
673                     "boolean" -> "Z"
674                     "char" -> "C"
675                     "short" -> "S"
676                     "int" -> "I"
677                     "long" -> "J"
678                     "float" -> "F"
679                     "double" -> "D"
680                     else -> "L" + getInternalName(name) + ";"
681                 }
682 
683             return dimension + base
684         }
685 
686         /**
687          * Computes the internal class name of the given fully qualified class name. For example, it
688          * converts foo.bar.Foo.Bar into foo/bar/Foo$Bar
689          *
690          * @param qualifiedName the fully qualified class name
691          * @return the internal class name
692          */
693         private fun getInternalName(qualifiedName: String): String {
694             if (qualifiedName.indexOf('.') == -1) {
695                 return qualifiedName
696             }
697 
698             // If class name contains $, it's not an ambiguous nested class name.
699             if (qualifiedName.indexOf('$') != -1) {
700                 return qualifiedName.replace('.', '/')
701             }
702             // Let's assume that components that start with Caps are class names.
703             return buildString {
704                 var prev: String? = null
705                 for (part in qualifiedName.split(".")) {
706                     if (!prev.isNullOrEmpty()) {
707                         if (Character.isUpperCase(prev[0])) {
708                             append('$')
709                         } else {
710                             append('/')
711                         }
712                     }
713                     append(part)
714                     prev = part
715                 }
716             }
717         }
718     }
719 }
720 
721 /**
722  * Configuration options for how to represent a type as a string.
723  *
724  * @param annotations Whether to include annotations on the type.
725  * @param eraseGenerics If `true` then type parameters are ignored and type variables are replaced
726  *   with the upper bound of the type parameter.
727  * @param kotlinStyleNulls Whether to represent nullability with Kotlin-style suffixes: `?` for
728  *   nullable, no suffix for non-null, and `!` for platform nullability. For example, the Java type
729  *   `@Nullable List<String>` would be represented as `List<String!>?`.
730  * @param nestedClassSeparator The character that is used to separate a nested class from its
731  *   containing class.
732  * @param spaceBetweenTypeArguments Whether to include a space between type arguments of a generic
733  *   type.
734  * @param stripJavaLangPrefix Controls how `java.lang.` prefixes are removed from the types.
735  * @param treatVarargsAsArray If `false` then a varargs type will use `...` to indicate that it is a
736  *   varargs type, otherwise it will use `[]` like a normal array.
737  */
738 data class TypeStringConfiguration(
739     val annotations: Boolean = false,
740     val eraseGenerics: Boolean = false,
741     val kotlinStyleNulls: Boolean = false,
742     val nestedClassSeparator: Char = '.',
743     val spaceBetweenTypeArguments: Boolean = false,
744     val stripJavaLangPrefix: StripJavaLangPrefix = StripJavaLangPrefix.NEVER,
745     val treatVarargsAsArray: Boolean = false,
746 ) {
747     /**
748      * Check to see if this matches [DEFAULT].
749      *
750      * This is computed lazily to avoid the comparison against [DEFAULT] being done while creating
751      * the instance to assign to [DEFAULT] at which point [DEFAULT] would be `null`.
752      */
<lambda>null753     val isDefault by lazy(LazyThreadSafetyMode.NONE) { this == DEFAULT }
754 
755     companion object {
756         /** The default[TypeStringConfiguration]. */
757         val DEFAULT: TypeStringConfiguration = TypeStringConfiguration()
758     }
759 }
760 
761 /**
762  * The type for [ClassTypeItem.arguments].
763  *
764  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeArgument.
765  */
766 interface TypeArgumentTypeItem : TypeItem {
767     /** Override to specialize the return type. */
convertTypenull768     override fun convertType(typeParameterBindings: TypeParameterBindings): TypeArgumentTypeItem
769 
770     /** Override to specialize the return type. */
771     override fun substitute(modifiers: TypeModifiers): TypeArgumentTypeItem
772 
773     /** Override to specialize the return type. */
774     override fun transform(transformer: TypeTransformer): TypeArgumentTypeItem
775 }
776 
777 /**
778  * The type for a reference.
779  *
780  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-ReferenceType.
781  */
782 interface ReferenceTypeItem : TypeItem, TypeArgumentTypeItem {
783     /** Override to specialize the return type. */
784     override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem
785 
786     /** Override to specialize the return type. */
787     override fun substitute(modifiers: TypeModifiers): ReferenceTypeItem
788 
789     /** Override to specialize the return type. */
790     override fun transform(transformer: TypeTransformer): ReferenceTypeItem
791 }
792 
793 /**
794  * The type of [TypeParameterItem]'s type bounds.
795  *
796  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeBound
797  */
798 interface BoundsTypeItem : TypeItem, ReferenceTypeItem
799 
800 /**
801  * The type of [MethodItem.throwsTypes]'s.
802  *
803  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-ExceptionType.
804  */
805 sealed interface ExceptionTypeItem : TypeItem, ReferenceTypeItem {
806     /** Override to specialize the return type. */
transformnull807     override fun transform(transformer: TypeTransformer): ExceptionTypeItem
808 
809     /**
810      * Get the erased [ClassItem], if any.
811      *
812      * The erased [ClassItem] is the one which would be used by Java at runtime after the generic
813      * types have been erased. This will cause an error if it is called on a [VariableTypeItem]
814      * whose [TypeParameterItem]'s upper bound is not a [ExceptionTypeItem]. However, that should
815      * never happen as it would be a compile time error.
816      */
817     val erasedClass: ClassItem?
818 
819     /**
820      * The best guess of the full name, i.e. the qualified class name without the package but
821      * including the outer class names.
822      *
823      * This is not something that can be accurately determined solely by examining the reference or
824      * even the import as there is no distinction made between a package name and a class name. Java
825      * naming conventions do say that package names should start with a lower case letter and class
826      * names should start with an upper case letter, but they are not enforced so cannot be fully
827      * relied upon.
828      *
829      * It is possible that in some contexts a model could provide a better full name than guessing
830      * from the fully qualified name, e.g. a reference within the same package, however that is not
831      * something that will be supported by all models and so attempting to use that could lead to
832      * subtle model differences that could break users of the models.
833      *
834      * The only way to fully determine the full name is to resolve the class and extract it from
835      * there but this avoids resolving a class as it can be expensive. Instead, this just makes the
836      * best guess assuming normal Java conventions.
837      */
838     @Deprecated(
839         "Do not use as full name is only ever a best guess based on naming conventions; use the full type string instead",
840         ReplaceWith("toTypeString()")
841     )
842     fun fullName(): String = bestGuessAtFullName(toTypeString())
843 
844     companion object {
845         /** A partial ordering over [ExceptionTypeItem] comparing [ExceptionTypeItem] full names. */
846         val fullNameComparator: Comparator<ExceptionTypeItem> =
847             Comparator.comparing { @Suppress("DEPRECATION") it.fullName() }
848     }
849 }
850 
851 /** Represents a primitive type, like int or boolean. */
852 interface PrimitiveTypeItem : TypeItem {
853     /** The kind of [Primitive] this type is. */
854     val kind: Primitive
855 
856     /** The possible kinds of primitives. */
857     enum class Primitive(
858         val primitiveName: String,
859         val defaultValue: Any?,
860         val defaultValueString: String,
861         val wrapperClass: Class<*>,
862     ) {
863         BOOLEAN(
864             primitiveName = "boolean",
865             defaultValue = false,
866             defaultValueString = "false",
867             wrapperClass = java.lang.Boolean::class.java,
868         ),
869         BYTE(
870             primitiveName = "byte",
871             defaultValue = 0.toByte(),
872             defaultValueString = "0",
873             wrapperClass = java.lang.Byte::class.java,
874         ),
875         CHAR(
876             primitiveName = "char",
877             defaultValue = 0.toChar(),
878             defaultValueString = "0",
879             wrapperClass = java.lang.Character::class.java,
880         ),
881         DOUBLE(
882             primitiveName = "double",
883             defaultValue = 0.0,
884             defaultValueString = "0",
885             wrapperClass = java.lang.Double::class.java,
886         ),
887         FLOAT(
888             primitiveName = "float",
889             defaultValue = 0F,
890             defaultValueString = "0",
891             wrapperClass = java.lang.Float::class.java,
892         ),
893         INT(
894             primitiveName = "int",
895             defaultValue = 0,
896             defaultValueString = "0",
897             wrapperClass = java.lang.Integer::class.java,
898         ),
899         LONG(
900             primitiveName = "long",
901             defaultValue = 0L,
902             defaultValueString = "0",
903             wrapperClass = java.lang.Long::class.java,
904         ),
905         SHORT(
906             primitiveName = "short",
907             defaultValue = 0.toShort(),
908             defaultValueString = "0",
909             wrapperClass = java.lang.Short::class.java,
910         ),
911         VOID(
912             primitiveName = "void",
913             defaultValue = null,
914             defaultValueString = "null",
915             wrapperClass = java.lang.Void::class.java,
916         )
917     }
918 
defaultValuenull919     override fun defaultValue(): Any? = kind.defaultValue
920 
921     override fun defaultValueString(): String = kind.defaultValueString
922 
923     override fun accept(visitor: TypeVisitor) {
924         visitor.visit(this)
925     }
926 
acceptnull927     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
928         visitor.visit(this, other)
929     }
930 
931     @Deprecated(
932         "implementation detail of this class",
933         replaceWith = ReplaceWith("substitute(modifiers)"),
934     )
duplicatenull935     fun duplicate(modifiers: TypeModifiers): PrimitiveTypeItem
936 
937     override fun substitute(modifiers: TypeModifiers): PrimitiveTypeItem =
938         if (modifiers !== this.modifiers) @Suppress("DEPRECATION") duplicate(modifiers) else this
939 
940     override fun convertType(typeParameterBindings: TypeParameterBindings): PrimitiveTypeItem {
941         // Primitive type is never affected by a type mapping so always return this.
942         return this
943     }
944 
transformnull945     override fun transform(transformer: TypeTransformer): PrimitiveTypeItem {
946         return transformer.transform(this)
947     }
948 
equalToTypenull949     override fun equalToType(other: TypeItem?): Boolean {
950         return (other as? PrimitiveTypeItem)?.kind == kind
951     }
952 
hashCodeForTypenull953     override fun hashCodeForType(): Int = kind.hashCode()
954 
955     override fun asClass(): ClassItem? = null
956 }
957 
958 /** Represents an array type, including vararg types. */
959 interface ArrayTypeItem : TypeItem, ReferenceTypeItem {
960     /** The array's inner type (which for multidimensional arrays is another array type). */
961     val componentType: TypeItem
962 
963     /** Whether this array type represents a varargs parameter. */
964     val isVarargs: Boolean
965 
966     override fun accept(visitor: TypeVisitor) {
967         visitor.visit(this)
968     }
969 
970     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
971         visitor.visit(this, other)
972     }
973 
974     /**
975      * Duplicates this type substituting in the provided [modifiers] and [componentType] in place of
976      * this instance's [modifiers] and [componentType].
977      */
978     @Deprecated(
979         "implementation detail of this class",
980         replaceWith = ReplaceWith("substitute(modifiers, componentType)"),
981     )
982     fun duplicate(modifiers: TypeModifiers, componentType: TypeItem): ArrayTypeItem
983 
984     override fun substitute(modifiers: TypeModifiers): ArrayTypeItem =
985         substitute(modifiers, componentType)
986 
987     /**
988      * Return an [ArrayTypeItem] instance identical to this one except its [TypeItem.modifiers] and
989      * [ArrayTypeItem.componentType] properties are the same as the [modifiers] and [componentType]
990      * parameters respectively.
991      *
992      * If the parameters are the same as this instance's properties then it will just return this
993      * instance, otherwise it will return a new instance.
994      */
995     fun substitute(
996         modifiers: TypeModifiers = this.modifiers,
997         componentType: TypeItem = this.componentType,
998     ) =
999         if (modifiers !== this.modifiers || componentType !== this.componentType)
1000             @Suppress("DEPRECATION") duplicate(modifiers, componentType)
1001         else this
1002 
1003     override fun convertType(typeParameterBindings: TypeParameterBindings): ArrayTypeItem {
1004         return substitute(
1005             componentType = componentType.convertType(typeParameterBindings),
1006         )
1007     }
1008 
1009     override fun transform(transformer: TypeTransformer): ArrayTypeItem {
1010         return transformer.transform(this)
1011     }
1012 
1013     override fun equalToType(other: TypeItem?): Boolean {
1014         if (other !is ArrayTypeItem) return false
1015         return isVarargs == other.isVarargs && componentType.equalToType(other.componentType)
1016     }
1017 
1018     override fun hashCodeForType(): Int = Objects.hash(isVarargs, componentType)
1019 
1020     override fun asClass(): ClassItem? = componentType.asClass()
1021 }
1022 
1023 /** Represents a class type. */
1024 interface ClassTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem {
1025     /** The qualified name of this class, e.g. "java.lang.String". */
1026     val qualifiedName: String
1027 
1028     /**
1029      * The class type's arguments, empty if it has none.
1030      *
1031      * i.e. The specific types that this class type assigns to each of the referenced [ClassItem]'s
1032      * type parameters.
1033      */
1034     val arguments: List<TypeArgumentTypeItem>
1035 
1036     /** The outer class type of this class, if it is a nested type. */
1037     val outerClassType: ClassTypeItem?
1038 
1039     /**
1040      * The name of the class, e.g. "String" for "java.lang.String" and "Inner" for
1041      * "test.pkg.Outer.Inner".
1042      */
1043     val className: String
1044 
1045     /**
1046      * Get the class name prefix, i.e. the part before [className] and after which type use
1047      * annotations, if any will appear.
1048      *
1049      * e.g. for `java.lang.String`, [className] is `String` and the prefix is `java.lang.`. For
1050      * `java.util.Map.Entry` [className] is `Entry` and the prefix is `java.util.Map.`.
1051      *
1052      * This is the value such that [classNamePrefix] + [className] == [qualifiedName].
1053      */
1054     val classNamePrefix: String
1055         get() {
1056             val classNamePrefixEnd = qualifiedName.length - className.length
1057             return qualifiedName.substring(0, classNamePrefixEnd)
1058         }
1059 
1060     override val erasedClass: ClassItem?
1061         get() = asClass()
1062 
acceptnull1063     override fun accept(visitor: TypeVisitor) {
1064         visitor.visit(this)
1065     }
1066 
acceptnull1067     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
1068         visitor.visit(this, other)
1069     }
1070 
1071     /**
1072      * Check to see whether this type has any type arguments.
1073      *
1074      * It will return `true` for say `List<T>`, but `false` for `String`.
1075      */
hasTypeArgumentsnull1076     fun hasTypeArguments() = arguments.isNotEmpty()
1077 
1078     override fun isString(): Boolean = qualifiedName == JAVA_LANG_STRING
1079 
1080     override fun isJavaLangObject(): Boolean = qualifiedName == JAVA_LANG_OBJECT
1081 
1082     /**
1083      * Check to see whether this type is a functional type, i.e. references a function interface,
1084      * which is an interface with at most one abstract method.
1085      */
1086     fun isFunctionalType(): Boolean = error("unsupported")
1087 
1088     /**
1089      * Duplicates this type substituting in the provided [modifiers], [outerClassType] and
1090      * [arguments] in place of this instance's [modifiers], [outerClassType] and [arguments].
1091      */
1092     @Deprecated(
1093         "implementation detail of this class",
1094         replaceWith = ReplaceWith("substitute(modifiers, outerClassType, arguments)"),
1095     )
1096     fun duplicate(
1097         modifiers: TypeModifiers,
1098         outerClassType: ClassTypeItem?,
1099         arguments: List<TypeArgumentTypeItem>,
1100     ): ClassTypeItem
1101 
1102     override fun substitute(modifiers: TypeModifiers): ClassTypeItem =
1103         substitute(modifiers, outerClassType, arguments)
1104 
1105     /**
1106      * Return a [ClassTypeItem] instance identical to this one except its [TypeItem.modifiers],
1107      * [ClassTypeItem.outerClassType] and [ClassTypeItem.arguments] properties are the same as the
1108      * [modifiers], [outerClassType] and [arguments] parameters respectively.
1109      *
1110      * If the parameters are the same as this instance's properties then it will just return this
1111      * instance, otherwise it will return a new instance.
1112      */
1113     fun substitute(
1114         modifiers: TypeModifiers = this.modifiers,
1115         outerClassType: ClassTypeItem? = this.outerClassType,
1116         arguments: List<TypeArgumentTypeItem> = this.arguments,
1117     ) =
1118         if (
1119             modifiers !== this.modifiers ||
1120                 outerClassType !== this.outerClassType ||
1121                 arguments !== this.arguments
1122         )
1123             @Suppress("DEPRECATION") duplicate(modifiers, outerClassType, arguments)
1124         else this
1125 
1126     override fun convertType(typeParameterBindings: TypeParameterBindings): ClassTypeItem {
1127         return substitute(
1128             outerClassType = outerClassType?.convertType(typeParameterBindings),
1129             arguments = arguments.mapIfNotSame { it.convertType(typeParameterBindings) },
1130         )
1131     }
1132 
transformnull1133     override fun transform(transformer: TypeTransformer): ClassTypeItem {
1134         return transformer.transform(this)
1135     }
1136 
equalToTypenull1137     override fun equalToType(other: TypeItem?): Boolean {
1138         if (other !is ClassTypeItem) return false
1139         return qualifiedName == other.qualifiedName &&
1140             arguments.size == other.arguments.size &&
1141             arguments.zip(other.arguments).all { (p1, p2) -> p1.equalToType(p2) } &&
1142             ((outerClassType == null && other.outerClassType == null) ||
1143                 outerClassType?.equalToType(other.outerClassType) == true)
1144     }
1145 
hashCodeForTypenull1146     override fun hashCodeForType(): Int = Objects.hash(qualifiedName, outerClassType, arguments)
1147 
1148     companion object {
1149         /** Computes the simple name of a class from a qualified class name. */
1150         fun computeClassName(qualifiedName: String): String {
1151             val lastDotIndex = qualifiedName.lastIndexOf('.')
1152             return if (lastDotIndex == -1) {
1153                 qualifiedName
1154             } else {
1155                 qualifiedName.substring(lastDotIndex + 1)
1156             }
1157         }
1158     }
1159 }
1160 
1161 /**
1162  * Represents a kotlin lambda type.
1163  *
1164  * This extends [ClassTypeItem] out of necessity because that is how lambdas have been represented
1165  * in Metalava up until this was created and so until such time as all the code that consumes this
1166  * has been updated to handle lambdas specifically it will need to remain a [ClassTypeItem].
1167  */
1168 interface LambdaTypeItem : ClassTypeItem {
1169     /** True if the lambda is a suspend function, false otherwise. */
1170     val isSuspend: Boolean
1171 
1172     /** The type of the optional receiver. */
1173     val receiverType: TypeItem?
1174 
1175     /** The parameter types. */
1176     val parameterTypes: List<TypeItem>
1177 
1178     /** The return type. */
1179     val returnType: TypeItem
1180 
1181     @Deprecated(
1182         "implementation detail of this class",
1183         replaceWith = ReplaceWith("substitute(modifiers, outerClassType, arguments)")
1184     )
duplicatenull1185     override fun duplicate(
1186         modifiers: TypeModifiers,
1187         outerClassType: ClassTypeItem?,
1188         arguments: List<TypeArgumentTypeItem>,
1189     ): LambdaTypeItem
1190 
1191     override fun substitute(modifiers: TypeModifiers): LambdaTypeItem =
1192         substitute(modifiers, outerClassType, arguments)
1193 
1194     /** Override to specialize the return type. */
1195     override fun substitute(
1196         modifiers: TypeModifiers,
1197         outerClassType: ClassTypeItem?,
1198         arguments: List<TypeArgumentTypeItem>
1199     ) = super.substitute(modifiers, outerClassType, arguments) as LambdaTypeItem
1200 
1201     override fun transform(transformer: TypeTransformer): LambdaTypeItem {
1202         return transformer.transform(this)
1203     }
1204 }
1205 
1206 /** Represents a type variable type. */
1207 interface VariableTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem {
1208     /** The name of the type variable */
1209     val name: String
1210 
1211     /** The corresponding type parameter for this type variable. */
1212     val asTypeParameter: TypeParameterItem
1213 
1214     override val erasedClass: ClassItem?
1215         get() = (asTypeParameter.asErasedType() as ClassTypeItem).erasedClass
1216 
descriptionnull1217     override fun description() =
1218         "$name (extends ${this.asTypeParameter.asErasedType()?.description() ?: "unknown type"})}"
1219 
1220     override fun accept(visitor: TypeVisitor) {
1221         visitor.visit(this)
1222     }
1223 
acceptnull1224     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
1225         visitor.visit(this, other)
1226     }
1227 
1228     @Deprecated(
1229         "implementation detail of this class",
1230         replaceWith = ReplaceWith("substitute(modifiers)")
1231     )
duplicatenull1232     fun duplicate(modifiers: TypeModifiers): VariableTypeItem
1233 
1234     override fun substitute(modifiers: TypeModifiers): VariableTypeItem =
1235         if (modifiers !== this.modifiers) @Suppress("DEPRECATION") duplicate(modifiers) else this
1236 
1237     override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem {
1238         val nullability = modifiers.nullability
1239         return typeParameterBindings[asTypeParameter]?.let { replacement ->
1240             val replacementNullability =
1241                 when {
1242                     // If this use of the type parameter is marked as nullable, then it overrides
1243                     // the nullability of the substituted type.
1244                     nullability == TypeNullability.NULLABLE -> nullability
1245                     // If the type that is replacing the type parameter has platform nullability,
1246                     // i.e. carries no information one way or another about whether it is nullable,
1247                     // then use the nullability of the use of the type parameter as while at worst
1248                     // it may also have no nullability information, it could have some, e.g. from a
1249                     // declaration nullability annotation.
1250                     replacement.modifiers.nullability == TypeNullability.PLATFORM -> nullability
1251                     else -> null
1252                 }
1253 
1254             if (replacementNullability == null) {
1255                 replacement
1256             } else {
1257                 replacement.substitute(replacementNullability) as ReferenceTypeItem
1258             }
1259         }
1260             ?:
1261             // The type parameter binding does not contain a replacement for this variable so use
1262             // this as is.
1263             this
1264     }
1265 
transformnull1266     override fun transform(transformer: TypeTransformer): VariableTypeItem {
1267         return transformer.transform(this)
1268     }
1269 
asClassnull1270     override fun asClass() = asTypeParameter.asErasedType()?.asClass()
1271 
1272     override fun equalToType(other: TypeItem?): Boolean {
1273         return (other as? VariableTypeItem)?.name == name
1274     }
1275 
hashCodeForTypenull1276     override fun hashCodeForType(): Int = name.hashCode()
1277 }
1278 
1279 /**
1280  * Represents a wildcard type, like `?`, `? extends String`, and `? super String` in Java, or `*`,
1281  * `out String`, and `in String` in Kotlin.
1282  */
1283 interface WildcardTypeItem : TypeItem, TypeArgumentTypeItem {
1284     /** The type this wildcard must extend. If null, the extends bound is implicitly `Object`. */
1285     val extendsBound: ReferenceTypeItem?
1286 
1287     /** The type this wildcard must be a super class of. */
1288     val superBound: ReferenceTypeItem?
1289 
1290     override fun accept(visitor: TypeVisitor) {
1291         visitor.visit(this)
1292     }
1293 
1294     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
1295         visitor.visit(this, other)
1296     }
1297 
1298     /**
1299      * Duplicates this type substituting in the provided [modifiers], [extendsBound] and
1300      * [superBound] in place of this instance's [modifiers], [extendsBound] and [superBound].
1301      */
1302     @Deprecated(
1303         "implementation detail of this class",
1304         replaceWith = ReplaceWith("substitute(modifiers, extendsBound, superBound)"),
1305     )
1306     fun duplicate(
1307         modifiers: TypeModifiers,
1308         extendsBound: ReferenceTypeItem?,
1309         superBound: ReferenceTypeItem?,
1310     ): WildcardTypeItem
1311 
1312     override fun substitute(modifiers: TypeModifiers): WildcardTypeItem =
1313         substitute(modifiers, extendsBound, superBound)
1314 
1315     /**
1316      * Return a [WildcardTypeItem] instance identical to this one except its [TypeItem.modifiers],
1317      * [WildcardTypeItem.extendsBound] and [WildcardTypeItem.superBound] properties are the same as
1318      * the [modifiers], [extendsBound] and [superBound] parameters respectively.
1319      *
1320      * If the parameters are the same as this instance's properties then it will just return this
1321      * instance, otherwise it will return a new instance.
1322      */
1323     fun substitute(
1324         modifiers: TypeModifiers = this.modifiers,
1325         extendsBound: ReferenceTypeItem? = this.extendsBound,
1326         superBound: ReferenceTypeItem? = this.superBound,
1327     ) =
1328         if (
1329             modifiers !== this.modifiers ||
1330                 extendsBound !== this.extendsBound ||
1331                 superBound !== this.superBound
1332         )
1333             @Suppress("DEPRECATION") duplicate(modifiers, extendsBound, superBound)
1334         else this
1335 
1336     override fun convertType(typeParameterBindings: TypeParameterBindings): WildcardTypeItem {
1337         return substitute(
1338             modifiers,
1339             extendsBound?.convertType(typeParameterBindings),
1340             superBound?.convertType(typeParameterBindings)
1341         )
1342     }
1343 
1344     override fun transform(transformer: TypeTransformer): WildcardTypeItem {
1345         return transformer.transform(this)
1346     }
1347 
1348     override fun equalToType(other: TypeItem?): Boolean {
1349         if (other !is WildcardTypeItem) return false
1350         return extendsBound?.equalToType(other.extendsBound) != false &&
1351             superBound?.equalToType(other.superBound) != false
1352     }
1353 
1354     override fun hashCodeForType(): Int = Objects.hash(extendsBound, superBound)
1355 
1356     override fun asClass(): ClassItem? = null
1357 }
1358 
1359 /**
1360  * Create a [TypeTransformer] that will remove any type annotations for which [filter] returns false
1361  * when called against the [AnnotationItem]'s [ClassItem] return by [AnnotationItem.resolve]. If
1362  * that returns `null` then the [AnnotationItem] will be kept.
1363  */
typeUseAnnotationFilternull1364 fun typeUseAnnotationFilter(filter: FilterPredicate): TypeTransformer =
1365     object : BaseTypeTransformer() {
1366         override fun transform(modifiers: TypeModifiers): TypeModifiers {
1367             if (modifiers.annotations.isEmpty()) return modifiers
1368             return modifiers.substitute(
1369                 annotations =
1370                     modifiers.annotations.filter { annotationItem ->
1371                         // If the annotation cannot be resolved then keep it.
1372                         val annotationClass = annotationItem.resolve() ?: return@filter true
1373                         filter.test(annotationClass)
1374                     }
1375             )
1376         }
1377     }
1378 
1379 /**
1380  * Map the items in this list to a new list if [transform] returns at least one item which is not
1381  * the same instance as its input, otherwise return this.
1382  */
mapIfNotSamenull1383 fun <T> List<T>.mapIfNotSame(transform: (T) -> T): List<T> {
1384     if (isEmpty()) return this
1385     val newList = map(transform)
1386     val i1 = iterator()
1387     val i2 = newList.iterator()
1388     while (i1.hasNext() && i2.hasNext()) {
1389         val t1 = i1.next()
1390         val t2 = i2.next()
1391         if (t1 !== t2) return newList
1392     }
1393     return this
1394 }
1395 
1396 /**
1397  * Attempt to get the full name from the qualified name.
1398  *
1399  * The full name is the qualified name without the package including any outer class names.
1400  *
1401  * It relies on the convention that packages start with a lower case letter and classes start with
1402  * an upper case letter.
1403  */
bestGuessAtFullNamenull1404 fun bestGuessAtFullName(qualifiedName: String): String {
1405     val length = qualifiedName.length
1406     var prev: Char? = null
1407     var lastDotIndex = -1
1408     for (i in 0..length - 1) {
1409         val c = qualifiedName[i]
1410         if (prev == null || prev == '.') {
1411             if (c.isUpperCase()) {
1412                 return qualifiedName.substring(i)
1413             }
1414         }
1415         if (c == '.') {
1416             lastDotIndex = i
1417         }
1418         prev = c
1419     }
1420 
1421     return if (lastDotIndex == -1) {
1422         qualifiedName
1423     } else {
1424         qualifiedName.substring(lastDotIndex + 1)
1425     }
1426 }
1427