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