1 /* <lambda>null2 * Copyright (C) 2024 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.type 18 19 import com.android.tools.metalava.model.AnnotationItem 20 import com.android.tools.metalava.model.BoundsTypeItem 21 import com.android.tools.metalava.model.ClassTypeItem 22 import com.android.tools.metalava.model.ExceptionTypeItem 23 import com.android.tools.metalava.model.Item 24 import com.android.tools.metalava.model.PrimitiveTypeItem 25 import com.android.tools.metalava.model.TypeItem 26 import com.android.tools.metalava.model.TypeNullability 27 import com.android.tools.metalava.model.TypeParameterItem 28 import com.android.tools.metalava.model.TypeParameterScope 29 import com.android.tools.metalava.model.WildcardTypeItem 30 import com.android.tools.metalava.model.typeNullability 31 32 /** 33 * Factory for building [TypeItem] from the underlying model specific representation of the type. 34 * 35 * The purpose of this is to abstract away all the model specific details of type construction in a 36 * consistent manner across the models so that the types can be constructed in a consistent manner. 37 * It provides specialist functions for different type uses that can ensure the specific constraints 38 * of those type uses are enforced. e.g. types used for super class and interfaces cannot be 39 * nullable. 40 * 41 * At the moment the specialist functions are limited to just a few different types but over time it 42 * will be expanded as more type creation logic is moved inside. 43 * 44 * @param T the underlying model specific representation of a type. 45 * @param F the type of the factory implementation. 46 */ 47 interface TypeItemFactory<in T, F : TypeItemFactory<T, F>> { 48 49 /** 50 * The [TypeParameterScope] that is used by this factory to resolve references to 51 * [TypeParameterItem]s. 52 */ 53 val typeParameterScope: TypeParameterScope 54 55 /** 56 * Create a [TypeItemFactory] that can resolve references to the [typeParameters]. 57 * 58 * Returns `this` if [typeParameters] is empty, otherwise returns a new [TypeItemFactory] with a 59 * new [typeParameterScope] with the specified [scopeDescription] and containing the supplied 60 * [typeParameters]. 61 */ 62 fun nestedFactory(scopeDescription: String, typeParameters: List<TypeParameterItem>): F 63 64 /** Get a type suitable for use in a wildcard type bounds clause. */ 65 fun getBoundsType(underlyingType: T): BoundsTypeItem 66 67 /** Get a type suitable for use in a `throws` list. */ 68 fun getExceptionType(underlyingType: T): ExceptionTypeItem 69 70 /** 71 * Get a general type suitable for use anywhere not covered by one of the more specific type 72 * methods in this. 73 */ 74 fun getGeneralType(underlyingType: T): TypeItem 75 76 /** 77 * Get a type suitable for use in an `implements` list of a concrete class, or an `extends` list 78 * of an interface class. 79 */ 80 fun getInterfaceType(underlyingType: T): ClassTypeItem 81 82 /** Get a type suitable for use in an `extends` clause of a concrete class. */ 83 fun getSuperClassType(underlyingType: T): ClassTypeItem 84 85 // Item specific type methods. 86 87 /** 88 * Get the type for a field. 89 * 90 * This considers a number of factors, in addition to the declared type, to determine the 91 * appropriate [TypeNullability] for the field type, i.e.: 92 * * Any [AnnotationItem.typeNullability] annotations in [itemAnnotations]. 93 * * Setting of [isEnumConstant]; if it is `true` then it is always [TypeNullability.NONNULL]. 94 * * If the field is `final` then the nullability of the field's value can be considered 95 * ([isInitialValueNonNull]). Otherwise, it cannot as it may change over the lifetime of the 96 * field. 97 * 98 * @param underlyingType the underlying model's type. 99 * @param itemAnnotations the annotations on the field (not the type). 100 * @param isFinal `true` if the field is `final`. 101 * @param isEnumConstant `true` if the field is actually an enum constant. 102 * @param isInitialValueNonNull a lambda that will be invoked on `final` fields to determine 103 * whether its initial value is non-null. This is a lambda as the determination of the initial 104 * value may be expensive. 105 */ 106 fun getFieldType( 107 underlyingType: T, 108 itemAnnotations: List<AnnotationItem>, 109 isEnumConstant: Boolean, 110 isFinal: Boolean, 111 isInitialValueNonNull: () -> Boolean, 112 ): TypeItem = error("unsupported") 113 114 /** 115 * Get the parameter type for a method (or constructor). 116 * 117 * This considers a number of factors, in addition to the declared type, to determine the 118 * appropriate [TypeNullability] for the method parameter type, i.e.: 119 * * Any [AnnotationItem.typeNullability] annotations in [itemAnnotations]. 120 * * Method [fingerprint], which may match a known method whose return type has a known 121 * [TypeNullability]. 122 * 123 * @param underlyingParameterType the underlying model's type. 124 * @param itemAnnotations the annotations on the method parameter (not the type). 125 * @param fingerprint method fingerprint 126 * @param parameterIndex the index of the parameter in the method's list of parameters. 127 * @param isVarArg whether this parameter is a vararg parameter. This is provided separately to 128 * the [underlyingParameterType] because while some models encapsulate that information within 129 * the type not all do. 130 */ 131 fun getMethodParameterType( 132 underlyingParameterType: T, 133 itemAnnotations: List<AnnotationItem>, 134 fingerprint: MethodFingerprint, 135 parameterIndex: Int, 136 isVarArg: Boolean, 137 ): TypeItem = error("unsupported") 138 139 /** 140 * Get the return type for a method. 141 * 142 * This considers a number of factors, in addition to the declared type, to determine the 143 * appropriate [TypeNullability] for the field type, i.e.: 144 * * If it [isAnnotationElement], they must be [TypeNullability.NONNULL]. 145 * * Method [fingerprint], which may match a known method whose return type has a known 146 * [TypeNullability]. 147 * 148 * @param underlyingReturnType the underlying model's return type. 149 * @param itemAnnotations the annotations on the field (not the type). 150 * @param fingerprint method fingerprint 151 * @param isAnnotationElement true for a non-static method of an annotation class. 152 */ 153 fun getMethodReturnType( 154 underlyingReturnType: T, 155 itemAnnotations: List<AnnotationItem>, 156 fingerprint: MethodFingerprint, 157 isAnnotationElement: Boolean, 158 ): TypeItem = error("unsupported") 159 } 160 161 /** 162 * A fingerprint of a method that is used to determined if it is a known method with known 163 * nullability. 164 */ 165 data class MethodFingerprint( 166 /** The name of the method. */ 167 val name: String, 168 /** The number of parameters. */ 169 val parameterCount: Int, 170 ) 171 172 /** 173 * Encapsulates the information necessary to compute the [TypeNullability] from a variety of 174 * different sources with differing priorities. 175 * 176 * The priorities are: 177 * 1. Forced by specification, e.g. enum constants, super class types, primitives. 178 * 2. Kotlin; ignoring [TypeNullability.PLATFORM]. 179 * 3. Annotations. 180 * 4. Nullability inferred from context, e.g. constant field with non-null value. 181 * 4. [TypeNullability.PLATFORM] 182 */ 183 class ContextNullability( 184 /** 185 * The [TypeNullability] that a [TypeItem] MUST have by virtue of what the type is, or where it 186 * is used; e.g. [PrimitiveTypeItem]s and super class types MUST be [TypeNullability.NONNULL] 187 * while [WildcardTypeItem]s MUST be [TypeNullability.UNDEFINED]. 188 * 189 * This CANNOT be overridden by a nullability annotation. 190 */ 191 val forcedNullability: TypeNullability? = null, 192 193 /** 194 * The [TypeNullability] that a [TypeItem] that is a component of an array MUST have. 195 * 196 * This is used to ensure that annotation attributes, i.e. methods on an annotation class, that 197 * return arrays cannot return arrays of a nullable component type. 198 * 199 * This CANNOT be overridden by a nullability annotation. 200 */ 201 val forcedComponentNullability: TypeNullability? = null, 202 203 /** 204 * The annotations from the [Item] whose type this is. 205 * 206 * If supplied then this may be used by [compute] to determine the [TypeNullability] of the 207 * constructed type. 208 */ 209 val itemAnnotations: List<AnnotationItem>? = null, 210 211 /** 212 * A [TypeNullability] that can be inferred from the context. 213 * 214 * It is passed as a lambda as it may be expensive to compute. 215 */ 216 val inferNullability: (() -> TypeNullability?)? = null, 217 ) { 218 /** 219 * Compute the [TypeNullability] according to the priority in the documentation for this class. 220 */ computenull221 fun compute( 222 kotlinNullability: TypeNullability?, 223 typeAnnotations: List<AnnotationItem> 224 ): TypeNullability = 225 // If forced is set then use that as the top priority. 226 forcedNullability 227 // If kotlin provides it then use that as it is most accurate, ignore PLATFORM though 228 // as that may be overridden by annotations or the default. 229 ?: kotlinNullability?.takeIf { nullability -> nullability != TypeNullability.PLATFORM } 230 // If annotations provide it then use them as the developer requested. 231 ?: typeAnnotations.typeNullability 232 // If item annotations are found then check them. 233 ?: itemAnnotations?.typeNullability 234 // If an inferred nullability is provided then use it. 235 ?: inferNullability?.invoke() 236 // Finally default to [TypeNullability.PLATFORM]. 237 ?: TypeNullability.PLATFORM 238 239 /** 240 * Get a [ContextNullability] instance for components of arrays. 241 * 242 * If [forcedComponentNullability] is null then this returns [ContextNullability.none], 243 * otherwise it returns a [ContextNullability] whose [forcedNullability] is set to 244 * [forcedComponentNullability]. 245 */ forComponentTypenull246 fun forComponentType() = 247 forcedComponentNullability?.let { ContextNullability(forcedNullability = it) } ?: none 248 249 companion object { 250 val none = ContextNullability() 251 val forceNonNull = ContextNullability(TypeNullability.NONNULL) 252 val forceUndefined = ContextNullability(TypeNullability.UNDEFINED) 253 } 254 } 255 256 abstract class DefaultTypeItemFactory<in T, F : DefaultTypeItemFactory<T, F>>( 257 final override val typeParameterScope: TypeParameterScope 258 ) : TypeItemFactory<T, F> { 259 nestedFactorynull260 final override fun nestedFactory( 261 scopeDescription: String, 262 typeParameters: List<TypeParameterItem> 263 ): F { 264 val scope = typeParameterScope.nestedScope(scopeDescription, typeParameters) 265 return if (scope === typeParameterScope) self() else createNestedFactory(scope) 266 } 267 getBoundsTypenull268 override fun getBoundsType(underlyingType: T) = getType(underlyingType) as BoundsTypeItem 269 270 override fun getExceptionType(underlyingType: T) = getType(underlyingType) as ExceptionTypeItem 271 272 override fun getGeneralType(underlyingType: T) = getType(underlyingType) 273 274 override fun getInterfaceType(underlyingType: T) = getSuperType(underlyingType) 275 276 override fun getSuperClassType(underlyingType: T) = getSuperType(underlyingType) 277 278 /** 279 * Creates a [ClassTypeItem] that is suitable for use as a super type, e.g. in an `extends` or 280 * `implements` list. 281 */ 282 private fun getSuperType(underlyingType: T): ClassTypeItem { 283 return getType(underlyingType, contextNullability = ContextNullability.forceNonNull) 284 as ClassTypeItem 285 } 286 getFieldTypenull287 override fun getFieldType( 288 underlyingType: T, 289 itemAnnotations: List<AnnotationItem>, 290 isEnumConstant: Boolean, 291 isFinal: Boolean, 292 isInitialValueNonNull: () -> Boolean 293 ): TypeItem { 294 // Get the context nullability. Enum constants are always non-null, item annotations and 295 // whether a field is final and has a non-null value are used only if no other source of 296 // information about nullability is available. 297 val contextNullability = 298 if (isEnumConstant) ContextNullability.forceNonNull 299 else { 300 ContextNullability( 301 itemAnnotations = itemAnnotations, 302 inferNullability = { 303 // Treat the field as non-null if it is final and has a non-null value. 304 TypeNullability.NONNULL.takeIf { isFinal && isInitialValueNonNull() } 305 } 306 ) 307 } 308 309 // Get the field's type, passing in the context nullability. 310 return getType(underlyingType, contextNullability = contextNullability) 311 } 312 getMethodParameterTypenull313 override fun getMethodParameterType( 314 underlyingParameterType: T, 315 itemAnnotations: List<AnnotationItem>, 316 fingerprint: MethodFingerprint, 317 parameterIndex: Int, 318 isVarArg: Boolean 319 ): TypeItem { 320 val contextNullability = 321 ContextNullability( 322 itemAnnotations = itemAnnotations, 323 inferNullability = { 324 // Check for a known method's nullability. 325 getMethodParameterNullability(fingerprint, parameterIndex) 326 } 327 ) 328 329 return getType( 330 underlyingParameterType, 331 contextNullability = contextNullability, 332 isVarArg = isVarArg, 333 ) 334 } 335 getMethodReturnTypenull336 override fun getMethodReturnType( 337 underlyingReturnType: T, 338 itemAnnotations: List<AnnotationItem>, 339 fingerprint: MethodFingerprint, 340 isAnnotationElement: Boolean, 341 ): TypeItem { 342 // Annotation elements, aka attributes, or methods on an annotation class cannot have a null 343 // value. 344 val annotationElementNullability = TypeNullability.NONNULL.takeIf { isAnnotationElement } 345 val contextNullability = 346 ContextNullability( 347 forcedNullability = annotationElementNullability, 348 forcedComponentNullability = annotationElementNullability, 349 itemAnnotations = itemAnnotations, 350 inferNullability = { 351 // Check for a known method's nullability. 352 getMethodReturnTypeNullability(fingerprint) 353 } 354 ) 355 356 // Get the method's return type, passing in the context nullability. 357 return getType(underlyingReturnType, contextNullability = contextNullability) 358 } 359 360 /** Type safe access to `this`. */ selfnull361 protected abstract fun self(): F 362 363 /** Create a nested factory that is a copy of this one, except using [scope]. */ 364 protected abstract fun createNestedFactory(scope: TypeParameterScope): F 365 366 /** 367 * Get the [TypeItem] corresponding to the [underlyingType] and within the [contextNullability]. 368 * 369 * The [isVarArg] is provided separately to the [underlyingType] because not all models 370 * encapsulate that information within the type. 371 */ 372 protected abstract fun getType( 373 underlyingType: T, 374 contextNullability: ContextNullability = ContextNullability.none, 375 isVarArg: Boolean = false, 376 ): TypeItem 377 378 companion object { 379 /** 380 * Get known [TypeNullability] for parameter [parameterIndex] of method with [fingerprint] 381 * if available, or `null`. 382 */ 383 private fun getMethodParameterNullability( 384 fingerprint: MethodFingerprint, 385 parameterIndex: Int 386 ): TypeNullability? { 387 val (name, parameterCount) = fingerprint 388 return when { 389 name == "equals" && parameterCount == 1 -> 390 TypeNullability.NULLABLE.takeIf { parameterIndex == 0 } 391 else -> null 392 } 393 } 394 395 /** 396 * Get [TypeNullability], if known, for the return type of the method with [fingerprint], or 397 * `null` if the method is not known. 398 */ 399 private fun getMethodReturnTypeNullability( 400 fingerprint: MethodFingerprint 401 ): TypeNullability? { 402 val (name, parameterCount) = fingerprint 403 return when { 404 name == "toString" && parameterCount == 0 -> TypeNullability.NONNULL 405 else -> null 406 } 407 } 408 } 409 } 410