diff options
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.kt | 1613 |
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) } +} |