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