| /* |
| * Copyright 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.room.compiler.processing.ksp |
| |
| import androidx.room.compiler.processing.XType |
| import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement |
| import com.google.devtools.ksp.symbol.KSAnnotated |
| import com.google.devtools.ksp.symbol.KSDeclaration |
| import com.google.devtools.ksp.symbol.KSNode |
| import com.google.devtools.ksp.symbol.Origin |
| |
| /** |
| * JvmType of a Kotlin type depends on where the type is declared. |
| * |
| * This resolver is used to resolve that type when necessary (computing TypeName). |
| * See [KSTypeVarianceResolver] for details. |
| */ |
| internal class KspJvmTypeResolver( |
| val scope: KspJvmTypeResolutionScope, |
| val delegate: KspType |
| ) { |
| internal fun resolveJvmType( |
| env: KspProcessingEnv |
| ): KspType { |
| if (!scope.needsWildcardResolution) { |
| return delegate |
| } |
| val wildcardMode = if (scope.suppressesWildcards) { |
| KSTypeVarianceResolver.WildcardMode.SUPPRESSED |
| } else { |
| KSTypeVarianceResolver.WildcardMode.PREFERRED |
| } |
| |
| val declarationType = scope.findDeclarationType() as? KspType |
| return env.resolveWildcards( |
| ksType = delegate.ksType, |
| wildcardMode = wildcardMode, |
| // See KSTypeVarianceResolver#applyTypeVariance: "If the ksType is from the original |
| // declaration, declarationType should be null". |
| declarationType = if (declarationType == delegate) { |
| null |
| } else { |
| // use the jvm type of the declaration so that it also gets its jvm wildcards |
| // resolved. |
| declarationType?.jvmWildcardTypeOrSelf?.ksType |
| } |
| ).let { |
| env.wrap( |
| ksType = it, |
| allowPrimitives = delegate.typeName.isPrimitive |
| ) |
| } |
| } |
| } |
| |
| /** |
| * Provides KSType resolution scope for parameter types. |
| */ |
| internal sealed class KspJvmTypeResolutionScope( |
| private val annotated: KSAnnotated, |
| private val container: KSDeclaration? |
| ) { |
| /** |
| * Checks whether we need wildcard resolution at all. It is only necessary if either the method |
| * parameter is in kotlin or the containing class, which inherited the method, is in kotlin. |
| */ |
| val needsWildcardResolution: Boolean by lazy { |
| annotated.isInKotlinCode() || container?.isInKotlinCode() == true |
| } |
| |
| /** |
| * Checks if the wildcards are suppressed by checking the hierarchy of the declaration to see if |
| * it has the @JvmSuppressWildcards annotation. |
| */ |
| val suppressesWildcards by lazy { |
| // suppress wildcards depend on the declaration site. |
| annotated.hasSuppressWildcardsAnnotationInHierarchy() |
| } |
| |
| private fun KSAnnotated.isInKotlinCode(): Boolean { |
| // find the true origin by skipping synthetics. |
| var current: KSNode? = this |
| while (current != null) { |
| val origin = current.origin |
| if (origin != Origin.SYNTHETIC) { |
| return origin == Origin.KOTLIN || origin == Origin.KOTLIN_LIB |
| } |
| current = current.parent |
| } |
| return false |
| } |
| |
| /** |
| * Finds the XType from the declaration if and only if this method is inherited from another |
| * class / interface. |
| */ |
| abstract fun findDeclarationType(): XType? |
| |
| internal class MethodParameter( |
| private val kspExecutableElement: KspExecutableElement, |
| private val parameterIndex: Int, |
| annotated: KSAnnotated, |
| container: KSDeclaration?, |
| ) : KspJvmTypeResolutionScope(annotated, container) { |
| override fun findDeclarationType(): XType? { |
| return if (kspExecutableElement is KspMethodElement) { |
| kspExecutableElement.executableType.parameterTypes.getOrNull(parameterIndex) |
| } else { |
| null |
| } |
| } |
| } |
| |
| internal class PropertySetterParameter( |
| val declaration: KspSyntheticPropertyMethodElement |
| ) : KspJvmTypeResolutionScope( |
| annotated = declaration.accessor, |
| container = declaration.field.enclosingElement.declaration |
| ) { |
| override fun findDeclarationType(): XType? { |
| // We return the declaration from the setter, not the field because the setter parameter |
| // will have a different type in jvm (due to jvm wildcard resolution) |
| return declaration.field.syntheticSetter?.parameters?.firstOrNull()?.type |
| } |
| } |
| } |