aboutsummaryrefslogtreecommitdiff
path: root/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt')
-rw-r--r--compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt1613
1 files changed, 1613 insertions, 0 deletions
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt
new file mode 100644
index 00000000..7c1fade2
--- /dev/null
+++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt
@@ -0,0 +1,1613 @@
+/*
+ * Copyright 2020 Google LLC
+ * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ *
+ * 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 com.google.devtools.ksp.processing.impl
+
+import com.google.devtools.ksp.*
+import com.google.devtools.ksp.processing.KSBuiltIns
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.Variance
+import com.google.devtools.ksp.symbol.impl.binary.*
+import com.google.devtools.ksp.symbol.impl.declarationsInSourceOrder
+import com.google.devtools.ksp.symbol.impl.findParentAnnotated
+import com.google.devtools.ksp.symbol.impl.findPsi
+import com.google.devtools.ksp.symbol.impl.getInstanceForCurrentRound
+import com.google.devtools.ksp.symbol.impl.java.*
+import com.google.devtools.ksp.symbol.impl.jvmAccessFlag
+import com.google.devtools.ksp.symbol.impl.kotlin.*
+import com.google.devtools.ksp.symbol.impl.resolveContainingClass
+import com.google.devtools.ksp.symbol.impl.synthetic.*
+import com.google.devtools.ksp.visitor.CollectAnnotatedSymbolsVisitor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.PsiClassReferenceType
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
+import org.jetbrains.kotlin.codegen.ClassBuilderMode
+import org.jetbrains.kotlin.codegen.OwnerKind
+import org.jetbrains.kotlin.codegen.state.JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME
+import org.jetbrains.kotlin.codegen.state.JVM_WILDCARD_ANNOTATION_FQ_NAME
+import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
+import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
+import org.jetbrains.kotlin.container.ComponentProvider
+import org.jetbrains.kotlin.container.get
+import org.jetbrains.kotlin.container.tryGetService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
+import org.jetbrains.kotlin.descriptors.annotations.Annotations
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.load.java.components.TypeUsage
+import org.jetbrains.kotlin.load.java.descriptors.JavaForKotlinOverridePropertyDescriptor
+import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
+import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
+import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
+import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
+import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
+import org.jetbrains.kotlin.load.java.lazy.childForMethod
+import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
+import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
+import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.JavaClass
+import org.jetbrains.kotlin.load.java.structure.classId
+import org.jetbrains.kotlin.load.java.structure.impl.JavaArrayTypeImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaFieldImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
+import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethod
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethodBase
+import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
+import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
+import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass
+import org.jetbrains.kotlin.load.kotlin.getOptimalModeForReturnType
+import org.jetbrains.kotlin.load.kotlin.getOptimalModeForValueParameter
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.FqNameUnsafe
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.*
+import org.jetbrains.kotlin.resolve.annotations.argumentValue
+import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
+import org.jetbrains.kotlin.resolve.calls.inference.components.composeWith
+import org.jetbrains.kotlin.resolve.calls.inference.substitute
+import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
+import org.jetbrains.kotlin.resolve.constants.*
+import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
+import org.jetbrains.kotlin.resolve.descriptorUtil.classId
+import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
+import org.jetbrains.kotlin.resolve.descriptorUtil.module
+import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
+import org.jetbrains.kotlin.resolve.lazy.DeclarationScopeProvider
+import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
+import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+import org.jetbrains.kotlin.resolve.multiplatform.findActuals
+import org.jetbrains.kotlin.resolve.multiplatform.findExpects
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.LexicalScope
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+import org.jetbrains.kotlin.types.*
+import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext
+import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
+import org.jetbrains.kotlin.types.typeUtil.substitute
+import org.jetbrains.kotlin.types.typeUtil.supertypes
+import org.jetbrains.kotlin.util.containingNonLocalDeclaration
+import org.jetbrains.org.objectweb.asm.ClassReader
+import org.jetbrains.org.objectweb.asm.ClassVisitor
+import org.jetbrains.org.objectweb.asm.MethodVisitor
+import org.jetbrains.org.objectweb.asm.Opcodes
+import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION
+import java.io.File
+import java.util.Stack
+
+class ResolverImpl(
+ val module: ModuleDescriptor,
+ val allKSFiles: Collection<KSFile>,
+ val newKSFiles: Collection<KSFile>,
+ private val deferredSymbols: Map<SymbolProcessor, List<KSAnnotated>>,
+ val bindingTrace: BindingTrace,
+ val project: Project,
+ componentProvider: ComponentProvider,
+ val incrementalContext: IncrementalContext,
+ val options: KspOptions,
+) : Resolver {
+ val psiDocumentManager = PsiDocumentManager.getInstance(project)
+ private val nameToKSMap: MutableMap<KSName, KSClassDeclaration>
+ private val javaTypeParameterMap: MutableMap<LazyJavaTypeParameterDescriptor, PsiTypeParameter> = mutableMapOf()
+
+ /**
+ * Checking as member of is an expensive operation, hence we cache result values in this map.
+ */
+ private val functionAsMemberOfCache: MutableMap<Pair<KSFunctionDeclaration, KSType>, KSFunction>
+ private val propertyAsMemberOfCache: MutableMap<Pair<KSPropertyDeclaration, KSType>, KSType>
+
+ private val typeMapper = KotlinTypeMapper(
+ BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
+ module.name.getNonSpecialIdentifier(),
+ KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
+ true
+ )
+ private val qualifiedExpressionResolver = QualifiedExpressionResolver(LanguageVersionSettingsImpl.DEFAULT)
+
+ private val aliasingFqNs: MutableMap<String, KSTypeAlias> = mutableMapOf()
+ private val aliasingNames: MutableSet<String> = mutableSetOf()
+ private val topDownAnalysisContext by lazy {
+ TopDownAnalysisContext(TopDownAnalysisMode.TopLevelDeclarations, DataFlowInfo.EMPTY, declarationScopeProvider)
+ }
+
+ companion object {
+ var instance: ResolverImpl? = null
+ }
+
+ var resolveSession: ResolveSession
+ var bodyResolver: BodyResolver
+ var constantExpressionEvaluator: ConstantExpressionEvaluator
+ var declarationScopeProvider: DeclarationScopeProvider
+
+ lateinit var moduleClassResolver: ModuleClassResolver
+ lateinit var javaTypeResolver: JavaTypeResolver
+ lateinit var lazyJavaResolverContext: LazyJavaResolverContext
+
+ init {
+ resolveSession = componentProvider.get()
+ bodyResolver = componentProvider.get()
+ declarationScopeProvider = componentProvider.get()
+ constantExpressionEvaluator = componentProvider.get()
+
+ componentProvider.tryGetService(JavaResolverComponents::class.java)?.let {
+ lazyJavaResolverContext = LazyJavaResolverContext(it, TypeParameterResolver.EMPTY) { null }
+ javaTypeResolver = lazyJavaResolverContext.typeResolver
+ moduleClassResolver = lazyJavaResolverContext.components.moduleClassResolver
+ }
+ instance = this
+
+ nameToKSMap = mutableMapOf()
+ functionAsMemberOfCache = mutableMapOf()
+ propertyAsMemberOfCache = mutableMapOf()
+
+ val visitor = object : KSVisitorVoid() {
+ override fun visitFile(file: KSFile, data: Unit) {
+ file.declarations.forEach { it.accept(this, data) }
+
+ // TODO: evaluate with benchmarks: cost of getContainingFile v.s. name collision
+ // Import aliases are file-scoped. `aliasingNamesByFile` could be faster
+ (file as? KSFileImpl)?.file?.importDirectives?.forEach {
+ it.aliasName?.let { aliasingNames.add(it) }
+ }
+ }
+
+ override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
+ val qualifiedName = classDeclaration.qualifiedName
+ if (qualifiedName != null) {
+ nameToKSMap[qualifiedName] = classDeclaration
+ }
+ classDeclaration.declarations.forEach { it.accept(this, data) }
+ }
+
+ override fun visitTypeAlias(typeAlias: KSTypeAlias, data: Unit) {
+ typeAlias.qualifiedName?.asString()?.let { fqn ->
+ aliasingFqNs[fqn] = typeAlias
+ aliasingNames.add(fqn.substringAfterLast('.'))
+ }
+ }
+ }
+
+ // FIXME: reuse results from previous rounds and only loop through newKSFiles.
+ allKSFiles.forEach { it.accept(visitor, Unit) }
+ }
+
+ // Mitigation for processors with memory leaks
+ // https://github.com/google/ksp/issues/1063
+ fun tearDown() {
+ instance = null
+ }
+
+ override fun getNewFiles(): Sequence<KSFile> {
+ return newKSFiles.asSequence()
+ }
+
+ override fun getAllFiles(): Sequence<KSFile> {
+ return allKSFiles.asSequence()
+ }
+
+ override fun getClassDeclarationByName(name: KSName): KSClassDeclaration? {
+ nameToKSMap[name]?.let { return it }
+
+ return module.resolveClassByFqName(FqName(name.asString()), NoLookupLocation.WHEN_FIND_BY_FQNAME)
+ ?.let {
+ val psi = it.findPsi()
+ if (psi != null) {
+ when (psi) {
+ is KtClassOrObject -> KSClassDeclarationImpl.getCached(psi)
+ is PsiClass -> KSClassDeclarationJavaImpl.getCached(psi)
+ else -> throw IllegalStateException("unexpected psi: ${psi.javaClass}")
+ }
+ } else {
+ KSClassDeclarationDescriptorImpl.getCached(it)
+ }
+ }
+ }
+
+ override fun getFunctionDeclarationsByName(
+ name: KSName,
+ includeTopLevel: Boolean,
+ ): Sequence<KSFunctionDeclaration> {
+ val qualifier = name.getQualifier()
+ val functionName = name.getShortName()
+ val nonTopLevelResult = this.getClassDeclarationByName(qualifier)?.getDeclaredFunctions()
+ ?.filter { it.simpleName.asString() == functionName }?.asSequence() ?: emptySequence()
+ return if (!includeTopLevel) nonTopLevelResult else {
+ nonTopLevelResult.plus(
+ module.getPackage(FqName(qualifier))
+ .memberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) {
+ it.asString() == functionName
+ }
+ .filterIsInstance<MemberDescriptor>().mapNotNull { it.toKSDeclaration() as? KSFunctionDeclaration }
+ )
+ }
+ }
+
+ override fun getPropertyDeclarationByName(name: KSName, includeTopLevel: Boolean): KSPropertyDeclaration? {
+ val qualifier = name.getQualifier()
+ val propertyName = name.getShortName()
+ val nonTopLevelResult = this.getClassDeclarationByName(qualifier)?.getDeclaredProperties()
+ ?.singleOrNull { it.simpleName.asString() == propertyName }
+ return if (!includeTopLevel) nonTopLevelResult else {
+ val topLevelResult = (
+ module.getPackage(FqName(qualifier))
+ .memberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES) {
+ it.asString() == propertyName
+ }
+ .also {
+ if (it.size > 1) {
+ throw IllegalStateException("Found multiple properties with same qualified name")
+ }
+ }
+ .singleOrNull() as? MemberDescriptor
+ )?.toKSDeclaration() as? KSPropertyDeclaration
+ if (topLevelResult != null && nonTopLevelResult != null) {
+ throw IllegalStateException("Found multiple properties with same qualified name")
+ }
+ nonTopLevelResult ?: topLevelResult
+ }
+ }
+
+ internal fun checkAnnotation(annotation: KSAnnotation, ksName: KSName, shortName: String): Boolean {
+ val annotationType = annotation.annotationType
+ val referencedName = (annotationType.element as? KSClassifierReference)?.referencedName()
+ val simpleName = referencedName?.substringAfterLast('.')
+ return (simpleName == shortName || simpleName in aliasingNames) &&
+ annotationType.resolveToUnderlying().declaration.qualifiedName == ksName
+ }
+
+ override fun getSymbolsWithAnnotation(annotationName: String, inDepth: Boolean): Sequence<KSAnnotated> {
+ // If annotationName is a typealias, resolve to underlying type.
+ val realAnnotationName =
+ aliasingFqNs[annotationName]?.type?.resolveToUnderlying()?.declaration?.qualifiedName?.asString()
+ ?: annotationName
+
+ val ksName = KSNameImpl.getCached(realAnnotationName)
+ val shortName = ksName.getShortName()
+
+ fun checkAnnotated(annotated: KSAnnotated): Boolean {
+ return annotated.annotations.any {
+ checkAnnotation(it, ksName, shortName)
+ }
+ }
+
+ val allAnnotated = if (inDepth) newAnnotatedSymbolsWithLocals else newAnnotatedSymbols
+ return allAnnotated.asSequence().filter(::checkAnnotated)
+ }
+
+ private fun collectAnnotatedSymbols(inDepth: Boolean): Collection<KSAnnotated> {
+ val visitor = CollectAnnotatedSymbolsVisitor(inDepth)
+
+ for (file in newKSFiles) {
+ file.accept(visitor, Unit)
+ }
+
+ return visitor.symbols
+ }
+
+ private val deferredSymbolsUpdated: Collection<KSAnnotated> by lazy {
+ deferredSymbols.values.flatten().mapNotNull { it.getInstanceForCurrentRound() }
+ }
+
+ private val newAnnotatedSymbols: Collection<KSAnnotated> by lazy {
+ collectAnnotatedSymbols(false) + deferredSymbolsUpdated
+ }
+
+ private val newAnnotatedSymbolsWithLocals: Collection<KSAnnotated> by lazy {
+ collectAnnotatedSymbols(true) + deferredSymbolsUpdated
+ }
+
+ override fun getKSNameFromString(name: String): KSName {
+ return KSNameImpl.getCached(name)
+ }
+
+ override fun createKSTypeReferenceFromKSType(type: KSType): KSTypeReference {
+ return KSTypeReferenceSyntheticImpl.getCached(type, null)
+ }
+
+ @KspExperimental
+ override fun mapToJvmSignature(declaration: KSDeclaration): String? = mapToJvmSignatureInternal(declaration)
+
+ internal fun mapToJvmSignatureInternal(declaration: KSDeclaration): String? = when (declaration) {
+ is KSClassDeclaration -> resolveClassDeclaration(declaration)?.let { typeMapper.mapType(it).descriptor }
+ is KSFunctionDeclaration -> resolveFunctionDeclaration(declaration)?.let {
+ when (it) {
+ is FunctionDescriptor -> typeMapper.mapAsmMethod(it).descriptor
+ is PropertyDescriptor -> typeMapper.mapFieldSignature(it.type, it) ?: typeMapper.mapType(it).descriptor
+ else -> throw IllegalStateException("Unexpected descriptor type for declaration: $declaration")
+ }
+ }
+ is KSPropertyDeclaration -> resolvePropertyDeclaration(declaration)?.let {
+ typeMapper.mapFieldSignature(it.type, it) ?: typeMapper.mapType(it).descriptor
+ }
+ else -> null
+ }
+
+ override fun overrides(overrider: KSDeclaration, overridee: KSDeclaration): Boolean {
+ fun resolveForOverride(declaration: KSDeclaration): DeclarationDescriptor? {
+ return when (declaration) {
+ is KSPropertyDeclaration -> resolvePropertyDeclaration(declaration)
+ is KSFunctionDeclarationJavaImpl -> resolveJavaDeclaration(declaration.psi)
+ is KSFunctionDeclaration -> resolveFunctionDeclaration(declaration)
+ else -> null
+ }
+ }
+
+ if (!overridee.isOpen())
+ return false
+ if (!overridee.isVisibleFrom(overrider))
+ return false
+ if (!(
+ overridee is KSFunctionDeclaration || overrider is KSFunctionDeclaration ||
+ (overridee is KSPropertyDeclaration && overrider is KSPropertyDeclaration)
+ )
+ )
+ return false
+
+ if (overrider is KSPropertyDeclarationJavaImpl)
+ return false
+
+ val superDescriptor = resolveForOverride(overridee) as? CallableMemberDescriptor ?: return false
+ val subDescriptor = resolveForOverride(overrider) as? CallableMemberDescriptor ?: return false
+ val subClassDescriptor = overrider.closestClassDeclaration()?.let {
+ resolveClassDeclaration(it)
+ } ?: return false
+ val superClassDescriptor = overridee.closestClassDeclaration()?.let {
+ resolveClassDeclaration(it)
+ } ?: return false
+
+ incrementalContext.recordLookupWithSupertypes(subClassDescriptor.defaultType)
+
+ val typeOverride = subClassDescriptor.getAllSuperClassifiers()
+ .filter { it != subClassDescriptor } // exclude subclass itself as it cannot override its own methods
+ .any {
+ it == superClassDescriptor
+ }
+ if (!typeOverride) return false
+
+ incrementalContext.recordLookupForDeclaration(overrider)
+ incrementalContext.recordLookupForDeclaration(overridee)
+
+ return OverridingUtil.overrides(subDescriptor, superDescriptor, true, true)
+ }
+
+ // check if the candidate is overridden from the original declaration.
+ private fun isOriginal(original: KSDeclaration, candidate: KSDeclaration): Boolean {
+ incrementalContext.recordLookupForDeclaration(original)
+ incrementalContext.recordLookupForDeclaration(candidate)
+ val originalDescriptor = when (original) {
+ is KSPropertyDeclaration -> resolvePropertyDeclaration(original)
+ is KSFunctionDeclaration ->
+ (resolveFunctionDeclaration(original) as? FunctionDescriptor)?.propertyIfAccessor
+ else -> return false
+ }
+
+ val candidateDescriptors = when (candidate) {
+ is KSPropertyDeclaration -> resolvePropertyDeclaration(candidate)?.overriddenDescriptors
+ is KSFunctionDeclaration -> resolveFunctionDeclaration(candidate)?.overriddenDescriptors
+ else -> return false
+ }
+ return candidateDescriptors?.any { it == originalDescriptor } ?: false
+ }
+
+ override fun overrides(
+ overrider: KSDeclaration,
+ overridee: KSDeclaration,
+ containingClass: KSClassDeclaration
+ ): Boolean {
+ incrementalContext.recordLookupForDeclaration(containingClass)
+ return when (overrider) {
+ is KSPropertyDeclaration -> containingClass.getAllProperties().singleOrNull {
+ it.simpleName.asString() == overrider.simpleName.asString() && isOriginal(overrider, it)
+ }?.let { overrides(it, overridee) } ?: false
+ is KSFunctionDeclaration -> {
+ val candidates = containingClass.getAllFunctions().filter {
+ it.simpleName.asString() == overridee.simpleName.asString()
+ }
+ if (overrider.simpleName.asString().startsWith("get") ||
+ overrider.simpleName.asString().startsWith("set")
+ ) {
+ candidates.plus(
+ containingClass.getAllProperties().filter {
+ val overriderName = overrider.simpleName.asString().substring(3)
+ .replaceFirstChar { it.lowercase() }
+ it.simpleName.asString() == overriderName ||
+ it.simpleName.asString().replaceFirstChar { it.lowercase() } == overriderName
+ }
+ // TODO: It is currently not possible to do the overridden descriptor optimization for java overrides.
+ ).any { overrides(it, overridee) }
+ } else {
+ candidates.singleOrNull { isOriginal(overrider, it) }?.let { overrides(it, overridee) } ?: false
+ }
+ }
+ else -> false
+ }
+ }
+
+ fun evaluateConstant(expression: KtExpression?, expectedType: KotlinType): ConstantValue<*>? {
+ return expression?.let {
+ if (it is KtClassLiteralExpression && it.receiverExpression != null) {
+ val parent = KtStubbedPsiUtil.getPsiOrStubParent(it, KtPrimaryConstructor::class.java, false)
+ val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(parent!!)
+ val result = qualifiedExpressionResolver
+ .resolveDescriptorForDoubleColonLHS(it.receiverExpression!!, scope, bindingTrace, false)
+ val classifier = result.classifierDescriptor ?: return null
+ val typeResolutionContext = TypeResolutionContext(scope, bindingTrace, true, true, false)
+ val possiblyBareType = resolveSession.typeResolver
+ .resolveTypeForClassifier(typeResolutionContext, classifier, result, it, Annotations.EMPTY)
+ var actualType = if (possiblyBareType.isBare)
+ possiblyBareType.bareTypeConstructor.declarationDescriptor!!.defaultType
+ else possiblyBareType.actualType
+ var arrayDimension = 0
+ while (KotlinBuiltIns.isArray(actualType)) {
+ actualType = actualType.arguments.single().type
+ arrayDimension += 1
+ }
+ KClassValue(actualType.constructor.declarationDescriptor.classId!!, arrayDimension)
+ } else {
+ constantExpressionEvaluator.evaluateExpression(it, bindingTrace)?.toConstantValue(expectedType) ?: run {
+ val parent = KtStubbedPsiUtil
+ .getPsiOrStubParent(expression, KtPrimaryConstructor::class.java, false)
+ val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(parent!!)
+ qualifiedExpressionResolver
+ .resolvePackageHeader(expression.containingKtFile.packageDirective!!, module, bindingTrace)
+ bodyResolver.resolveConstructorParameterDefaultValues(
+ topDownAnalysisContext.outerDataFlowInfo, bindingTrace,
+ parent, (scope.ownerDescriptor as ClassDescriptor).constructors.first(), scope,
+ resolveSession.inferenceSession
+ )
+ constantExpressionEvaluator.evaluateExpression(it, bindingTrace)?.toConstantValue(expectedType)
+ }
+ }
+ }
+ }
+
+ fun resolveDeclaration(declaration: KtDeclaration): DeclarationDescriptor? {
+ return if (KtPsiUtil.isLocal(declaration)) {
+ resolveDeclarationForLocal(declaration)
+ bindingTrace.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
+ } else {
+ resolveSession.resolveToDescriptor(declaration)
+ }
+ }
+
+ // TODO: Resolve Java variables is not supported by this function. Not needed currently.
+ fun resolveJavaDeclaration(psi: PsiElement): DeclarationDescriptor? {
+ return when (psi) {
+ is PsiClass -> moduleClassResolver.resolveClass(
+ JavaClassImpl(psi).apply { workaroundForNested(lazyJavaResolverContext) }
+ )
+ is PsiMethod -> {
+ // TODO: get rid of hardcoded check if possible.
+ val property = if (psi.name.startsWith("set") || psi.name.startsWith("get")) {
+ moduleClassResolver
+ .resolveContainingClass(psi)
+ ?.findEnclosedDescriptor(
+ kindFilter = DescriptorKindFilter.CALLABLES
+ ) {
+ (it as? PropertyDescriptor)?.getter?.findPsi() == psi ||
+ (it as? PropertyDescriptor)?.setter?.findPsi() == psi
+ }
+ } else null
+ property ?: moduleClassResolver
+ .resolveContainingClass(psi)?.let { containingClass ->
+ val filter = if (psi is SyntheticElement) {
+ { declaration: DeclarationDescriptor -> declaration.name.asString() == psi.name }
+ } else {
+ { declaration: DeclarationDescriptor -> declaration.findPsi() == psi }
+ }
+ containingClass.findEnclosedDescriptor(
+ kindFilter = DescriptorKindFilter.FUNCTIONS,
+ filter = filter
+ )
+ }
+ }
+ is PsiField -> {
+ moduleClassResolver
+ .resolveClass(
+ JavaFieldImpl(psi).containingClass.apply { workaroundForNested(lazyJavaResolverContext) }
+ )
+ ?.findEnclosedDescriptor(
+ kindFilter = DescriptorKindFilter.VARIABLES,
+ filter = { it.findPsi() == psi }
+ )
+ }
+ else -> throw IllegalStateException("unhandled psi element kind: ${psi.javaClass}")
+ }
+ }
+
+ fun resolveClassDeclaration(classDeclaration: KSClassDeclaration): ClassDescriptor? {
+ return when (classDeclaration) {
+ is KSClassDeclarationImpl -> resolveDeclaration(classDeclaration.ktClassOrObject)
+ is KSClassDeclarationDescriptorImpl -> classDeclaration.descriptor
+ is KSClassDeclarationJavaImpl -> resolveJavaDeclaration(classDeclaration.psi)
+ else -> throw IllegalStateException("unexpected class: ${classDeclaration.javaClass}")
+ } as ClassDescriptor?
+ }
+
+ fun resolveFunctionDeclaration(function: KSFunctionDeclaration): CallableDescriptor? {
+ return when (function) {
+ is KSFunctionDeclarationImpl -> resolveDeclaration(function.ktFunction)
+ is KSFunctionDeclarationDescriptorImpl -> function.descriptor
+ is KSFunctionDeclarationJavaImpl -> {
+ val descriptor = resolveJavaDeclaration(function.psi)
+ if (descriptor is JavaForKotlinOverridePropertyDescriptor) {
+ if (function.simpleName.asString().startsWith("set")) {
+ descriptor.setter
+ } else {
+ descriptor.getter
+ }
+ } else {
+ descriptor
+ }
+ }
+ is KSConstructorSyntheticImpl -> {
+ // we might create synthetic constructor when it is not declared in code
+ // it is either for kotlin, where we can use primary constructor, or for java
+ // where we can use the only available constructor
+ val resolved = resolveClassDeclaration(function.ksClassDeclaration)
+ resolved?.unsubstitutedPrimaryConstructor ?: resolved?.constructors?.singleOrNull()
+ }
+ else -> throw IllegalStateException("unexpected class: ${function.javaClass}")
+ } as? CallableDescriptor
+ }
+
+ fun resolvePropertyDeclaration(property: KSPropertyDeclaration): PropertyDescriptor? {
+ return when (property) {
+ is KSPropertyDeclarationImpl -> resolveDeclaration(property.ktProperty)
+ is KSPropertyDeclarationParameterImpl -> resolveDeclaration(property.ktParameter)
+ is KSPropertyDeclarationDescriptorImpl -> property.descriptor
+ is KSPropertyDeclarationJavaImpl -> resolveJavaDeclaration(property.psi)
+ else -> throw IllegalStateException("unexpected class: ${property.javaClass}")
+ } as PropertyDescriptor?
+ }
+
+ fun resolvePropertyAccessorDeclaration(accessor: KSPropertyAccessor): PropertyAccessorDescriptor? {
+ return when (accessor) {
+ is KSPropertyAccessorDescriptorImpl -> accessor.descriptor
+ is KSPropertyAccessorImpl -> resolveDeclaration(accessor.ktPropertyAccessor)
+ is KSPropertySetterSyntheticImpl -> resolvePropertyDeclaration(accessor.receiver)?.setter
+ is KSPropertyGetterSyntheticImpl -> resolvePropertyDeclaration(accessor.receiver)?.getter
+ else -> throw IllegalStateException("unexpected class: ${accessor.javaClass}")
+ } as PropertyAccessorDescriptor?
+ }
+
+ fun resolveJavaType(psi: PsiType, parentTypeReference: KSTypeReference? = null): KotlinType {
+ incrementalContext.recordLookup(psi)
+ val javaType = JavaTypeImpl.create(psi)
+
+ var parent: KSNode? = parentTypeReference
+
+ val stack = Stack<KSNode>()
+ while (parent != null) {
+ if (parent is KSFunctionDeclarationJavaImpl || parent is KSClassDeclarationJavaImpl) {
+ stack.push(parent)
+ }
+ parent = parent.parent
+ }
+ // Construct resolver context for the PsiType
+ var resolverContext = lazyJavaResolverContext
+
+ for (e in stack) {
+ when (e) {
+ is KSFunctionDeclarationJavaImpl -> {
+ // Non-physical methods have no interesting scope and may have no containing class
+ if (!e.psi.isPhysical || e.psi.containingClass == null)
+ continue
+ resolverContext = resolverContext
+ .childForMethod(
+ resolveJavaDeclaration(e.psi)!!,
+ if (e.psi.isConstructor) JavaConstructorImpl(e.psi) else JavaMethodImpl(e.psi)
+ )
+ }
+ is KSClassDeclarationJavaImpl -> {
+ resolverContext = resolverContext
+ .childForClassOrPackage(resolveJavaDeclaration(e.psi) as ClassDescriptor, JavaClassImpl(e.psi))
+ }
+ }
+ }
+ return if (javaType is JavaArrayTypeImpl)
+ resolverContext
+ .typeResolver.transformArrayType(javaType, TypeUsage.COMMON.toAttributes(), psi is PsiEllipsisType)
+ else
+ resolverContext.typeResolver.transformJavaType(javaType, TypeUsage.COMMON.toAttributes())
+ }
+
+ /*
+ * Don't map Java types in annotation parameters
+ *
+ * Users may specify Java types explicitly by instances of `Class<T>`.
+ * The situation is similar to `getClassDeclarationByName` where we have
+ * decided to keep those Java types not mapped.
+ *
+ * It would be troublesome if users try to use reflection on types that
+ * were mapped to Kotlin builtins, becuase some of those builtins don't
+ * even exist in classpath.
+ *
+ * Therefore, ResolverImpl.resolveJavaType cannot be used.
+ */
+ fun resolveJavaTypeInAnnotations(psiType: PsiType): KSType = if (options.mapAnnotationArgumentsInJava) {
+ getKSTypeCached(resolveJavaType(psiType))
+ } else {
+ when (psiType) {
+ is PsiPrimitiveType -> {
+ getClassDeclarationByName(psiType.boxedTypeName!!)!!.asStarProjectedType()
+ }
+ is PsiArrayType -> {
+ val componentType = resolveJavaTypeInAnnotations(psiType.componentType)
+ val componentTypeRef = createKSTypeReferenceFromKSType(componentType)
+ val typeArgs = listOf(getTypeArgument(componentTypeRef, Variance.INVARIANT))
+ builtIns.arrayType.replace(typeArgs)
+ }
+ else -> {
+ getClassDeclarationByName(psiType.canonicalText)?.asStarProjectedType() ?: KSErrorType
+ }
+ }
+ }
+
+ fun KotlinType.expandNonRecursively(): KotlinType =
+ (constructor.declarationDescriptor as? TypeAliasDescriptor)?.expandedType?.withAbbreviation(this as SimpleType)
+ ?: this
+
+ fun TypeProjection.expand(): TypeProjection {
+ val expandedType = type.expand()
+ return if (expandedType == type) this else substitute { expandedType }
+ }
+
+ // TODO: Is this the most efficient way?
+ fun KotlinType.expand(): KotlinType =
+ replace(arguments.map { it.expand() }).expandNonRecursively()
+
+ fun KtTypeReference.lookup(): KotlinType? =
+ bindingTrace.get(BindingContext.ABBREVIATED_TYPE, this)?.expand() ?: bindingTrace.get(BindingContext.TYPE, this)
+
+ fun resolveUserType(type: KSTypeReference): KSType {
+ when (type) {
+ is KSTypeReferenceImpl -> {
+ val typeReference = type.ktTypeReference
+ typeReference.lookup()?.let {
+ return getKSTypeCached(it, type.element.typeArguments, type.annotations)
+ }
+ KtStubbedPsiUtil.getContainingDeclaration(typeReference)?.let { containingDeclaration ->
+ resolveDeclaration(containingDeclaration)?.let {
+ // TODO: only resolve relevant branch.
+ ForceResolveUtil.forceResolveAllContents(it)
+ }
+ // TODO: Fix resolution look up to avoid fallback to file scope.
+ typeReference.lookup()?.let {
+ return getKSTypeCached(it, type.element.typeArguments, type.annotations)
+ }
+ }
+ val scope = resolveSession.fileScopeProvider.getFileResolutionScope(typeReference.containingKtFile)
+ return resolveSession.typeResolver.resolveType(scope, typeReference, bindingTrace, false).let {
+ getKSTypeCached(it, type.element.typeArguments, type.annotations)
+ }
+ }
+ is KSTypeReferenceDescriptorImpl -> {
+ return getKSTypeCached(type.kotlinType)
+ }
+ is KSTypeReferenceJavaImpl -> {
+ val psi = (type.psi as? PsiClassReferenceType)?.resolve()
+ if (psi is PsiTypeParameter) {
+ (type.psi as PsiClassReferenceType).typeArguments().forEach {
+ if (it is PsiType) {
+ incrementalContext.recordLookup(it)
+ }
+ }
+ val containingDeclaration = if (psi.owner is PsiClass) {
+ moduleClassResolver.resolveClass(
+ JavaClassImpl(psi.owner as PsiClass).apply { workaroundForNested(lazyJavaResolverContext) }
+ )
+ } else {
+ val owner = psi.owner
+ check(owner is PsiMethod) {
+ "unexpected owner type: $owner / ${owner?.javaClass}"
+ }
+ moduleClassResolver.resolveContainingClass(owner)
+ ?.findEnclosedDescriptor(
+ kindFilter = DescriptorKindFilter.FUNCTIONS,
+ filter = { it.findPsi() == owner }
+ ) as FunctionDescriptor
+ } as DeclarationDescriptor
+ val typeParameterDescriptor = LazyJavaTypeParameterDescriptor(
+ lazyJavaResolverContext,
+ JavaTypeParameterImpl(psi),
+ psi.index,
+ containingDeclaration
+ )
+ javaTypeParameterMap[typeParameterDescriptor] = psi
+ }
+ return getKSTypeCached(resolveJavaType(type.psi, type), type.element.typeArguments, type.annotations)
+ }
+ else -> throw IllegalStateException("Unable to resolve type for $type, $ExceptionMessage")
+ }
+ }
+
+ fun findDeclaration(kotlinType: KotlinType): KSDeclaration {
+ val descriptor = kotlinType.constructor.declarationDescriptor
+ val psi = descriptor?.findPsi()
+ return if (psi != null) {
+ when (psi) {
+ is KtClassOrObject -> KSClassDeclarationImpl.getCached(psi)
+ is PsiClass -> KSClassDeclarationJavaImpl.getCached(psi)
+ is KtTypeAlias -> KSTypeAliasImpl.getCached(psi)
+ is KtTypeParameter -> KSTypeParameterImpl.getCached(psi)
+ is PsiEnumConstant -> KSClassDeclarationJavaEnumEntryImpl.getCached(psi)
+ else -> throw IllegalStateException("Unexpected psi type: ${psi.javaClass}, $ExceptionMessage")
+ }
+ } else {
+ when (descriptor) {
+ is ClassDescriptor -> KSClassDeclarationDescriptorImpl.getCached(descriptor)
+ // LazyJavaTypeParameterDescriptor has `source` overridden to `NO_SOURCE`, therefore
+ // need to look up psi within KSP.
+ is TypeParameterDescriptor -> if (descriptor in javaTypeParameterMap) {
+ KSTypeParameterJavaImpl.getCached(javaTypeParameterMap[descriptor]!!)
+ } else {
+ KSTypeParameterDescriptorImpl.getCached(descriptor)
+ }
+ is TypeAliasDescriptor -> KSTypeAliasDescriptorImpl.getCached(descriptor)
+ null -> throw IllegalStateException("Failed to resolve descriptor for $kotlinType")
+ else -> throw IllegalStateException(
+ "Unexpected descriptor type: ${descriptor.javaClass}, $ExceptionMessage"
+ )
+ }
+ }
+ }
+
+ // Finds closest non-local scope.
+ fun KtElement.findLexicalScope(): LexicalScope {
+ return containingNonLocalDeclaration()?.let {
+ resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(it)
+ } ?: resolveSession.fileScopeProvider.getFileResolutionScope(this.containingKtFile)
+ }
+
+ fun resolveAnnotationEntry(ktAnnotationEntry: KtAnnotationEntry): AnnotationDescriptor? {
+ bindingTrace.get(BindingContext.ANNOTATION, ktAnnotationEntry)?.let { return it }
+ KtStubbedPsiUtil.getContainingDeclaration(ktAnnotationEntry)?.let { containingDeclaration ->
+ if (KtPsiUtil.isLocal(containingDeclaration)) {
+ resolveDeclarationForLocal(containingDeclaration)
+ } else {
+ resolveSession.resolveToDescriptor(containingDeclaration).annotations.forEach {}
+ }
+ } ?: ktAnnotationEntry.containingKtFile.let {
+ resolveSession.getFileAnnotations(it).forEach {}
+ }
+ return bindingTrace.get(BindingContext.ANNOTATION, ktAnnotationEntry)
+ }
+
+ fun resolveDeclarationForLocal(localDeclaration: KtDeclaration) {
+ var declaration = KtStubbedPsiUtil.getContainingDeclaration(localDeclaration) ?: return
+ while (KtPsiUtil.isLocal(declaration))
+ declaration = KtStubbedPsiUtil.getContainingDeclaration(declaration)!!
+
+ val containingFD = resolveSession.resolveToDescriptor(declaration).also {
+ ForceResolveUtil.forceResolveAllContents(it)
+ }
+
+ if (declaration is KtNamedFunction) {
+ val dataFlowInfo = DataFlowInfo.EMPTY
+ val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(declaration)
+ bodyResolver.resolveFunctionBody(
+ dataFlowInfo,
+ bindingTrace,
+ declaration,
+ containingFD as FunctionDescriptor,
+ scope,
+ null
+ )
+ }
+ }
+
+ @KspExperimental
+ override fun getJvmName(accessor: KSPropertyAccessor): String? {
+ val descriptor = resolvePropertyAccessorDeclaration(accessor)
+
+ return descriptor?.let {
+ // KotlinTypeMapper.mapSignature always uses OwnerKind.IMPLEMENTATION
+ typeMapper.mapFunctionName(descriptor, OwnerKind.IMPLEMENTATION)
+ }
+ }
+
+ @KspExperimental
+ override fun getJvmName(declaration: KSFunctionDeclaration): String? {
+ // function names might be mangled if they receive inline class parameters or they are internal
+ val descriptor = resolveFunctionDeclaration(declaration)
+ return (descriptor as? FunctionDescriptor)?.let {
+ // KotlinTypeMapper.mapSignature always uses OwnerKind.IMPLEMENTATION
+ typeMapper.mapFunctionName(it, OwnerKind.IMPLEMENTATION)
+ }
+ }
+
+ @KspExperimental
+ override fun getOwnerJvmClassName(declaration: KSPropertyDeclaration): String? {
+ val descriptor = resolvePropertyDeclaration(declaration) ?: return null
+ return getJvmOwnerQualifiedName(descriptor)
+ }
+
+ @KspExperimental
+ override fun getOwnerJvmClassName(declaration: KSFunctionDeclaration): String? {
+ val descriptor = resolveFunctionDeclaration(declaration) ?: return null
+ return getJvmOwnerQualifiedName(descriptor)
+ }
+
+ private fun getJvmOwnerQualifiedName(descriptor: DeclarationDescriptor): String? {
+ return try {
+ typeMapper.mapImplementationOwner(descriptor).className
+ } catch (unsupported: UnsupportedOperationException) {
+ null
+ }
+ }
+
+ private fun extractThrowsFromClassFile(
+ virtualFileContent: ByteArray,
+ jvmDesc: String?,
+ simpleName: String?
+ ): Sequence<KSType> {
+ val exceptionNames = mutableListOf<String>()
+ ClassReader(virtualFileContent).accept(
+ object : ClassVisitor(API_VERSION) {
+ override fun visitMethod(
+ access: Int,
+ name: String?,
+ descriptor: String?,
+ signature: String?,
+ exceptions: Array<out String>?,
+ ): MethodVisitor {
+ if (name == simpleName && jvmDesc == descriptor) {
+ exceptions?.toList()?.let { exceptionNames.addAll(it) }
+ }
+ return object : MethodVisitor(API_VERSION) {
+ }
+ }
+ },
+ ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES
+ )
+ return exceptionNames.mapNotNull {
+ this@ResolverImpl.getClassDeclarationByName(it.replace("/", "."))?.asStarProjectedType()
+ }.asSequence()
+ }
+
+ @SuppressWarnings("UNCHECKED_CAST")
+ private fun extractThrowsAnnotation(annotated: KSAnnotated): Sequence<KSType> {
+ return annotated.annotations
+ .singleOrNull {
+ it.shortName.asString() == "Throws" &&
+ it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.Throws"
+ }?.arguments
+ ?.singleOrNull()
+ ?.let { it.value as? ArrayList<KSType> }
+ ?.asSequence() ?: emptySequence()
+ }
+
+ // TODO: refactor and reuse BinaryClassInfoCache
+ @KspExperimental
+ override fun getJvmCheckedException(function: KSFunctionDeclaration): Sequence<KSType> {
+ return when (function.origin) {
+ Origin.JAVA -> {
+ val psi = (function as KSFunctionDeclarationJavaImpl).psi
+ psi.throwsList.referencedTypes.asSequence().map { getKSTypeCached(resolveJavaType(it)) }
+ }
+ Origin.KOTLIN -> {
+ extractThrowsAnnotation(function)
+ }
+ Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
+ val descriptor = (function as KSFunctionDeclarationDescriptorImpl).descriptor
+ val jvmDesc = this.mapToJvmSignature(function)
+ val virtualFileContent = if (function.origin == Origin.KOTLIN_LIB) {
+ (descriptor.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass)?.file
+ ?.contentsToByteArray()
+ } else {
+ (
+ ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaMethodBase)
+ ?.containingClass as? BinaryJavaClass
+ )?.virtualFile?.contentsToByteArray()
+ }
+ if (virtualFileContent == null) {
+ return emptySequence()
+ }
+ extractThrowsFromClassFile(virtualFileContent, jvmDesc, function.simpleName.asString())
+ }
+ else -> emptySequence()
+ }
+ }
+
+ @KspExperimental
+ override fun getJvmCheckedException(accessor: KSPropertyAccessor): Sequence<KSType> {
+ return when (accessor.origin) {
+ Origin.KOTLIN, Origin.SYNTHETIC -> {
+ extractThrowsAnnotation(accessor)
+ }
+ Origin.KOTLIN_LIB -> {
+ val descriptor = (accessor as KSPropertyAccessorDescriptorImpl).descriptor
+ val jvmDesc = typeMapper.mapAsmMethod(descriptor).descriptor
+ val virtualFileContent = if (accessor.origin == Origin.KOTLIN_LIB) {
+ (descriptor.correspondingProperty.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass)
+ ?.file?.contentsToByteArray()
+ } else {
+ (
+ ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaMethod)?.containingClass
+ as? BinaryJavaClass
+ )?.virtualFile?.contentsToByteArray()
+ }
+ if (virtualFileContent == null) {
+ return emptySequence()
+ }
+ extractThrowsFromClassFile(virtualFileContent, jvmDesc, getJvmName(accessor))
+ }
+ else -> emptySequence()
+ }
+ }
+
+ private val javaPackageToClassMap: Map<String, List<KSDeclaration>> by lazy {
+ val packageToClassMapping = mutableMapOf<String, List<KSDeclaration>>()
+ allKSFiles
+ .filter { file ->
+ file.origin == Origin.JAVA &&
+ options.javaSourceRoots.any { root ->
+ file.filePath.startsWith(root.absolutePath) &&
+ file.filePath.substringAfter(root.absolutePath)
+ .dropLastWhile { c -> c != File.separatorChar }.dropLast(1).drop(1)
+ .replace(File.separatorChar, '.') == file.packageName.asString()
+ }
+ }
+ .forEach {
+ packageToClassMapping.put(
+ it.packageName.asString(),
+ packageToClassMapping.getOrDefault(it.packageName.asString(), emptyList())
+ .plus(
+ it.declarations.filterNot {
+ it.containingFile?.fileName?.split(".")?.first() == it.simpleName.asString()
+ }
+ )
+ )
+ }
+ packageToClassMapping
+ }
+
+ @KspExperimental
+ override fun getDeclarationsFromPackage(packageName: String): Sequence<KSDeclaration> {
+ val noPackageFilter = DescriptorKindFilter.ALL.withoutKinds(DescriptorKindFilter.PACKAGES_MASK)
+ return module.getPackage(FqName(packageName))
+ .memberScope.getContributedDescriptors(noPackageFilter)
+ .asSequence()
+ .mapNotNull { (it as? MemberDescriptor)?.toKSDeclaration() }
+ .plus(javaPackageToClassMap.getOrDefault(packageName, emptyList()).asSequence())
+ }
+
+ override fun getTypeArgument(typeRef: KSTypeReference, variance: Variance): KSTypeArgument {
+ return KSTypeArgumentLiteImpl.getCached(typeRef, variance)
+ }
+
+ internal fun asMemberOf(
+ property: KSPropertyDeclaration,
+ containing: KSType,
+ ): KSType {
+ val key = property to containing
+ return propertyAsMemberOfCache.getOrPut(key) {
+ computeAsMemberOf(property, containing)
+ }
+ }
+
+ private fun computeAsMemberOf(
+ property: KSPropertyDeclaration,
+ containing: KSType,
+ ): KSType {
+ val propertyDeclaredIn = property.closestClassDeclaration()
+ ?: throw IllegalArgumentException(
+ "Cannot call asMemberOf with a property that is " +
+ "not declared in a class or an interface"
+ )
+ val declaration = resolvePropertyDeclaration(property)
+ if (declaration != null && containing is KSTypeImpl && !containing.isError) {
+ incrementalContext.recordLookupWithSupertypes(containing.kotlinType)
+ incrementalContext.recordLookupForDeclaration(property)
+ if (!containing.kotlinType.isSubtypeOf(propertyDeclaredIn)) {
+ throw IllegalArgumentException(
+ "$containing is not a sub type of the class/interface that contains `$property` " +
+ "($propertyDeclaredIn)"
+ )
+ }
+ val typeSubstitutor = containing.kotlinType.createTypeSubstitutor()
+ val substituted = declaration.substitute(typeSubstitutor) as? ValueDescriptor
+ substituted?.let {
+ return getKSTypeCached(substituted.type)
+ }
+ }
+ // if substitution fails, fallback to the type from the property
+ return KSErrorType
+ }
+
+ internal fun asMemberOf(
+ function: KSFunctionDeclaration,
+ containing: KSType,
+ ): KSFunction {
+ val key = function to containing
+ return functionAsMemberOfCache.getOrPut(key) {
+ computeAsMemberOf(function, containing)
+ }
+ }
+
+ private fun computeAsMemberOf(
+ function: KSFunctionDeclaration,
+ containing: KSType,
+ ): KSFunction {
+ val functionDeclaredIn = function.closestClassDeclaration()
+ ?: throw IllegalArgumentException(
+ "Cannot call asMemberOf with a function that is " +
+ "not declared in a class or an interface"
+ )
+ val declaration = resolveFunctionDeclaration(function)
+ if (declaration != null && containing is KSTypeImpl && !containing.isError) {
+ incrementalContext.recordLookupWithSupertypes(containing.kotlinType)
+ incrementalContext.recordLookupForDeclaration(function)
+ if (!containing.kotlinType.isSubtypeOf(functionDeclaredIn)) {
+ throw IllegalArgumentException(
+ "$containing is not a sub type of the class/interface that contains " +
+ "`$function` ($functionDeclaredIn)"
+ )
+ }
+ val typeSubstitutor = containing.kotlinType.createTypeSubstitutor()
+ if (declaration is PropertyAccessorDescriptor) {
+ val substitutedProperty = (declaration.correspondingProperty).substitute(typeSubstitutor)
+ // TODO: Fix in upstream for property accessors: https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyAccessorDescriptorImpl.java#L122
+ return KSFunctionImpl(
+ (substitutedProperty as PropertyDescriptor).accessors.single {
+ it.name == declaration.name
+ }
+ )
+ }
+ val substituted = declaration.substitute(typeSubstitutor)
+ return KSFunctionImpl(substituted)
+ }
+ // if substitution fails, return an error function that resembles the original declaration
+ return KSFunctionErrorImpl(function)
+ }
+
+ private fun KotlinType.isSubtypeOf(declaration: KSClassDeclaration): Boolean {
+ val classDeclaration = resolveClassDeclaration(declaration)
+ if (classDeclaration == null) {
+ throw IllegalArgumentException(
+ "Cannot find the declaration for class $classDeclaration"
+ )
+ }
+ return constructor
+ .declarationDescriptor
+ ?.getAllSuperClassifiers()
+ ?.any { it == classDeclaration } == true
+ }
+
+ override val builtIns: KSBuiltIns by lazy {
+ val builtIns = module.builtIns
+ object : KSBuiltIns {
+ override val anyType: KSType by lazy { getKSTypeCached(builtIns.anyType) }
+ override val nothingType by lazy { getKSTypeCached(builtIns.nothingType) }
+ override val unitType: KSType by lazy { getKSTypeCached(builtIns.unitType) }
+ override val numberType: KSType by lazy { getKSTypeCached(builtIns.numberType) }
+ override val byteType: KSType by lazy { getKSTypeCached(builtIns.byteType) }
+ override val shortType: KSType by lazy { getKSTypeCached(builtIns.shortType) }
+ override val intType: KSType by lazy { getKSTypeCached(builtIns.intType) }
+ override val longType: KSType by lazy { getKSTypeCached(builtIns.longType) }
+ override val floatType: KSType by lazy { getKSTypeCached(builtIns.floatType) }
+ override val doubleType: KSType by lazy { getKSTypeCached(builtIns.doubleType) }
+ override val charType: KSType by lazy { getKSTypeCached(builtIns.charType) }
+ override val booleanType: KSType by lazy { getKSTypeCached(builtIns.booleanType) }
+ override val stringType: KSType by lazy { getKSTypeCached(builtIns.stringType) }
+ override val iterableType: KSType by lazy {
+ getKSTypeCached(builtIns.iterableType.replaceArgumentsWithStarProjections())
+ }
+ override val annotationType: KSType by lazy { getKSTypeCached(builtIns.annotationType) }
+ override val arrayType: KSType by lazy {
+ getKSTypeCached(builtIns.array.defaultType.replaceArgumentsWithStarProjections())
+ }
+ }
+ }
+
+ internal val mockSerializableType = module.builtIns.numberType.supertypes().singleOrNull {
+ it.constructor.declarationDescriptor?.name?.asString() == "Serializable"
+ }
+
+ internal val javaSerializableType = module.resolveClassByFqName(
+ FqName("java.io.Serializable"), NoLookupLocation.WHEN_FIND_BY_FQNAME
+ )?.defaultType
+
+ @KspExperimental
+ override fun mapJavaNameToKotlin(javaName: KSName): KSName? =
+ JavaToKotlinClassMap.mapJavaToKotlin(FqName(javaName.asString()))?.toKSName()
+
+ @KspExperimental
+ override fun mapKotlinNameToJava(kotlinName: KSName): KSName? =
+ JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(kotlinName.asString()))?.toKSName()
+
+ @KspExperimental
+ internal fun mapToJvmSignature(accessor: KSPropertyAccessor): String {
+ return resolvePropertyAccessorDeclaration(accessor)?.let {
+ typeMapper.mapAsmMethod(it).descriptor
+ } ?: ""
+ }
+
+ @KspExperimental
+ override fun getDeclarationsInSourceOrder(container: KSDeclarationContainer): Sequence<KSDeclaration> {
+ return container.declarationsInSourceOrder
+ }
+
+ @KspExperimental
+ override fun effectiveJavaModifiers(declaration: KSDeclaration): Set<Modifier> {
+ val modifiers = HashSet<Modifier>(declaration.modifiers.filter { it in javaModifiers })
+
+ // This is only needed by sources.
+ // PUBLIC, PRIVATE, PROTECTED are already handled in descriptor based impls.
+ fun addVisibilityModifiers() {
+ when {
+ declaration.isPublic() -> modifiers.add(Modifier.PUBLIC)
+ declaration.isPrivate() -> modifiers.add(Modifier.PRIVATE)
+ declaration.isProtected() -> modifiers.add(Modifier.PROTECTED)
+ }
+ }
+
+ when (declaration.origin) {
+ Origin.JAVA -> {
+ addVisibilityModifiers()
+ if (declaration is KSClassDeclaration && declaration.classKind == ClassKind.INTERFACE)
+ modifiers.add(Modifier.ABSTRACT)
+ }
+ Origin.KOTLIN -> {
+ addVisibilityModifiers()
+ if (!declaration.isOpen())
+ modifiers.add(Modifier.FINAL)
+ if (declaration.hasAnnotation(JVM_STATIC_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_STATIC)
+ if (declaration.hasAnnotation(JVM_DEFAULT_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_DEFAULT)
+ if (declaration.hasAnnotation(JVM_DEFAULT_WITHOUT_COMPATIBILITY_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_DEFAULT)
+ if (declaration.hasAnnotation(JVM_STRICTFP_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_STRICT)
+ if (declaration.hasAnnotation(JVM_SYNCHRONIZED_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_SYNCHRONIZED)
+ if (declaration.hasAnnotation(JVM_TRANSIENT_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_TRANSIENT)
+ if (declaration.hasAnnotation(JVM_VOLATILE_ANNOTATION_FQN))
+ modifiers.add(Modifier.JAVA_VOLATILE)
+ when (declaration) {
+ is KSClassDeclaration -> {
+ if (declaration.isCompanionObject)
+ modifiers.add(Modifier.JAVA_STATIC)
+ if (declaration.classKind == ClassKind.INTERFACE)
+ modifiers.add(Modifier.ABSTRACT)
+ }
+ is KSPropertyDeclaration -> {
+ if (declaration.isAbstract())
+ modifiers.add(Modifier.ABSTRACT)
+ }
+ is KSFunctionDeclaration -> {
+ if (declaration.isAbstract)
+ modifiers.add(Modifier.ABSTRACT)
+ }
+ }
+ }
+ Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
+ when (declaration) {
+ is KSPropertyDeclaration -> {
+ if (declaration.jvmAccessFlag and Opcodes.ACC_TRANSIENT != 0)
+ modifiers.add(Modifier.JAVA_TRANSIENT)
+ if (declaration.jvmAccessFlag and Opcodes.ACC_VOLATILE != 0)
+ modifiers.add(Modifier.JAVA_VOLATILE)
+ }
+ is KSFunctionDeclaration -> {
+ if (declaration.jvmAccessFlag and Opcodes.ACC_STRICT != 0)
+ modifiers.add(Modifier.JAVA_STRICT)
+ if (declaration.jvmAccessFlag and Opcodes.ACC_SYNCHRONIZED != 0)
+ modifiers.add(Modifier.JAVA_SYNCHRONIZED)
+ }
+ }
+ }
+ else -> Unit
+ }
+ return modifiers
+ }
+
+ private enum class RefPosition {
+ PARAMETER_TYPE,
+ RETURN_TYPE,
+ SUPER_TYPE
+ }
+
+ // Search in self and parents for the first type reference that is not part of a type argument.
+ private fun KSTypeReference.findOuterMostRef(): Pair<KSTypeReference, List<Int>> {
+ fun KSNode.findParentRef(): KSTypeReference? {
+ var parent = parent
+ while (parent != null && parent !is KSTypeReference)
+ parent = parent.parent
+ return parent as? KSTypeReference
+ }
+
+ val fallback = Pair<KSTypeReference, List<Int>>(this, emptyList())
+ val indexes = mutableListOf<Int>()
+ var candidate: KSTypeReference = this
+ // KSTypeArgument's parent can be either KSReferenceElement or KSType.
+ while (candidate.parent is KSTypeArgument) {
+ // If the parent is a KSType, it's a synthetic reference.
+ // Do nothing and reply on the fallback behavior.
+ val referenceElement = (candidate.parent!!.parent as? KSReferenceElement) ?: return fallback
+ indexes.add(referenceElement.typeArguments.indexOf(candidate.parent))
+ // In case the program isn't properly structured, fallback.
+ candidate = referenceElement.findParentRef() ?: return fallback
+ }
+ return Pair(candidate, indexes)
+ }
+
+ // TODO: Strict mode for catching unhandled cases.
+ private fun findRefPosition(ref: KSTypeReference): RefPosition = when (val parent = ref.parent) {
+ is KSCallableReference -> when (ref) {
+ parent.returnType -> RefPosition.RETURN_TYPE
+ else -> RefPosition.PARAMETER_TYPE
+ }
+ is KSFunctionDeclaration -> when (ref) {
+ parent.returnType -> RefPosition.RETURN_TYPE
+ else -> RefPosition.PARAMETER_TYPE
+ }
+ is KSPropertyGetter -> RefPosition.RETURN_TYPE
+ is KSPropertyDeclaration -> when (ref) {
+ parent.type -> RefPosition.RETURN_TYPE
+ else -> RefPosition.PARAMETER_TYPE
+ }
+ is KSClassDeclaration -> RefPosition.SUPER_TYPE
+ // is KSTypeArgument -> RefPosition.PARAMETER_TYPE
+ // is KSAnnotation -> RefPosition.PARAMETER_TYPE
+ // is KSTypeAlias -> RefPosition.PARAMETER_TYPE
+ // is KSValueParameter -> RefPosition.PARAMETER_TYPE
+ // is KSTypeParameter -> RefPosition.PARAMETER_TYPE
+ else -> RefPosition.PARAMETER_TYPE
+ }
+
+ private fun KSTypeReference.isReturnTypeOfAnnotationMethod(): Boolean {
+ var candidate = this.parent
+ while (candidate !is KSClassDeclaration && candidate != null)
+ candidate = candidate.parent
+ return (candidate as? KSClassDeclaration)?.classKind == ClassKind.ANNOTATION_CLASS
+ }
+
+ // Convert type arguments for Java wildcard, recursively.
+ private fun KotlinType.toWildcard(mode: TypeMappingMode): KotlinType? {
+ val parameters = constructor.parameters
+ val arguments = arguments
+
+ val wildcardArguments = parameters.zip(arguments).map { (parameter, argument) ->
+ if (!argument.isStarProjection &&
+ parameter.variance != argument.projectionKind &&
+ parameter.variance != org.jetbrains.kotlin.types.Variance.INVARIANT &&
+ argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
+ ) {
+ // conflicting variances
+ // TODO: error message
+ return null
+ }
+
+ val argMode = mode.updateFromAnnotations(argument.type)
+ val variance = KotlinTypeMapper.getVarianceForWildcard(parameter, argument, argMode)
+ val genericMode = argMode.toGenericArgumentMode(
+ getEffectiveVariance(parameter.variance, argument.projectionKind)
+ )
+ TypeProjectionImpl(variance, argument.type.toWildcard(genericMode) ?: return null)
+ }
+
+ return replace(wildcardArguments)
+ }
+
+ private val JVM_SUPPRESS_WILDCARDS_NAME = KSNameImpl.getCached("kotlin.jvm.JvmSuppressWildcards")
+ private val JVM_SUPPRESS_WILDCARDS_SHORT = "JvmSuppressWildcards"
+ private fun KSTypeReference.findJvmSuppressWildcards(): Boolean? {
+ var candidate: KSNode? = this
+
+ while (candidate != null) {
+ if ((candidate is KSTypeReference || candidate is KSDeclaration)) {
+ (
+ (candidate as KSAnnotated).annotations.firstOrNull {
+ checkAnnotation(it, JVM_SUPPRESS_WILDCARDS_NAME, JVM_SUPPRESS_WILDCARDS_SHORT)
+ }?.arguments?.firstOrNull()?.value as? Boolean
+ )?.let {
+ // KSAnnotated.getAnnotationsByType is handy but it uses reflection.
+ return it
+ }
+ }
+ candidate = candidate.parent
+ }
+
+ return null
+ }
+
+ private fun TypeMappingMode.updateFromAnnotations(
+ type: KotlinType
+ ): TypeMappingMode {
+ (
+ type.annotations.findAnnotation(JVM_SUPPRESS_WILDCARDS_ANNOTATION_FQ_NAME)
+ ?.argumentValue("suppress")?.value as? Boolean
+ )?.let {
+ return this.suppressJvmWildcards(it)
+ }
+
+ if (type.annotations.hasAnnotation(JVM_WILDCARD_ANNOTATION_FQ_NAME)) {
+ return TypeMappingMode.createWithConstantDeclarationSiteWildcardsMode(
+ skipDeclarationSiteWildcards = false,
+ isForAnnotationParameter = isForAnnotationParameter,
+ fallbackMode = this,
+ needInlineClassWrapping = needInlineClassWrapping,
+ mapTypeAliases = mapTypeAliases
+ )
+ }
+
+ return this
+ }
+
+ private fun TypeMappingMode.suppressJvmWildcards(
+ suppress: Boolean
+ ): TypeMappingMode {
+ return TypeMappingMode.createWithConstantDeclarationSiteWildcardsMode(
+ skipDeclarationSiteWildcards = suppress,
+ isForAnnotationParameter = isForAnnotationParameter,
+ needInlineClassWrapping = needInlineClassWrapping,
+ mapTypeAliases = mapTypeAliases
+ )
+ }
+
+ private fun TypeMappingMode.updateFromParents(
+ ref: KSTypeReference
+ ): TypeMappingMode {
+ ref.findJvmSuppressWildcards()?.let {
+ return this.suppressJvmWildcards(it)
+ }
+
+ return this
+ }
+
+ // Type arguments need to be resolved recursively in a top-down manner. So we find and resolve the outer most
+ // reference that contains this argument. Then locate and return the argument.
+ @KspExperimental
+ override fun getJavaWildcard(reference: KSTypeReference): KSTypeReference {
+ // If the outer-most reference cannot be found, e.g., when this reference is nested in KSType.arguments,
+ // fallback to PARAMETER_TYPE effectively.
+ val (ref, indexes) = reference.findOuterMostRef()
+
+ val type = ref.resolve()
+ if (type.isError)
+ return reference
+
+ val position = findRefPosition(ref)
+ val kotlinType = (type as KSTypeImpl).kotlinType
+
+ val typeSystem = SimpleClassicTypeSystemContext
+ val typeMappingMode = when (position) {
+ RefPosition.PARAMETER_TYPE -> typeSystem.getOptimalModeForValueParameter(kotlinType)
+ RefPosition.RETURN_TYPE ->
+ typeSystem.getOptimalModeForReturnType(kotlinType, ref.isReturnTypeOfAnnotationMethod())
+ RefPosition.SUPER_TYPE -> TypeMappingMode.SUPER_TYPE
+ }.updateFromParents(ref)
+
+ val parameters = kotlinType.constructor.parameters
+ val arguments = kotlinType.arguments
+
+ parameters.zip(arguments).forEach { (parameter, argument) ->
+ if (position == RefPosition.SUPER_TYPE &&
+ argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
+ ) {
+ // Type projection isn't allowed in immediate arguments to supertypes.
+ // TODO: error message
+ return KSTypeReferenceSyntheticImpl.getCached(KSErrorType, null)
+ }
+ }
+
+ val wildcardType = kotlinType.toWildcard(typeMappingMode)?.let {
+ var candidate: KotlinType = it
+ for (i in indexes.reversed()) {
+ candidate = candidate.arguments[i].type
+ }
+ getKSTypeCached(candidate)
+ } ?: KSErrorType
+
+ return KSTypeReferenceSyntheticImpl.getCached(wildcardType, null)
+ }
+
+ @KspExperimental
+ override fun isJavaRawType(type: KSType): Boolean {
+ return type is KSTypeImpl && type.kotlinType.unwrap() is RawType
+ }
+
+ private val psiJavaFiles = allKSFiles.filterIsInstance<KSFileJavaImpl>().map {
+ Pair(it.psi.virtualFile.path, it.psi)
+ }.toMap()
+
+ internal fun findPsiJavaFile(path: String): PsiFile? = psiJavaFiles.get(path)
+}
+
+// TODO: cross module resolution
+fun DeclarationDescriptor.findExpectsInKSDeclaration(): Sequence<KSDeclaration> =
+ findExpects().asSequence().map {
+ it.toKSDeclaration()
+ }
+
+// TODO: cross module resolution
+fun DeclarationDescriptor.findActualsInKSDeclaration(): Sequence<KSDeclaration> =
+ findActuals(this.module).asSequence().map {
+ it.toKSDeclaration()
+ }
+
+fun MemberDescriptor.toKSDeclaration(): KSDeclaration =
+ when (val psi = findPsi()) {
+ is KtClassOrObject -> KSClassDeclarationImpl.getCached(psi)
+ is KtFunction -> KSFunctionDeclarationImpl.getCached(psi)
+ is KtProperty -> KSPropertyDeclarationImpl.getCached((psi))
+ is KtTypeAlias -> KSTypeAliasImpl.getCached(psi)
+ is PsiClass -> KSClassDeclarationJavaImpl.getCached(psi)
+ is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(psi)
+ is PsiField -> KSPropertyDeclarationJavaImpl.getCached(psi)
+ else -> when (this) {
+ is ClassDescriptor -> KSClassDeclarationDescriptorImpl.getCached(this)
+ is FunctionDescriptor -> KSFunctionDeclarationDescriptorImpl.getCached(this)
+ is PropertyDescriptor -> KSPropertyDeclarationDescriptorImpl.getCached(this)
+ else -> throw IllegalStateException("Unknown expect/actual implementation")
+ }
+ }
+
+/**
+ * [NewTypeSubstitutor] handles variance better than [TypeSubstitutor] so we use it when subtituting
+ * types in [ResolverImpl.asMemberOf] implementations.
+ */
+private fun TypeSubstitutor.toNewSubstitutor() = composeWith(
+ org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
+)
+
+private fun KotlinType.createTypeSubstitutor(): NewTypeSubstitutor {
+ return SubstitutionUtils.buildDeepSubstitutor(this).toNewSubstitutor()
+}
+
+/**
+ * Extracts the identifier from a module Name.
+ *
+ * One caveat here is that kotlin passes a special name into the plugin which cannot be used as an identifier.
+ * On the other hand, to construct the correct TypeMapper, we need a non-special name.
+ * This function extracts the non-special name from a given name if it is special.
+ *
+ * @see: https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt#L305
+ */
+private fun Name.getNonSpecialIdentifier(): String {
+ // the analyzer might pass down a special name which will break type mapper name computations.
+ // If it is a special name, we turn it back to an id
+ if (!isSpecial || asString().isBlank()) {
+ return asString()
+ }
+ // special names starts with a `<` and usually end with `>`
+ return if (asString().last() == '>') {
+ asString().substring(1, asString().length - 1)
+ } else {
+ asString().substring(1)
+ }
+}
+
+private inline fun MemberScope.findEnclosedDescriptor(
+ kindFilter: DescriptorKindFilter,
+ crossinline filter: (DeclarationDescriptor) -> Boolean,
+): DeclarationDescriptor? {
+ return getContributedDescriptors(
+ kindFilter = kindFilter
+ ).firstOrNull(filter)
+}
+
+private inline fun ClassDescriptor.findEnclosedDescriptor(
+ kindFilter: DescriptorKindFilter,
+ crossinline filter: (DeclarationDescriptor) -> Boolean,
+): DeclarationDescriptor? {
+ return this.unsubstitutedMemberScope.findEnclosedDescriptor(
+ kindFilter = kindFilter,
+ filter = filter
+ ) ?: this.staticScope.findEnclosedDescriptor(
+ kindFilter = kindFilter,
+ filter = filter
+ ) ?: constructors.firstOrNull {
+ kindFilter.accepts(it) && filter(it)
+ }
+}
+
+internal fun KSAnnotated.findAnnotationFromUseSiteTarget(): Sequence<KSAnnotation> {
+ return when (this) {
+ is KSPropertyGetter -> (this.receiver as? KSDeclarationImpl)?.let {
+ it.originalAnnotations.asSequence().filter { it.useSiteTarget == AnnotationUseSiteTarget.GET }
+ }
+ is KSPropertySetter -> (this.receiver as? KSDeclarationImpl)?.let {
+ it.originalAnnotations.asSequence().filter { it.useSiteTarget == AnnotationUseSiteTarget.SET }
+ }
+ is KSValueParameter -> {
+ var parent = when (this) {
+ is KSValueParameterSyntheticImpl -> this.owner
+ is KSValueParameterImpl -> this.ktParameter.findParentAnnotated()
+ else -> null
+ }
+ // TODO: eliminate annotationsFromParents to make this fully sequence.
+ val annotationsFromParents = mutableListOf<KSAnnotation>()
+ (parent as? KSPropertyAccessorImpl)?.let {
+ annotationsFromParents.addAll(
+ it.originalAnnotations.asSequence()
+ .filter { it.useSiteTarget == AnnotationUseSiteTarget.SETPARAM }
+ )
+ parent = (parent as KSPropertyAccessorImpl).receiver
+ }
+ (parent as? KSPropertyDeclarationImpl)?.let {
+ annotationsFromParents.addAll(
+ it.originalAnnotations.asSequence()
+ .filter { it.useSiteTarget == AnnotationUseSiteTarget.SETPARAM }
+ )
+ }
+ annotationsFromParents.asSequence()
+ }
+ else -> emptySequence()
+ } ?: emptySequence()
+}
+
+// Resolve to underlying type.
+// Only slightly slower than resolving a plain type, because everything is resolved and cached in the first resolve().
+// FIXME: add a resolution mode in resolveUserType() to resolve to underlying type directly.
+internal fun KSTypeReference.resolveToUnderlying(): KSType {
+ var candidate = resolve()
+ var declaration = candidate.declaration
+ while (declaration is KSTypeAlias) {
+ candidate = declaration.type.resolve()
+ declaration = candidate.declaration
+ }
+ return candidate
+}
+
+// TODO: Remove this after upgrading to Kotlin 1.8.20.
+// Temporary work around for https://github.com/google/ksp/issues/1034
+// Force resolve outer most class for Java nested classes.
+internal fun JavaClass.workaroundForNested(
+ lazyJavaResolverContext: LazyJavaResolverContext = ResolverImpl.instance!!.lazyJavaResolverContext
+) {
+ var outerMost = outerClass
+ while (outerMost?.outerClass != null) {
+ outerMost = outerMost.outerClass
+ }
+ outerMost?.classId?.let { lazyJavaResolverContext.components.finder.findClass(it) }
+}