• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.text
18 
19 import com.android.tools.metalava.model.AnnotationItem
20 import com.android.tools.metalava.model.ArrayTypeItem
21 import com.android.tools.metalava.model.BaseTypeVisitor
22 import com.android.tools.metalava.model.ClassTypeItem
23 import com.android.tools.metalava.model.Codebase
24 import com.android.tools.metalava.model.DefaultAnnotationItem
25 import com.android.tools.metalava.model.JAVA_LANG_OBJECT
26 import com.android.tools.metalava.model.JAVA_LANG_PREFIX
27 import com.android.tools.metalava.model.PrimitiveTypeItem
28 import com.android.tools.metalava.model.ReferenceTypeItem
29 import com.android.tools.metalava.model.TypeArgumentTypeItem
30 import com.android.tools.metalava.model.TypeItem
31 import com.android.tools.metalava.model.TypeModifiers
32 import com.android.tools.metalava.model.TypeNullability
33 import com.android.tools.metalava.model.TypeParameterScope
34 import com.android.tools.metalava.model.TypeVisitor
35 import com.android.tools.metalava.model.VariableTypeItem
36 import com.android.tools.metalava.model.WildcardTypeItem
37 import com.android.tools.metalava.model.type.ContextNullability
38 import com.android.tools.metalava.model.type.DefaultArrayTypeItem
39 import com.android.tools.metalava.model.type.DefaultClassTypeItem
40 import com.android.tools.metalava.model.type.DefaultPrimitiveTypeItem
41 import com.android.tools.metalava.model.type.DefaultTypeModifiers
42 import com.android.tools.metalava.model.type.DefaultVariableTypeItem
43 import com.android.tools.metalava.model.type.DefaultWildcardTypeItem
44 import com.android.tools.metalava.reporter.Issues
45 import kotlin.collections.HashMap
46 
47 /** Parses and caches types for a [codebase]. */
48 internal class TextTypeParser(
49     val codebase: Codebase,
50     val kotlinStyleNulls: Boolean = false,
51     delegateErrorReporter: SignatureErrorReporter = SignatureErrorReporter.THROWING,
52 ) {
53     /**
54      * Tracks whether types that were unqualified and so implicitly treated as being part of the
55      * 'java.lang` package are actually part of that package. If they are not then an error is
56      * reported and it is not prefixed with `java.lang`.
57      */
58     private val javaLangPackage: JavaLangPackage = JavaLangPackage.DEFAULT
59 
60     /**
61      * A count of the errors reported through [errorReporter].
62      *
63      * This is used to prevent caching [TypeItem]s that reported errors to make sure that every such
64      * case is reported.
65      */
66     private var errorCount = 0
67 
68     /**
69      * Report a recoverable error.
70      *
71      * This keeps a count of how many were reported so that [CacheEntry.getTypeItem] can use that to
72      * determine if any errors were found while parsing a type ([errorCount] increased) and so
73      * prevent it from being cached which would suppress any more errors with that type string.
74      */
75     private val errorReporter: SignatureErrorReporter =
76         object : SignatureErrorReporter {
77             override fun report(
78                 issue: Issues.Issue,
79                 message: String,
80             ) {
81                 delegateErrorReporter.report(issue, message)
82                 errorCount += 1
83             }
84         }
85 
86     /**
87      * The cache key, incorporates some information from [ContextNullability] and [kotlinStyleNulls]
88      * as well as the type string as they can all affect the created [TypeItem].
89      *
90      * e.g. [ContextNullability.forceNonNull] will cause the type to always be
91      * [TypeNullability.NONNULL] even if [kotlinStyleNulls] is `false` which would normally cause it
92      * to be [TypeNullability.PLATFORM]. However, when [kotlinStyleNulls] is `true` then there is no
93      * difference between [ContextNullability.forceNonNull] and [ContextNullability.none] as they
94      * will both cause a class type with no nullability suffix to be treated as
95      * [TypeNullability.NONNULL].
96      *
97      * That information is encapsulated in the [forceClassToBeNonNull] property.
98      */
99     private data class Key(val forceClassToBeNonNull: Boolean, val type: String)
100 
101     /** The cache from [Key] to [CacheEntry]. */
102     private val typeCache = HashMap<Key, CacheEntry>()
103 
104     internal var requests = 0
105     internal var cacheSkip = 0
106     internal var cacheHit = 0
107     internal var cacheSize = 0
108 
109     /** A [TypeItem] representing `java.lang.Object`, suitable for general use. */
110     private val objectType: ReferenceTypeItem
111         get() = cachedParseType(JAVA_LANG_OBJECT, TypeParameterScope.empty) as ReferenceTypeItem
112 
113     /**
114      * Creates or retrieves from the cache a [TypeItem] representing [type], in the context of the
115      * type parameters from [typeParameterScope], if applicable.
116      */
117     fun obtainTypeFromString(
118         type: String,
119         typeParameterScope: TypeParameterScope,
120         contextNullability: ContextNullability = ContextNullability.none,
121     ): TypeItem = cachedParseType(type, typeParameterScope, emptyList(), contextNullability)
122 
123     /**
124      * Creates or retrieves from the cache a [TypeItem] representing [type], in the context of the
125      * type parameters from [typeParameterScope], if applicable.
126      *
127      * Used internally, as it has an extra [annotations] parameter that allows the annotations on
128      * array components to be correctly associated with the correct component. They are optional
129      * leading type-use annotations that have already been removed from the arrays type string.
130      */
131     private fun cachedParseType(
132         type: String,
133         typeParameterScope: TypeParameterScope,
134         annotations: List<AnnotationItem> = emptyList(),
135         contextNullability: ContextNullability = ContextNullability.none,
136     ): TypeItem {
137         requests++
138 
139         // Class types used as super types, i.e. in an extends or implements list are forced to be
140         // [TypeNullability.NONNULL], just as they would be if kotlinStyleNulls was true. Use the
141         // same cache key for both so that they reuse cached types where possible.
142         val forceClassToBeNonNull =
143             contextNullability.forcedNullability == TypeNullability.NONNULL || kotlinStyleNulls
144 
145         // Don't use the cache when there are type-use annotations not contained in the string.
146         return if (annotations.isEmpty()) {
147             val key = Key(forceClassToBeNonNull, type)
148 
149             // Get the cache entry for the supplied type and forceClassToBeNonNull.
150             val result =
151                 typeCache.computeIfAbsent(key) { CacheEntry(it.type, it.forceClassToBeNonNull) }
152 
153             // Get the appropriate [TypeItem], creating one if necessary.
154             result.getTypeItem(typeParameterScope)
155         } else {
156             cacheSkip++
157             parseType(type, typeParameterScope, annotations, forceClassToBeNonNull)
158         }
159     }
160 
161     /** Converts the [type] to a [TypeItem] in the context of the [typeParameterScope]. */
162     private fun parseType(
163         type: String,
164         typeParameterScope: TypeParameterScope,
165         annotations: List<AnnotationItem>,
166         // Forces a [ClassTypeItem] to have [TypeNullability.NONNULL]
167         forceClassToBeNonNull: Boolean = false,
168     ): TypeItem {
169         val (unannotated, annotationsFromString) = trimLeadingAnnotations(type)
170         val allAnnotations = annotations + annotationsFromString
171         val (withoutNullability, nullability) =
172             splitNullabilitySuffix(
173                 unannotated,
174                 // If forceClassToBeNonNull is true then a plain class type without any nullability
175                 // suffix must be treated as if it was not null, which is just how it would be
176                 // treated when kotlinStyleNulls is true. So, pretend that kotlinStyleNulls is true.
177                 kotlinStyleNulls || forceClassToBeNonNull,
178                 errorReporter,
179             )
180         val trimmed = withoutNullability.trim()
181 
182         // Figure out what kind of type this is.
183         //
184         // Start with variable as the type parameter scope allows us to determine whether something
185         // is a type parameter or not. Also, if a type parameter has the same name as a primitive
186         // type (possible in Kotlin, but not Java) then it will be treated as a type parameter not a
187         // primitive.
188         //
189         // Then try parsing as a primitive as while Kotlin classes can shadow primitive types
190         // they would need to be fully qualified.
191         return asVariable(trimmed, typeParameterScope, allAnnotations, nullability)
192             ?: asPrimitive(type, trimmed, allAnnotations, nullability)
193             // Try parsing as a wildcard before trying to parse as an array.
194             // `? extends java.lang.String[]` should be parsed as a wildcard with an array bound,
195             // not as an array of wildcards, for consistency with how this would be compiled.
196             ?: asWildcard(trimmed, typeParameterScope, allAnnotations, nullability)
197             // Try parsing as an array.
198             ?: asArray(trimmed, allAnnotations, nullability, typeParameterScope)
199             // If it isn't anything else, parse the type as a class.
200             ?: asClass(trimmed, typeParameterScope, allAnnotations, nullability)
201     }
202 
203     /**
204      * Try parsing [type] as a primitive. This will return a non-null [PrimitiveTypeItem] if [type]
205      * exactly matches a primitive name.
206      *
207      * [type] should have annotations and nullability markers stripped, with [original] as the
208      * complete annotated type. Once annotations are properly handled (b/300081840), preserving
209      * [original] won't be necessary.
210      */
211     private fun asPrimitive(
212         original: String,
213         type: String,
214         annotations: List<AnnotationItem>,
215         nullability: TypeNullability?
216     ): PrimitiveTypeItem? {
217         val kind =
218             when (type) {
219                 "byte" -> PrimitiveTypeItem.Primitive.BYTE
220                 "char" -> PrimitiveTypeItem.Primitive.CHAR
221                 "double" -> PrimitiveTypeItem.Primitive.DOUBLE
222                 "float" -> PrimitiveTypeItem.Primitive.FLOAT
223                 "int" -> PrimitiveTypeItem.Primitive.INT
224                 "long" -> PrimitiveTypeItem.Primitive.LONG
225                 "short" -> PrimitiveTypeItem.Primitive.SHORT
226                 "boolean" -> PrimitiveTypeItem.Primitive.BOOLEAN
227                 "void" -> PrimitiveTypeItem.Primitive.VOID
228                 else -> return null
229             }
230         if (nullability != null && nullability != TypeNullability.NONNULL) {
231             errorReporter.report("Invalid nullability suffix on primitive: $original")
232         }
233         return DefaultPrimitiveTypeItem(modifiers(annotations, TypeNullability.NONNULL), kind)
234     }
235 
236     /**
237      * Try parsing [type] as an array. This will return a non-null [ArrayTypeItem] if [type] ends
238      * with `[]` or `...`.
239      *
240      * The context [typeParameterScope] are used to parse the component type of the array.
241      */
242     private fun asArray(
243         type: String,
244         componentAnnotations: List<AnnotationItem>,
245         nullability: TypeNullability?,
246         typeParameterScope: TypeParameterScope
247     ): ArrayTypeItem? {
248         // Check if this is a regular array or varargs.
249         val (inner, varargs) =
250             if (type.endsWith("...")) {
251                 Pair(type.dropLast(3), true)
252             } else if (type.endsWith("[]")) {
253                 Pair(type.dropLast(2), false)
254             } else {
255                 return null
256             }
257 
258         // Create lists of the annotations and nullability markers for each dimension of the array.
259         // These are in separate lists because annotations appear in the type string in order from
260         // outermost array annotations to innermost array annotations (for `T @A [] @B [] @ C[]`,
261         // `@A` applies to the three-dimensional array, `@B` applies to the inner two-dimensional
262         // arrays, and `@C` applies to the inner one-dimensional arrays), while nullability markers
263         // appear in order from the innermost array nullability to the outermost array nullability
264         // (for `T[]![]?[]`, the three-dimensional array has no nullability marker, the inner
265         // two-dimensional arrays have `?` as the nullability marker, and the innermost arrays have
266         // `!` as a nullability marker.
267         val allAnnotations = mutableListOf<List<AnnotationItem>>()
268         // The nullability marker for the outer array is already known, include it in the list.
269         val allNullability = mutableListOf(nullability)
270 
271         // Remove annotations from the end of the string, add them to the list.
272         var annotationsResult = trimTrailingAnnotations(inner)
273         var componentString = annotationsResult.first
274         allAnnotations.add(annotationsResult.second)
275 
276         // Remove nullability marker from the component type, but don't add it to the list yet, as
277         // it might not be an array.
278         var nullabilityResult =
279             splitNullabilitySuffix(
280                 componentString,
281                 kotlinStyleNulls,
282                 errorReporter,
283             )
284         componentString = nullabilityResult.first
285         var componentNullability = nullabilityResult.second
286 
287         // Work through all layers of arrays to get to the inner component type.
288         // Inner arrays can't be varargs.
289         while (componentString.endsWith("[]")) {
290             // The component is an array, add the nullability to the list.
291             allNullability.add(componentNullability)
292 
293             // Remove annotations from the end of the string, add them to the list.
294             annotationsResult = trimTrailingAnnotations(componentString.removeSuffix("[]"))
295             componentString = annotationsResult.first
296             allAnnotations.add(annotationsResult.second)
297 
298             // Remove nullability marker from the new component type, but don't add it to the list
299             // yet, as the next component type might not be an array.
300             nullabilityResult =
301                 splitNullabilitySuffix(
302                     componentString,
303                     kotlinStyleNulls,
304                     errorReporter,
305                 )
306             componentString = nullabilityResult.first
307             componentNullability = nullabilityResult.second
308         }
309 
310         // Re-add the component's nullability suffix when parsing the component type, and include
311         // the leading annotations already removed from the type string.
312         componentString += componentNullability?.suffix.orEmpty()
313         val deepComponentType =
314             cachedParseType(componentString, typeParameterScope, componentAnnotations)
315 
316         // Join the annotations and nullability markers -- as described in the comment above, these
317         // appear in the string in reverse order of each other. The modifiers list will be ordered
318         // from innermost array modifiers to outermost array modifiers.
319         val allModifiers =
320             allAnnotations.zip(allNullability.reversed()).map { (annotations, nullability) ->
321                 modifiers(annotations, nullability)
322             }
323         // The final modifiers are in the list apply to the outermost array.
324         val componentModifiers = allModifiers.dropLast(1)
325         val arrayModifiers = allModifiers.last()
326         // Create the component type of the outermost array by building up the inner component type.
327         val componentType =
328             componentModifiers.fold(deepComponentType) { component, modifiers ->
329                 DefaultArrayTypeItem(modifiers, component, false)
330             }
331 
332         // Create the outer array.
333         return DefaultArrayTypeItem(arrayModifiers, componentType, varargs)
334     }
335 
336     /**
337      * Try parsing [type] as a wildcard. This will return a non-null [WildcardTypeItem] if [type]
338      * begins with `?`.
339      *
340      * The context [typeParameterScope] are needed to parse the bounds of the wildcard.
341      *
342      * [type] should have annotations and nullability markers stripped.
343      */
344     private fun asWildcard(
345         type: String,
346         typeParameterScope: TypeParameterScope,
347         annotations: List<AnnotationItem>,
348         nullability: TypeNullability?
349     ): WildcardTypeItem? {
350         // See if this is a wildcard
351         if (!type.startsWith("?")) return null
352 
353         val modifiers = modifiers(annotations, TypeNullability.UNDEFINED)
354 
355         // Unbounded wildcard type: there is an implicit Object extends bound
356         if (type == "?") return DefaultWildcardTypeItem(modifiers, objectType, null)
357 
358         // If there's a bound, the nullability suffix applies there instead.
359         val bound = type.substring(2) + nullability?.suffix.orEmpty()
360         return if (bound.startsWith("extends")) {
361             val extendsBound = bound.substring(8)
362             DefaultWildcardTypeItem(
363                 modifiers,
364                 getWildcardBound(extendsBound, typeParameterScope),
365                 null,
366             )
367         } else if (bound.startsWith("super")) {
368             val superBound = bound.substring(6)
369             DefaultWildcardTypeItem(
370                 modifiers,
371                 // All wildcards have an implicit Object extends bound
372                 objectType,
373                 getWildcardBound(superBound, typeParameterScope),
374             )
375         } else {
376             errorReporter.report("Type starts with \"?\" but doesn't appear to be wildcard: $type")
377 
378             // Ignore the part after the "?" and treat it as an unbounded wildcard.
379             DefaultWildcardTypeItem(modifiers, objectType, null)
380         }
381     }
382 
383     private fun getWildcardBound(bound: String, typeParameterScope: TypeParameterScope) =
384         cachedParseType(bound, typeParameterScope) as ReferenceTypeItem
385 
386     /**
387      * Try parsing [type] as a type variable. This will return a non-null [VariableTypeItem] if
388      * [type] matches a parameter from [typeParameterScope].
389      *
390      * [type] should have annotations and nullability markers stripped.
391      */
392     private fun asVariable(
393         type: String,
394         typeParameterScope: TypeParameterScope,
395         annotations: List<AnnotationItem>,
396         nullability: TypeNullability?
397     ): VariableTypeItem? {
398         val param = typeParameterScope.findTypeParameter(type) ?: return null
399         return DefaultVariableTypeItem(modifiers(annotations, nullability), param)
400     }
401 
402     /**
403      * Parse the [type] as a class. This function will always return a non-null [ClassTypeItem], so
404      * it should only be used when it is certain that [type] is not a different kind of type.
405      *
406      * The context [typeParameterScope] are used to parse the parameters of the class type.
407      *
408      * [type] should have annotations and nullability markers stripped.
409      */
410     private fun asClass(
411         type: String,
412         typeParameterScope: TypeParameterScope,
413         annotations: List<AnnotationItem>,
414         nullability: TypeNullability?
415     ): ClassTypeItem {
416         return createClassType(type, null, typeParameterScope, annotations, nullability)
417     }
418 
419     /**
420      * Creates a class name for the class represented by [type] with optional [outerClassType].
421      *
422      * For instance, `test.pkg.Outer<P1>` would be the [outerClassType] when parsing `Inner<P2>`
423      * from the [original] type `test.pkg.Outer<P1>.Inner<P2>`.
424      */
425     private fun createClassType(
426         type: String,
427         outerClassType: ClassTypeItem?,
428         typeParameterScope: TypeParameterScope,
429         annotations: List<AnnotationItem>,
430         nullability: TypeNullability?
431     ): ClassTypeItem {
432         val (name, afterName, classAnnotations) = splitClassType(type)
433 
434         val qualifiedName =
435             if (outerClassType != null) {
436                 // This is a nested type, add the prefix of the outer name
437                 "${outerClassType.qualifiedName}.$name"
438             } else if (!name.contains('.')) {
439                 val javaLangName = "java.lang.$name"
440                 if (javaLangPackage.containsQualified(javaLangName)) {
441                     // Reverse the effect of [TypeItem.stripJavaLangPrefix].
442                     javaLangName
443                 } else {
444                     errorReporter.report(
445                         Issues.UNQUALIFIED_TYPE_ERROR,
446                         "Unqualified type '$name' is not in 'java.lang' and is not a type parameter in scope"
447                     )
448                     name
449                 }
450             } else {
451                 name
452             }
453 
454         val (argumentStrings, remainder) = typeParameterStringsWithRemainder(afterName)
455         val arguments =
456             argumentStrings.map { cachedParseType(it, typeParameterScope) as TypeArgumentTypeItem }
457         // If this is an outer class type (there's a remainder), call it non-null and don't apply
458         // the leading annotations (they belong to the nested class type).
459         val classModifiers =
460             if (remainder != null) {
461                 modifiers(classAnnotations, TypeNullability.NONNULL)
462             } else {
463                 modifiers(classAnnotations + annotations, nullability)
464             }
465         val classType =
466             DefaultClassTypeItem(codebase, classModifiers, qualifiedName, arguments, outerClassType)
467 
468         if (remainder != null) {
469             if (!remainder.startsWith('.')) {
470                 errorReporter.report(
471                     "Could not parse type `$type`. Found unexpected string after type parameters: $remainder"
472                 )
473                 // Ignore the remainder.
474                 return classType
475             }
476 
477             // This is a nested class type, recur with the new outer class
478             return createClassType(
479                 remainder.substring(1),
480                 classType,
481                 typeParameterScope,
482                 annotations,
483                 nullability
484             )
485         }
486 
487         return classType
488     }
489 
490     private fun modifiers(
491         annotations: List<AnnotationItem>,
492         nullability: TypeNullability?
493     ): TypeModifiers {
494         return DefaultTypeModifiers.create(
495             annotations,
496             nullability,
497         )
498     }
499 
500     /**
501      * Removes all annotations at the beginning of the type, returning the trimmed type and list of
502      * annotations.
503      */
504     fun trimLeadingAnnotations(type: String): Pair<String, List<AnnotationItem>> {
505         val annotations = mutableListOf<AnnotationItem>()
506         var trimmed = type.trim()
507         while (trimmed.startsWith('@')) {
508             val end = findAnnotationEnd(trimmed, 1)
509             val annotationSource = trimmed.substring(0, end).trim()
510             DefaultAnnotationItem.create(codebase, annotationSource)?.let { annotationItem ->
511                 annotations.add(annotationItem)
512             }
513             trimmed = trimmed.substring(end).trim()
514         }
515         return Pair(trimmed, annotations)
516     }
517 
518     /**
519      * Removes all annotations at the end of the [type], returning the trimmed type and list of
520      * annotations. This is for use with arrays where annotations applying to the array type go
521      * after the component type, for instance `String @A []`. The input [type] should **not**
522      * include the array suffix (`[]` or `...`).
523      */
524     fun trimTrailingAnnotations(type: String): Pair<String, List<AnnotationItem>> {
525         // The simple way to implement this would be to work from the end of the string, finding
526         // `@` and removing annotations from the end. However, it is possible for an annotation
527         // string to contain an `@`, so this is not a safe way to remove the annotations.
528         // Instead, this finds all annotations starting from the beginning of the string, then
529         // works backwards to find which ones are the trailing annotations.
530         val allAnnotationIndices = mutableListOf<Pair<Int, Int>>()
531         var trimmed = type.trim()
532 
533         // First find all annotations, saving the first and last index.
534         var currIndex = 0
535         while (currIndex < trimmed.length) {
536             if (trimmed[currIndex] == '@') {
537                 val endIndex = findAnnotationEnd(trimmed, currIndex + 1)
538                 allAnnotationIndices.add(Pair(currIndex, endIndex))
539                 currIndex = endIndex + 1
540             } else {
541                 currIndex++
542             }
543         }
544 
545         val annotations = mutableListOf<AnnotationItem>()
546         // Go through all annotations from the back, seeing if they're at the end of the string.
547         for ((start, end) in allAnnotationIndices.reversed()) {
548             // This annotation isn't at the end, so we've hit the last trailing annotation
549             if (end < trimmed.length) {
550                 break
551             }
552             val annotationSource = trimmed.substring(start)
553             DefaultAnnotationItem.create(codebase, annotationSource)?.let { annotationItem ->
554                 annotations.add(annotationItem)
555             }
556             // Cut this annotation off, so now the next one can end at the last index.
557             trimmed = trimmed.substring(0, start).trim()
558         }
559         return Pair(trimmed, annotations.reversed())
560     }
561 
562     /**
563      * Given [type] which represents a class, splits the string into the qualified name of the
564      * class, the remainder of the type string, and a list of type-use annotations. The remainder of
565      * the type string might be the type parameter list, nested class names, or a combination
566      *
567      * For `java.util.@A @B List<java.lang.@C String>`, returns the triple ("java.util.List",
568      * "<java.lang.@C String", listOf("@A", "@B")).
569      *
570      * For `test.pkg.Outer.Inner`, returns the triple ("test.pkg.Outer", ".Inner", emptyList()).
571      *
572      * For `test.pkg.@test.pkg.A Outer<P1>.@test.pkg.B Inner<P2>`, returns the triple
573      * ("test.pkg.Outer", "<P1>.@test.pkg.B Inner<P2>", listOf("@test.pkg.A")).
574      */
575     fun splitClassType(type: String): Triple<String, String?, List<AnnotationItem>> {
576         // The constructed qualified type name
577         var name = ""
578         // The part of the type which still needs to be parsed
579         var remaining = type.trim()
580         // The annotations of the type, may be set later
581         var annotations = emptyList<AnnotationItem>()
582 
583         var dotIndex = remaining.indexOf('.')
584         var paramIndex = remaining.indexOf('<')
585         var annotationIndex = remaining.indexOf('@')
586 
587         // Find which of '.', '<', or '@' comes first, if any
588         var minIndex = minIndex(dotIndex, paramIndex, annotationIndex)
589         while (minIndex != null) {
590             when (minIndex) {
591                 // '.' is first, the next part is part of the qualified class name.
592                 dotIndex -> {
593                     val nextNameChunk = remaining.substring(0, dotIndex)
594                     name += nextNameChunk
595                     remaining = remaining.substring(dotIndex)
596                     // Assumes that package names are all lower case and class names will have
597                     // an upper class character (the [START_WITH_UPPER] API lint check should
598                     // make this a safe assumption). If the name is a class name, we've found
599                     // the complete class name, return.
600                     if (nextNameChunk.any { it.isUpperCase() }) {
601                         return Triple(name, remaining, annotations)
602                     }
603                 }
604                 // '<' is first, the end of the class name has been reached.
605                 paramIndex -> {
606                     name += remaining.substring(0, paramIndex)
607                     remaining = remaining.substring(paramIndex)
608                     return Triple(name, remaining, annotations)
609                 }
610                 // '@' is first, trim all annotations.
611                 annotationIndex -> {
612                     name += remaining.substring(0, annotationIndex)
613                     trimLeadingAnnotations(remaining.substring(annotationIndex)).let {
614                         (first, second) ->
615                         remaining = first
616                         annotations = second
617                     }
618                 }
619             }
620             // Reset indices -- the string may now start with '.' for the next chunk of the name
621             // but this should find the end of the next chunk.
622             dotIndex = remaining.indexOf('.', 1)
623             paramIndex = remaining.indexOf('<')
624             annotationIndex = remaining.indexOf('@')
625             minIndex = minIndex(dotIndex, paramIndex, annotationIndex)
626         }
627         // End of the name reached with no leftover string.
628         name += remaining
629         return Triple(name, null, annotations)
630     }
631 
632     companion object {
633         /**
634          * Splits the Kotlin-style nullability marker off the type string, returning a pair of the
635          * cleaned type string and the nullability suffix.
636          */
637         fun splitNullabilitySuffix(
638             type: String,
639             kotlinStyleNulls: Boolean,
640             errorReporter: SignatureErrorReporter = SignatureErrorReporter.THROWING,
641         ): Pair<String, TypeNullability?> {
642             return if (kotlinStyleNulls) {
643                 // Don't interpret the wildcard type `?` as a nullability marker.
644                 if (type == "?") {
645                     Pair(type, TypeNullability.UNDEFINED)
646                 } else if (type.endsWith("?")) {
647                     Pair(type.dropLast(1), TypeNullability.NULLABLE)
648                 } else if (type.endsWith("!")) {
649                     Pair(type.dropLast(1), TypeNullability.PLATFORM)
650                 } else {
651                     Pair(type, TypeNullability.NONNULL)
652                 }
653             } else if (((type.length > 1) && type.endsWith("?")) || type.endsWith("!")) {
654                 errorReporter.report("Format does not support Kotlin-style null type syntax: $type")
655                 Pair(type.dropLast(1), TypeNullability.PLATFORM)
656             } else {
657                 Pair(type, null)
658             }
659         }
660 
661         /**
662          * Returns the minimum valid list index from the input, or null if there isn't one. -1 is
663          * not a valid index.
664          */
665         private fun minIndex(vararg index: Int): Int? = index.filter { it != -1 }.minOrNull()
666 
667         /**
668          * Given a string and the index in that string which is the start of an annotation (the
669          * character _after_ the `@`), returns the index of the end of the annotation.
670          */
671         fun findAnnotationEnd(type: String, start: Int): Int {
672             var index = start
673             val length = type.length
674             var balance = 0
675             while (index < length) {
676                 val c = type[index]
677                 if (c == '(') {
678                     balance++
679                 } else if (c == ')') {
680                     balance--
681                     if (balance == 0) {
682                         return index + 1
683                     }
684                 } else if (c != '.' && !Character.isJavaIdentifierPart(c) && balance == 0) {
685                     break
686                 }
687                 index++
688             }
689             return index
690         }
691 
692         /**
693          * Breaks a string representing type parameters into a list of the type parameter strings.
694          *
695          * E.g. `"<A, B, C>"` -> `["A", "B", "C"]` and `"<List<A>, B>"` -> `["List<A>", "B"]`.
696          */
697         fun typeParameterStrings(typeString: String?): List<String> {
698             return typeParameterStringsWithRemainder(typeString).first
699         }
700 
701         /**
702          * Breaks a string representing type parameters into a list of the type parameter strings,
703          * and also returns the remainder of the string after the closing ">".
704          *
705          * E.g. `"<A, B, C>.Inner"` -> `Pair(["A", "B", "C"], ".Inner")`
706          */
707         fun typeParameterStringsWithRemainder(typeString: String?): Pair<List<String>, String?> {
708             val s = typeString ?: return Pair(emptyList(), null)
709             if (!s.startsWith("<")) return Pair(emptyList(), s)
710             val list = mutableListOf<String>()
711             var balance = 0
712             var expect = false
713             var start = 0
714             var i = 0
715             while (i < s.length) {
716                 val c = s[i]
717                 if (c == '<') {
718                     balance++
719                     expect = balance == 1
720                 } else if (c == '>') {
721                     balance--
722                     if (balance == 0) {
723                         add(list, s, start, i)
724                         return if (i == s.length - 1) {
725                             Pair(list, null)
726                         } else {
727                             Pair(list, s.substring(i + 1))
728                         }
729                     }
730                 } else if (c == ',') {
731                     expect =
732                         if (balance == 1) {
733                             add(list, s, start, i)
734                             true
735                         } else {
736                             false
737                         }
738                 } else {
739                     // This is the start of a parameter
740                     if (expect && balance == 1) {
741                         start = i
742                         expect = false
743                     }
744 
745                     if (c == '@') {
746                         // Skip the entire text of the annotation
747                         i = findAnnotationEnd(typeString, i + 1)
748                         continue
749                     }
750                 }
751                 i++
752             }
753             return Pair(list, null)
754         }
755 
756         /**
757          * Adds the substring of [s] from [from] to [to] to the [list], trimming whitespace from the
758          * front.
759          */
760         private fun add(list: MutableList<String>, s: String, from: Int, to: Int) {
761             for (i in from until to) {
762                 if (!Character.isWhitespace(s[i])) {
763                     list.add(s.substring(i, to))
764                     return
765                 }
766             }
767         }
768     }
769 
770     /**
771      * The cache entry, that contains the [TypeItem] that has been produced from the [type] and
772      * [forceClassToBeNonNull] properties.
773      */
774     internal inner class CacheEntry(
775         /** The string type from which the [TypeItem] will be parsed. */
776         private val type: String,
777 
778         /**
779          * Indicates whether an outermost [ClassTypeItem] is forced to be [TypeNullability.NONNULL].
780          *
781          * It is passed into [parseType] and if `true` it will cause the top level class type to be
782          * treated as if it was being parsed when [kotlinStyleNulls] is `true` as that sets
783          * [TypeNullability.NONNULL] by default.
784          */
785         private val forceClassToBeNonNull: Boolean,
786     ) {
787         /**
788          * Map from [TypeParameterScope] to the [TypeItem] created for it.
789          *
790          * The [TypeParameterScope] that will be used to cache a type depends on the unqualified
791          * names used in the type. It will use the closest enclosing scope of the one supplied that
792          * adds at least one type parameter whose name is used in the type.
793          *
794          * See [TypeParameterScope.findSignificantScope].
795          */
796         private val scopeToItem = mutableMapOf<TypeParameterScope, TypeItem>()
797 
798         /**
799          * The set of unqualified names used by [type].
800          *
801          * This is determined solely by the contents of the [type] string and so will be the same
802          * for all [TypeItem]s cached in this entry.
803          *
804          * If this has not been set then no type items have been cached in this entry. It is set the
805          * first time that a [TypeItem] is cached.
806          */
807         private lateinit var unqualifiedNamesInType: Set<String>
808 
809         /** Get the [TypeItem] for this type depending on the setting of [forceClassToBeNonNull]. */
810         fun getTypeItem(typeParameterScope: TypeParameterScope): TypeItem {
811             // If this is not the first time through then check to see if anything suitable has been
812             // cached.
813             val scopeForCachingOrNull =
814                 if (::unqualifiedNamesInType.isInitialized) {
815                     // Find the scope to use for caching this type and then check to see if a
816                     // [TypeItem]
817                     // has been cached for that scope and if so return it. Otherwise, drop out.
818                     typeParameterScope.findSignificantScope(unqualifiedNamesInType).also {
819                         scopeForCaching ->
820                         scopeToItem[scopeForCaching]?.let {
821                             cacheHit++
822                             return it
823                         }
824                     }
825                 } else {
826                     // This is the first time through, so [unqualifiedNamesInType] is not available
827                     // so drop through and initialize later.
828                     null
829                 }
830 
831             // Remember the number of errors that have been reported so far.
832             val startErrorCount = errorCount
833 
834             // Parse the [type] to produce a [TypeItem]. This may report errors.
835             val typeItem = createTypeItem(typeParameterScope)
836 
837             // If the error count is different then do not cache this.
838             if (errorCount != startErrorCount) {
839                 return typeItem
840             }
841 
842             cacheSize++
843 
844             // Find the scope for caching if it was not found above.
845             val scopeForCaching =
846                 scopeForCachingOrNull
847                     ?: let {
848                         // This will only happen if [unqualifiedNamesInType] is uninitialized so
849                         // make sure to initialize it.
850                         unqualifiedNamesInType = unqualifiedNameGatherer.gatherFrom(typeItem)
851 
852                         // Find the scope for caching. It could not be found before because
853                         // [unqualifiedNamesInType] was not initialized.
854                         typeParameterScope.findSignificantScope(unqualifiedNamesInType)
855                     }
856 
857             // Store the type item in the scope selected for caching.
858             scopeToItem[scopeForCaching] = typeItem
859 
860             // Return it.
861             return typeItem
862         }
863 
864         /**
865          * Create a new [TypeItem] for [type] with the given [forceClassToBeNonNull] setting and for
866          * the requested [typeParameterScope].
867          */
868         private fun createTypeItem(typeParameterScope: TypeParameterScope): TypeItem {
869             return parseType(type, typeParameterScope, emptyList(), forceClassToBeNonNull)
870         }
871     }
872 
873     /**
874      * A [TypeVisitor] that will extract all unqualified names from the type.
875      *
876      * These are the names that could be used as a type parameter name and so whose meaning could
877      * change depending on the [TypeParameterScope], i.e. the set of type parameters currently in
878      * scope.
879      */
880     private class UnqualifiedNameGatherer : BaseTypeVisitor() {
881 
882         private val unqualifiedNames = mutableSetOf<String>()
883 
884         override fun visit(primitiveType: PrimitiveTypeItem) {
885             // Primitive type names are added because Kotlin allows them to be shadowed by a type
886             // parameter.
887             unqualifiedNames.add(primitiveType.kind.primitiveName)
888         }
889 
890         override fun visitClassType(classType: ClassTypeItem) {
891             // Classes in java.lang package can be represented in the type without the leading
892             // package, all other types must be fully qualified. At this point it is not clear
893             // whether the type used in the input type string was qualified or not as the package
894             // has been prepended so this assumes that they all are just to be on the safe side.
895             val name = classType.qualifiedName
896             if (!name.contains('.')) {
897                 unqualifiedNames.add(name)
898             } else {
899                 if (classType.classNamePrefix == JAVA_LANG_PREFIX) {
900                     unqualifiedNames.add(classType.className)
901                 }
902             }
903         }
904 
905         override fun visitVariableType(variableType: VariableTypeItem) {
906             unqualifiedNames.add(variableType.name)
907         }
908 
909         /** Gather the names from [typeItem] returning an immutable set of the unqualified names. */
910         fun gatherFrom(typeItem: TypeItem): Set<String> {
911             unqualifiedNames.clear()
912             typeItem.accept(this)
913             return unqualifiedNames.toSet()
914         }
915     }
916 
917     /**
918      * An instance of [UnqualifiedNameGatherer] used for gathering all the unqualified names from
919      * all the [TypeItem]s cached by this.
920      */
921     private val unqualifiedNameGatherer = UnqualifiedNameGatherer()
922 }
923