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.psi 18 19 import com.android.tools.metalava.model.TypeNullability 20 import com.intellij.psi.PsiElement 21 import org.jetbrains.kotlin.analysis.api.KtAnalysisSession 22 import org.jetbrains.kotlin.analysis.api.analyze 23 import org.jetbrains.kotlin.analysis.api.components.buildClassType 24 import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol 25 import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType 26 import org.jetbrains.kotlin.analysis.api.types.KtType 27 import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability 28 import org.jetbrains.kotlin.analysis.api.types.KtTypeParameterType 29 import org.jetbrains.kotlin.psi.KtCallableDeclaration 30 import org.jetbrains.kotlin.psi.KtClass 31 import org.jetbrains.kotlin.psi.KtElement 32 import org.jetbrains.kotlin.psi.KtFunction 33 import org.jetbrains.kotlin.psi.KtParameter 34 import org.jetbrains.kotlin.psi.KtProperty 35 import org.jetbrains.kotlin.psi.KtPropertyAccessor 36 import org.jetbrains.kotlin.psi.KtTypeReference 37 import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier 38 import org.jetbrains.uast.UElement 39 import org.jetbrains.uast.UField 40 import org.jetbrains.uast.UMethod 41 import org.jetbrains.uast.UParameter 42 import org.jetbrains.uast.getContainingUMethod 43 44 /** 45 * A wrapper for a [KtType] and the [KtAnalysisSession] needed to analyze it and the [PsiElement] 46 * that is the use site. 47 */ 48 internal class KotlinTypeInfo 49 private constructor( 50 val analysisSession: KtAnalysisSession?, 51 ktType: KtType?, 52 val context: PsiElement, 53 /** 54 * Override list of type arguments that should have been, but for some reason could not be, 55 * encapsulated within [ktType]. 56 */ 57 val overrideTypeArguments: List<KotlinTypeInfo>? = null, 58 ) { 59 constructor(context: PsiElement) : this(null, null, context) 60 61 /** Make sure that any typealiases are fully expanded. */ 62 val ktType = 63 analysisSession?.run { ktType?.fullyExpandedType } 64 ?: ktType?.let { 65 error("cannot have non-null ktType ($ktType) with a null analysisSession") 66 } 67 68 override fun toString(): String { 69 return "KotlinTypeInfo($ktType for $context)" 70 } 71 72 fun copy( 73 ktType: KtType? = this.ktType, 74 overrideTypeArguments: List<KotlinTypeInfo>? = this.overrideTypeArguments, 75 ) = KotlinTypeInfo(analysisSession, ktType, context, overrideTypeArguments) 76 77 /** 78 * Finds the nullability of the [ktType]. If there is no [analysisSession] or [ktType], defaults 79 * to `null` to allow for other sources, like annotations and inferred nullability to take 80 * effect. 81 */ 82 fun nullability(): TypeNullability? { 83 return if (analysisSession != null && ktType != null) { 84 analysisSession.run { 85 if (analysisSession.isInheritedGenericType(ktType)) { 86 TypeNullability.UNDEFINED 87 } else if (ktType.nullability == KtTypeNullability.NULLABLE) { 88 TypeNullability.NULLABLE 89 } else if (ktType.nullability == KtTypeNullability.NON_NULLABLE) { 90 TypeNullability.NONNULL 91 } else { 92 // No nullability information, possibly a propagated platform type. 93 null 94 } 95 } 96 } else { 97 null 98 } 99 } 100 101 /** 102 * Creates [KotlinTypeInfo] for the component type of this [ktType], assuming it is an array. 103 */ 104 fun forArrayComponentType(): KotlinTypeInfo { 105 return KotlinTypeInfo( 106 analysisSession, 107 analysisSession?.run { ktType?.getArrayElementType() }, 108 context, 109 ) 110 } 111 112 /** 113 * Creates [KotlinTypeInfo] for the type argument at [index] of this [KotlinTypeInfo], assuming 114 * it is a class type. 115 */ 116 fun forTypeArgument(index: Int): KotlinTypeInfo { 117 overrideTypeArguments?.getOrNull(index)?.let { 118 return it 119 } 120 return KotlinTypeInfo( 121 analysisSession, 122 analysisSession?.run { 123 when (ktType) { 124 is KtNonErrorClassType -> ktType.ownTypeArguments.getOrNull(index)?.type 125 else -> null 126 } 127 }, 128 context, 129 ) 130 } 131 132 /** 133 * Creates [KotlinTypeInfo] for the outer class type of this [ktType], assuming it is a class. 134 */ 135 fun forOuterClass(): KotlinTypeInfo { 136 return KotlinTypeInfo( 137 analysisSession, 138 analysisSession?.run { 139 (ktType as? KtNonErrorClassType)?.classId?.outerClassId?.let { outerClassId -> 140 buildClassType(outerClassId) { 141 // Add the parameters of the class type with nullability information. 142 ktType.qualifiers 143 .firstOrNull { it.name == outerClassId.shortClassName } 144 ?.typeArguments 145 ?.forEach { argument(it) } 146 } 147 } 148 }, 149 context, 150 ) 151 } 152 153 /** Get a [KotlinTypeInfo] that represents a suspend function's `Continuation` parameter. */ 154 fun forSyntheticContinuationParameter(returnType: KtType): KotlinTypeInfo { 155 // This cast is safe as this will only be called for a lambda function whose context will 156 // be [KtFunction]. 157 val ktElement = context as KtElement 158 return analyze(ktElement) { syntheticContinuationParameter(context, returnType) } 159 } 160 161 /** Get a [KotlinTypeInfo] that represents `Any?`. */ 162 fun nullableAny(): KotlinTypeInfo { 163 // This cast is safe as this will only be called for a lambda function whose context will 164 // be [KtFunction]. 165 val ktElement = context as KtElement 166 return analyze(ktElement) { KotlinTypeInfo(this, builtinTypes.NULLABLE_ANY, context) } 167 } 168 169 companion object { 170 /** 171 * Creates a [KotlinTypeInfo] instance from the given [context], with null values if the 172 * [KtType] for the [context] can't be resolved. 173 */ 174 fun fromContext(context: PsiElement): KotlinTypeInfo { 175 return if (context is KtElement) { 176 fromKtElement(context, context) 177 } else { 178 when (val sourcePsi = (context as? UElement)?.sourcePsi) { 179 is KtElement -> fromKtElement(sourcePsi, context) 180 else -> { 181 typeFromSyntheticElement(context) 182 } 183 } 184 } 185 ?: KotlinTypeInfo(context) 186 } 187 188 /** 189 * Try and compute [KotlinTypeInfo] from a [KtElement]. 190 * 191 * Multiple different [PsiElement] subclasses can be generated from the same [KtElement] and 192 * require different views of its types. The [context] is provided to differentiate between 193 * them. 194 */ 195 private fun fromKtElement(ktElement: KtElement, context: PsiElement): KotlinTypeInfo? = 196 when (ktElement) { 197 is KtProperty -> { 198 analyze(ktElement) { 199 val ktType = 200 when { 201 // If the context is the backing field then use the type of the 202 // delegate, if any. 203 context is UField -> ktElement.delegateExpression?.getKtType() 204 else -> null 205 } 206 ?: ktElement.getReturnKtType() 207 KotlinTypeInfo(this, ktType, ktElement) 208 } 209 } 210 is KtCallableDeclaration -> { 211 analyze(ktElement) { 212 val ktType = 213 if (ktElement is KtFunction && ktElement.isSuspend()) { 214 // A suspend function is transformed by Kotlin to return Any? 215 // instead of its actual return type. 216 builtinTypes.NULLABLE_ANY 217 } else { 218 ktElement.getReturnKtType() 219 } 220 KotlinTypeInfo(this, ktType, ktElement) 221 } 222 } 223 is KtTypeReference -> 224 analyze(ktElement) { KotlinTypeInfo(this, ktElement.getKtType(), ktElement) } 225 is KtPropertyAccessor -> 226 analyze(ktElement) { 227 KotlinTypeInfo(this, ktElement.getReturnKtType(), ktElement) 228 } 229 is KtClass -> { 230 analyze(ktElement) { 231 // If this is a named class or object then return a KotlinTypeInfo for the 232 // class. If it is generic then the type parameters will be used as the 233 // type arguments. 234 (ktElement.getSymbol() as? KtNamedClassOrObjectSymbol)?.let { symbol -> 235 KotlinTypeInfo(this, symbol.buildSelfClassType(), ktElement) 236 } 237 } 238 } 239 else -> null 240 } 241 242 /** 243 * Try and compute the type from a synthetic elements, e.g. a property setter. 244 * 245 * In order to get this far the [context] is either not a [UElement], or it has a null 246 * [UElement.sourcePsi]. That means it is most likely a parameter in a synthetic method 247 * created for use by code that operates on a "Psi" view of the source, i.e. java code. This 248 * method will attempt to reverse engineer the "Kt" -> "Psi" mapping to find the real Kotlin 249 * types. 250 */ 251 private fun typeFromSyntheticElement(context: PsiElement): KotlinTypeInfo? { 252 // If this is not a UParameter in a UMethod then it is an unknown synthetic element so 253 // just return. 254 val containingMethod = (context as? UParameter)?.getContainingUMethod() ?: return null 255 256 // Get the parameter index from the containing methods `uastParameters` as the parameter 257 // is a `UParameter`. 258 val parameterIndex = containingMethod.uastParameters.indexOf(context) 259 260 return when (val sourcePsi = containingMethod.sourcePsi) { 261 is KtProperty -> { 262 // This is the parameter of a synthetic setter, so get its type from the 263 // containing method. 264 fromContext(containingMethod) 265 } 266 is KtParameter -> { 267 // The underlying source representation of the synthetic method is a parameter, 268 // most likely a parameter of the primary constructor. In which case the 269 // synthetic method is most like a property setter. Whatever it may be, use the 270 // type of the parameter as it is most likely to be the correct type. 271 fromKtElement(sourcePsi, context) 272 } 273 is KtClass -> { 274 // The underlying source representation of the synthetic method is a whole 275 // class. 276 typeFromKtClass(parameterIndex, containingMethod, sourcePsi) 277 } 278 is KtFunction -> { 279 if ( 280 sourcePsi.isSuspend() && 281 parameterIndex == containingMethod.parameters.size - 1 282 ) { 283 // Compute the [KotlinTypeInfo] for the suspend function's synthetic 284 // [kotlin.coroutines.Continuation] parameter. 285 analyze(sourcePsi) { 286 val returnKtType = sourcePsi.getReturnKtType() 287 syntheticContinuationParameter(sourcePsi, returnKtType) 288 } 289 } else null 290 } 291 is KtPropertyAccessor -> 292 analyze(sourcePsi) { 293 // Getters and setters are always the same type as the property so use its 294 // type. 295 fromKtElement(sourcePsi.property, context) 296 } 297 else -> null 298 } 299 } 300 301 /** Check if this is a `suspend` function. */ 302 private fun KtFunction.isSuspend() = modifierList?.hasSuspendModifier() == true 303 304 /** 305 * Create a [KotlinTypeInfo] that represents the continuation parameter of a `suspend` 306 * function with [returnKtType]. 307 * 308 * Ideally, this would create a [KtNonErrorClassType] for `Continuation<$returnType$>`, 309 * where `$returnType$` is the return type of the Kotlin suspend function but while that 310 * works in K2 it fails in K1 as it cannot resolve the `Continuation` type even though it is 311 * in the Kotlin stdlib which will be on the classpath. 312 * 313 * Fortunately, doing that is not strictly necessary as the [KtType] is only used to 314 * retrieve nullability for the `Continuation` type and its type argument. So, this uses 315 * non-nullable [Any] as the fake type for `Continuation` (as that is always non-nullable) 316 * and stores the suspend function's return type in [KotlinTypeInfo.overrideTypeArguments] 317 * from where it will be retrieved. 318 */ 319 internal fun KtAnalysisSession.syntheticContinuationParameter( 320 context: PsiElement, 321 returnKtType: KtType 322 ): KotlinTypeInfo { 323 val returnTypeInfo = KotlinTypeInfo(this, returnKtType, context) 324 val fakeContinuationKtType = builtinTypes.ANY 325 return KotlinTypeInfo(this, fakeContinuationKtType, context, listOf(returnTypeInfo)) 326 } 327 328 /** Try and get the type for [parameterIndex] in [containingMethod] from the [ktClass]. */ 329 private fun typeFromKtClass( 330 parameterIndex: Int, 331 containingMethod: UMethod, 332 ktClass: KtClass 333 ) = 334 when { 335 ktClass.isData() && containingMethod.name == "copy" -> { 336 // The parameters in the copy constructor correspond to the parameters in the 337 // primary constructor so find the corresponding parameter in the primary 338 // constructor and use its type. 339 ktClass.primaryConstructor?.let { primaryConstructor -> 340 val ktParameter = primaryConstructor.valueParameters[parameterIndex] 341 analyze(ktParameter) { 342 KotlinTypeInfo( 343 this, 344 ktParameter.getReturnKtType(), 345 ktParameter, 346 ) 347 } 348 } 349 } 350 else -> null 351 } 352 353 // Mimic `hasInheritedGenericType` in `...uast.kotlin.FirKotlinUastResolveProviderService` 354 fun KtAnalysisSession.isInheritedGenericType(ktType: KtType): Boolean { 355 return ktType is KtTypeParameterType && 356 // explicitly nullable, e.g., T? 357 !ktType.isMarkedNullable && 358 // non-null upper bound, e.g., T : Any 359 ktType.canBeNull 360 } 361 } 362 } 363