| /* |
| * 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.Variance |
| import com.google.devtools.ksp.symbol.impl.binary.* |
| 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.kotlin.* |
| import com.google.devtools.ksp.symbol.impl.resolveContainingClass |
| import com.google.devtools.ksp.symbol.impl.synthetic.* |
| import com.intellij.openapi.project.Project |
| import com.intellij.psi.* |
| import com.intellij.psi.impl.source.PsiClassReferenceType |
| 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.KotlinTypeMapper |
| 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.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.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.kotlin.VirtualFileKotlinClass |
| import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass |
| import org.jetbrains.kotlin.name.ClassId |
| 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.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.ConstantValue |
| import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator |
| import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers |
| import org.jetbrains.kotlin.resolve.jvm.multiplatform.JavaActualAnnotationArgumentExtractor |
| 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.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.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) |
| val javaActualAnnotationArgumentExtractor = JavaActualAnnotationArgumentExtractor() |
| private val nameToKSMap: MutableMap<KSName, KSClassDeclaration> |
| |
| /** |
| * 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 aliasingFqNs: MutableMap<String, KSTypeAlias> = mutableMapOf() |
| private val aliasingNames: MutableSet<String> = mutableSetOf() |
| |
| companion object { |
| lateinit var resolveSession: ResolveSession |
| lateinit var bodyResolver: BodyResolver |
| lateinit var constantExpressionEvaluator: ConstantExpressionEvaluator |
| lateinit var declarationScopeProvider: DeclarationScopeProvider |
| lateinit var topDownAnalyzer: LazyTopDownAnalyzer |
| lateinit var instance: ResolverImpl |
| lateinit var annotationResolver: AnnotationResolver |
| lateinit var moduleClassResolver: ModuleClassResolver |
| lateinit var javaTypeResolver: JavaTypeResolver |
| lateinit var lazyJavaResolverContext: LazyJavaResolverContext |
| } |
| |
| init { |
| resolveSession = componentProvider.get() |
| bodyResolver = componentProvider.get() |
| declarationScopeProvider = componentProvider.get() |
| topDownAnalyzer = componentProvider.get() |
| constantExpressionEvaluator = componentProvider.get() |
| annotationResolver = resolveSession.annotationResolver |
| |
| 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) } |
| } |
| |
| 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) } |
| } |
| |
| 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 |
| } |
| } |
| |
| 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 |
| |
| fun checkAnnotation(annotated: KSAnnotated): Boolean { |
| val ksName = KSNameImpl.getCached(realAnnotationName) |
| |
| return annotated.annotations.any { |
| val annotationType = it.annotationType |
| val referencedName = (annotationType.element as? KSClassifierReference)?.referencedName() |
| val simpleName = referencedName?.substringAfterLast('.') |
| (simpleName == null || simpleName == ksName.getShortName() || simpleName in aliasingNames) && |
| annotationType.resolveToUnderlying().declaration.qualifiedName == ksName |
| } |
| } |
| |
| // TODO: Make visitor a generator |
| val visitor = object : KSVisitorVoid() { |
| val symbols = mutableSetOf<KSAnnotated>() |
| override fun visitAnnotated(annotated: KSAnnotated, data: Unit) { |
| if (checkAnnotation(annotated)) { |
| symbols.add(annotated) |
| } |
| } |
| |
| override fun visitFile(file: KSFile, data: Unit) { |
| visitAnnotated(file, data) |
| file.declarations.forEach { it.accept(this, data) } |
| } |
| |
| override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { |
| visitAnnotated(classDeclaration, data) |
| classDeclaration.typeParameters.forEach { it.accept(this, data) } |
| classDeclaration.declarations.forEach { it.accept(this, data) } |
| classDeclaration.primaryConstructor?.let { it.accept(this, data) } |
| } |
| |
| override fun visitPropertyGetter(getter: KSPropertyGetter, data: Unit) { |
| visitAnnotated(getter, data) |
| } |
| |
| override fun visitPropertySetter(setter: KSPropertySetter, data: Unit) { |
| visitAnnotated(setter, data) |
| } |
| |
| override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) { |
| visitAnnotated(function, data) |
| function.typeParameters.forEach { it.accept(this, data) } |
| function.parameters.forEach { it.accept(this, data) } |
| if (inDepth) { |
| function.declarations.forEach { it.accept(this, data) } |
| } |
| } |
| |
| override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) { |
| visitAnnotated(property, data) |
| property.typeParameters.forEach { it.accept(this, data) } |
| property.getter?.let { it.accept(this, data) } |
| property.setter?.let { it.accept(this, data) } |
| } |
| |
| override fun visitTypeParameter(typeParameter: KSTypeParameter, data: Unit) { |
| visitAnnotated(typeParameter, data) |
| super.visitTypeParameter(typeParameter, data) |
| } |
| |
| override fun visitValueParameter(valueParameter: KSValueParameter, data: Unit) { |
| if (valueParameter.isVal || valueParameter.isVar) { |
| return |
| } |
| visitAnnotated(valueParameter, data) |
| } |
| } |
| |
| for (file in newKSFiles) { |
| file.accept(visitor, Unit) |
| } |
| return visitor.symbols.asSequence() + deferredSymbols.values.flatten() |
| .mapNotNull { it.getInstanceForCurrentRound() }.filter { checkAnnotation(it) } |
| } |
| |
| 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? { |
| return when (declaration) { |
| is KSClassDeclaration -> resolveClassDeclaration(declaration)?.let { typeMapper.mapType(it).descriptor } |
| is KSFunctionDeclaration -> resolveFunctionDeclaration(declaration)?.let { |
| typeMapper.mapAsmMethod(it).descriptor |
| } |
| 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) |
| } |
| |
| fun evaluateConstant(expression: KtExpression?, expectedType: KotlinType): ConstantValue<*>? { |
| return expression?.let { constantExpressionEvaluator.evaluateToConstantValue(it, bindingTrace, 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)) |
| 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) |
| ?.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): FunctionDescriptor? { |
| 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 FunctionDescriptor? |
| } |
| |
| 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 -> { |
| 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()) |
| } |
| |
| 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)) |
| } 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 |
| return getKSTypeCached( |
| LazyJavaTypeParameterDescriptor( |
| lazyJavaResolverContext, |
| JavaTypeParameterImpl(psi), |
| psi.index, |
| containingDeclaration |
| ).defaultType |
| ) |
| } else { |
| 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 && psi !is KtTypeParameter) { |
| when (psi) { |
| is KtClassOrObject -> KSClassDeclarationImpl.getCached(psi) |
| is PsiClass -> KSClassDeclarationJavaImpl.getCached(psi) |
| is KtTypeAlias -> KSTypeAliasImpl.getCached(psi) |
| is PsiEnumConstant -> KSClassDeclarationJavaEnumEntryImpl.getCached(psi) |
| else -> throw IllegalStateException("Unexpected psi type: ${psi.javaClass}, $ExceptionMessage") |
| } |
| } else { |
| when (descriptor) { |
| is ClassDescriptor -> KSClassDeclarationDescriptorImpl.getCached(descriptor) |
| is TypeParameterDescriptor -> 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?.let { |
| // KotlinTypeMapper.mapSignature always uses OwnerKind.IMPLEMENTATION |
| typeMapper.mapFunctionName(descriptor, 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 |
| } |
| } |
| |
| @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() |
| } |
| |
| @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? BinaryJavaMethod)?.containingClass |
| as? BinaryJavaClass |
| )?.virtualFile?.contentsToByteArray() |
| } |
| if (virtualFileContent == null) { |
| return emptySequence() |
| } |
| 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 == function.simpleName.asString() && jvmDesc == descriptor) { |
| exceptions?.toList()?.let { exceptionNames.addAll(it) } |
| } |
| return object : MethodVisitor(API_VERSION) { |
| } |
| } |
| }, |
| ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES |
| ) |
| exceptionNames.mapNotNull { |
| this@ResolverImpl.getClassDeclarationByName(it.replace("/", "."))?.asStarProjectedType() |
| }.asSequence() |
| } |
| else -> emptySequence() |
| } |
| } |
| |
| @KspExperimental |
| override fun getJvmCheckedException(accessor: KSPropertyAccessor): Sequence<KSType> { |
| return when (accessor.origin) { |
| Origin.KOTLIN, Origin.SYNTHETIC -> { |
| extractThrowsAnnotation(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 KSTypeImpl.getCached(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 |
| |
| private fun ClassId.toKSName() = KSNameImpl.getCached(asSingleFqName().toString()) |
| |
| @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() |
| } |
| |
| // TODO: cross module resolution |
| fun DeclarationDescriptor.findExpectsInKSDeclaration(): Sequence<KSDeclaration> = |
| findExpects().asSequence().map { |
| it.toKSDeclaration() |
| } |
| |
| // TODO: cross module resolution |
| fun DeclarationDescriptor.findActualsInKSDeclaration(): Sequence<KSDeclaration> = |
| findActuals().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 |
| } |