All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.devtools.ksp.impl.ResolverAAImpl.kt Maven / Gradle / Ivy

There is a newer version: 2.1.20-Beta1-1.0.29
Show newest version
/*
 * Copyright 2022 Google LLC
 * Copyright 2010-2022 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.
 */
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")

package com.google.devtools.ksp.impl

import com.google.devtools.ksp.*
import com.google.devtools.ksp.common.*
import com.google.devtools.ksp.common.impl.*
import com.google.devtools.ksp.common.visitor.CollectAnnotatedSymbolsVisitor
import com.google.devtools.ksp.impl.symbol.java.KSAnnotationJavaImpl
import com.google.devtools.ksp.impl.symbol.kotlin.*
import com.google.devtools.ksp.impl.symbol.util.*
import com.google.devtools.ksp.impl.symbol.util.DeclarationOrdering
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.intellij.openapi.project.Project
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.file.impl.JavaFileManager
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.components.buildSubstitutor
import org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirSymbol
import org.jetbrains.kotlin.analysis.api.fir.types.KaFirType
import org.jetbrains.kotlin.analysis.api.projectStructure.KaSourceModule
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCliJavaFileManagerImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.isRaw
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.getOptimalModeForReturnType
import org.jetbrains.kotlin.load.kotlin.getOptimalModeForValueParameter
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.platform.jvm.JvmPlatform
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.org.objectweb.asm.Opcodes

@Suppress("MemberVisibilityCanBePrivate")
@OptIn(KspExperimental::class)
class ResolverAAImpl(
    val allKSFiles: List,
    val newKSFiles: List,
    val deferredSymbols: Map>,
    val project: Project,
    val incrementalContext: IncrementalContextAA,
) : Resolver {
    companion object {
        private val instance_prop: ThreadLocal = ThreadLocal()
        private val ktModule_prop: ThreadLocal = ThreadLocal()
        var instance: ResolverAAImpl
            get() = instance_prop.get()
            set(value) {
                instance_prop.set(value)
            }
        var ktModule: KaSourceModule
            get() = ktModule_prop.get()
            set(value) {
                ktModule_prop.set(value)
            }
    }
    lateinit var propertyAsMemberOfCache: MutableMap, KSType>
    lateinit var functionAsMemberOfCache: MutableMap, KSFunction>
    val javaFileManager = project.getService(JavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
    private val classBinaryCache = ClsKotlinBinaryClassCache()
    private val packageInfoFiles by lazy {
        allKSFiles.filter { it.fileName == "package-info.java" }.asSequence().memoized()
    }

    private val aliasingFqNs: Map by lazy {
        val result = 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)?.ktFileSymbol?.psi as? KtFile)?.importDirectives?.forEach {
                    it.aliasName?.let { aliasingNames.add(it) }
                }
            }

            override fun visitTypeAlias(typeAlias: KSTypeAlias, data: Unit) {
                typeAlias.qualifiedName?.asString()?.let { fqn ->
                    result[fqn] = typeAlias
                    aliasingNames.add(fqn.substringAfterLast('.'))
                }
            }
        }
        allKSFiles.forEach { it.accept(visitor, Unit) }
        result
    }
    private val aliasingNames: MutableSet = mutableSetOf()

    // TODO: fix in upstream for builtin types.
    override val builtIns: KSBuiltIns by lazy {
        val builtIns = analyze { useSiteSession.builtinTypes }
        object : KSBuiltIns {
            override val anyType: KSType by lazy { KSTypeImpl.getCached(builtIns.any) }
            override val nothingType: KSType by lazy { KSTypeImpl.getCached(builtIns.nothing) }
            override val unitType: KSType by lazy { KSTypeImpl.getCached(builtIns.unit) }
            override val numberType: KSType by lazy {
                getClassDeclarationByName("kotlin.Number")!!.asStarProjectedType()
            }
            override val byteType: KSType by lazy { KSTypeImpl.getCached(builtIns.byte) }
            override val shortType: KSType by lazy { KSTypeImpl.getCached(builtIns.short) }
            override val intType: KSType by lazy { KSTypeImpl.getCached(builtIns.int) }
            override val longType: KSType by lazy { KSTypeImpl.getCached(builtIns.long) }
            override val floatType: KSType by lazy { KSTypeImpl.getCached(builtIns.float) }
            override val doubleType: KSType by lazy { KSTypeImpl.getCached(builtIns.double) }
            override val charType: KSType by lazy { KSTypeImpl.getCached(builtIns.char) }
            override val booleanType: KSType by lazy { KSTypeImpl.getCached(builtIns.boolean) }
            override val stringType: KSType by lazy { KSTypeImpl.getCached(builtIns.string) }
            override val iterableType: KSType by lazy {
                getClassDeclarationByName("kotlin.collections.Iterable")!!.asStarProjectedType()
            }
            override val annotationType: KSType by lazy {
                getClassDeclarationByName("kotlin.Annotation")!!.asStarProjectedType()
            }
            override val arrayType: KSType by lazy {
                getClassDeclarationByName("kotlin.Array")!!.asStarProjectedType()
            }
        }
    }

    override fun createKSTypeReferenceFromKSType(type: KSType): KSTypeReference {
        return KSTypeReferenceSyntheticImpl.getCached(type, null)
    }

    override fun effectiveJavaModifiers(declaration: KSDeclaration): Set {
        val modifiers = HashSet(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)
                (declaration as? KSClassDeclarationImpl)?.let {
                    analyze {
                        if (
                            it.ktClassOrObjectSymbol.staticMemberScope
                                .declarations.contains(declaration.ktDeclarationSymbol)
                        )
                            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 -> {
                if (declaration.hasAnnotation(JVM_STATIC_ANNOTATION_FQN)) {
                    modifiers.add(Modifier.JAVA_STATIC)
                }
                addVisibilityModifiers()
                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
    }

    internal val KSPropertyDeclaration.jvmAccessFlag: Int
        get() = when (origin) {
            Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
                val fileManager = instance.javaFileManager
                val parentClass = this.findParentOfType()
                val classId = (parentClass as KSClassDeclarationImpl).ktClassOrObjectSymbol.classId!!
                BinaryClassInfoCache.getCached(classId, fileManager)
                    ?.fieldAccFlags?.get(this.simpleName.asString()) ?: 0
            }
            else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB")
        }

    internal val KSFunctionDeclaration.jvmAccessFlag: Int
        get() = when (origin) {
            Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
                val jvmDesc = mapToJvmSignatureInternal(this)
                val fileManager = instance.javaFileManager
                val parentClass = this.findParentOfType()
                val classId = (parentClass as KSClassDeclarationImpl).ktClassOrObjectSymbol.classId!!
                BinaryClassInfoCache.getCached(classId, fileManager)
                    ?.methodAccFlags?.get(this.simpleName.asString() + jvmDesc) ?: 0
            }
            else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB")
        }

    override fun getAllFiles(): Sequence {
        return allKSFiles.asSequence()
    }

    override fun getClassDeclarationByName(name: KSName): KSClassDeclaration? {
        fun findClass(name: KSName): KaSymbol? {
            if (name.asString() == "") {
                return null
            }
            val parent = name.getQualifier()
            val simpleName = name.getShortName()
            val classId = ClassId(FqName(parent), Name.identifier(simpleName))
            analyze {
                classId.toKtClassSymbol()
            }?.let { return it }
            return (findClass(KSNameImpl.getCached(name.getQualifier())) as? KaNamedClassSymbol)?.let {
                analyze {
                    (
                        it.staticMemberScope.classifiers { it.asString() == simpleName }.singleOrNull()
                            ?: it.memberScope.classifiers { it.asString() == simpleName }.singleOrNull()
                        ) as? KaNamedClassSymbol
                        ?: it.staticMemberScope.callables { it.asString() == simpleName }.singleOrNull()
                            as? KaEnumEntrySymbol
                }
            }
        }
        return findClass(name)?.let {
            when (it) {
                is KaNamedClassSymbol -> KSClassDeclarationImpl.getCached(it)
                is KaEnumEntrySymbol -> KSClassDeclarationEnumEntryImpl.getCached(it)
                else -> throw IllegalStateException()
            }
        }
    }

    @OptIn(KaExperimentalApi::class)
    @KspExperimental
    override fun getDeclarationsFromPackage(packageName: String): Sequence {
        return analyze {
            val packageNames = FqName(packageName).pathSegments().map { it.asString() }
            var packages = listOf(useSiteSession.rootPackageSymbol)
            for (curName in packageNames) {
                packages = packages
                    .flatMap { it.packageScope.getPackageSymbols { it.asString() == curName } }
                    .distinct()
            }
            packages.flatMap {
                it.packageScope.declarations.distinct().mapNotNull { symbol ->
                    when (symbol) {
                        is KaNamedClassSymbol -> KSClassDeclarationImpl.getCached(symbol)
                        is KaFunctionSymbol -> KSFunctionDeclarationImpl.getCached(symbol)
                        is KaPropertySymbol -> KSPropertyDeclarationImpl.getCached(symbol)
                        is KaTypeAliasSymbol -> KSTypeAliasImpl.getCached(symbol)
                        is KaJavaFieldSymbol -> KSPropertyDeclarationJavaImpl.getCached(symbol)
                        else -> null
                    }
                }
            }.asSequence()
        }
    }

    override fun getDeclarationsInSourceOrder(container: KSDeclarationContainer): Sequence {
        if (container.origin != Origin.KOTLIN_LIB) {
            return container.declarations
        }

        // TODO: multiplatform
        if (!isJvm) {
            return container.declarations
        }

        require(container is AbstractKSDeclarationImpl)
        val fileManager = instance.javaFileManager
        var parentClass: KSNode = container
        while (parentClass.parent != null &&
            (parentClass !is KSClassDeclarationImpl || parentClass.ktClassOrObjectSymbol.classId == null)
        ) {
            parentClass = parentClass.parent!!
        }

        if (parentClass !is KSClassDeclarationImpl) {
            return container.declarations
        }

        // Members of Foo's companion object are compiled into Foo and Foo$Companion. Total ordering is not recoverable
        // from class files. Let's give up and rely on AA for now.
        if (parentClass.isCompanionObject) {
            return container.declarations
        }

        val classId = parentClass.ktClassOrObjectSymbol.classId ?: return container.declarations
        val virtualFile = classId.getVirtualFile(fileManager) ?: return container.declarations
        val kotlinClass = classBinaryCache.getKotlinBinaryClass(virtualFile) ?: return container.declarations
        val declarationOrdering = DeclarationOrdering(kotlinClass)

        @Suppress("UNCHECKED_CAST")
        return (container.declarations as? Sequence)
            ?.sortedWith(declarationOrdering.comparator) ?: container.declarations
    }

    override fun getFunctionDeclarationsByName(
        name: KSName,
        includeTopLevel: Boolean
    ): Sequence {
        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(
                analyze {
                    findTopLevelCallables(FqName(qualifier), Name.identifier(functionName))
                        .filterIsInstance()
                        .map {
                            KSFunctionDeclarationImpl.getCached(it)
                        }
                }
            )
        }
    }

    override fun getJavaWildcard(reference: KSTypeReference): KSTypeReference {
        val (ref, indexes) = reference.findOuterMostRef()
        val type = ref.resolve()
        if (type.isError)
            return reference
        val position = findRefPosition(ref)
        val ktType = (type as KSTypeImpl).type
        // cast to FIR internal needed due to missing support in AA for type mapping mode
        // and corresponding type mapping APIs.
        val coneType = (ktType as KaFirType).coneType
        val mode = analyze {
            val typeContext = analyze { useSiteModule.getFirResolveSession(project).useSiteFirSession.typeContext }
            when (position) {
                RefPosition.RETURN_TYPE -> typeContext.getOptimalModeForReturnType(
                    coneType,
                    reference.isReturnTypeOfAnnotationMethod()
                )
                RefPosition.SUPER_TYPE -> TypeMappingMode.SUPER_TYPE
                RefPosition.PARAMETER_TYPE -> typeContext.getOptimalModeForValueParameter(coneType)
            }.updateFromParents(ref)
        }
        return analyze {
            ktType.toWildcard(mode).let {
                var candidate: KaType = it
                for (i in indexes.reversed()) {
                    candidate = candidate.typeArguments()[i].type!!
                }
                KSTypeReferenceSyntheticImpl.getCached(KSTypeImpl.getCached(candidate), null)
            }
        }
    }

    override fun getJvmCheckedException(accessor: KSPropertyAccessor): Sequence {
        return when (accessor.origin) {
            Origin.KOTLIN, Origin.SYNTHETIC -> {
                extractThrowsAnnotation(accessor)
            }
            Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
                val fileManager = javaFileManager
                val parentClass = accessor.findParentOfType()
                val classId = (parentClass as KSClassDeclarationImpl).ktClassOrObjectSymbol.classId
                    ?: return emptySequence()
                val virtualFileContent = classId.getFileContent(fileManager) ?: return emptySequence()
                val jvmDesc = this.mapToJvmSignatureInternal(accessor)
                extractThrowsFromClassFile(
                    virtualFileContent,
                    jvmDesc,
                    (if (accessor is KSPropertyGetter) "get" else "set") +
                        accessor.receiver.simpleName.asString().replaceFirstChar(Char::uppercaseChar)
                )
            }
            else -> emptySequence()
        }
    }

    @OptIn(KaExperimentalApi::class)
    override fun getJvmCheckedException(function: KSFunctionDeclaration): Sequence {
        return when (function.origin) {
            Origin.JAVA -> {
                val psi = (function as KSFunctionDeclarationImpl).ktFunctionSymbol.psi as PsiMethod
                psi.throwsList.referencedTypes.asSequence().mapNotNull {
                    analyze {
                        it.asKaType(psi)?.let { KSTypeImpl.getCached(it) }
                    }
                }
            }
            Origin.KOTLIN -> {
                extractThrowsAnnotation(function)
            }
            Origin.KOTLIN_LIB, Origin.JAVA_LIB -> {
                val fileManager = javaFileManager
                val parentClass = function.findParentOfType()
                val classId = (parentClass as KSClassDeclarationImpl).ktClassOrObjectSymbol.classId
                    ?: return emptySequence()
                val virtualFileContent = classId.getFileContent(fileManager) ?: return emptySequence()
                val jvmDesc = this.mapToJvmSignature(function)
                extractThrowsFromClassFile(virtualFileContent, jvmDesc, function.simpleName.asString())
            }
            else -> emptySequence()
        }
    }

    // TODO: handle library symbols
    override fun getJvmName(accessor: KSPropertyAccessor): String {
        val symbol: KaPropertyAccessorSymbol? = when (accessor) {
            is KSPropertyAccessorImpl -> accessor.ktPropertyAccessorSymbol
            else -> null
        }

        symbol?.explictJvmName()?.let {
            return it
        }

        if (accessor.receiver.closestClassDeclaration()?.classKind == ClassKind.ANNOTATION_CLASS) {
            return accessor.receiver.simpleName.asString()
        }

        val prefix = if (accessor is KSPropertyGetter) {
            "get"
        } else {
            "set"
        }

        val name = accessor.receiver.simpleName.asString().replaceFirstChar(Char::uppercaseChar)
        val inlineSuffix = symbol?.inlineSuffix ?: ""
        val mangledName = symbol?.internalSuffix ?: ""
        return "$prefix$name$inlineSuffix$mangledName"
    }

    // TODO: handle library symbols
    override fun getJvmName(declaration: KSFunctionDeclaration): String {
        val symbol: KaFunctionSymbol? = when (declaration) {
            is KSFunctionDeclarationImpl -> declaration.ktFunctionSymbol
            else -> null
        }

        symbol?.explictJvmName()?.let {
            return it
        }

        val inlineSuffix = symbol?.inlineSuffix ?: ""
        val mangledName = symbol?.internalSuffix ?: ""
        return declaration.simpleName.asString() + inlineSuffix + mangledName
    }

    override fun getKSNameFromString(name: String): KSName {
        return KSNameImpl.getCached(name)
    }

    // FIXME: correct implementation after incremental is ready.
    override fun getNewFiles(): Sequence {
        return newKSFiles.asSequence()
    }

    private fun getOwnerJvmClassNameHelper(declaration: KSDeclaration): String? {
        @Suppress("UNCHECKED_CAST")
        return declaration.closestClassDeclaration()?.let {
            // Find classId for JvmClassName for member callables.
            (it as? KSClassDeclarationImpl)?.ktClassOrObjectSymbol?.classId
                ?.asString()?.replace(".", "$")?.replace("/", ".")
        } ?: declaration.containingFile?.let {
            // Find containing file facade class name from file symbol
            (it as? KSFileImpl)?.let {
                ((it.ktFileSymbol.psi as? KtFile)?.findFacadeClass() as? KtLightClassForFacade)
                    ?.facadeClassFqName?.asString()
            }
            // Down cast to fir symbol for library symbols as light facade class for libraries not available in AA.
        } ?: (
            ((declaration as? AbstractKSDeclarationImpl)?.ktDeclarationSymbol as? KaFirSymbol>)
                ?.firSymbol?.containerSource as? JvmPackagePartSource
            )?.classId?.asFqNameString()
    }

    override fun getOwnerJvmClassName(declaration: KSFunctionDeclaration): String? {
        return getOwnerJvmClassNameHelper(declaration)
    }

    override fun getOwnerJvmClassName(declaration: KSPropertyDeclaration): String? {
        return getOwnerJvmClassNameHelper(declaration)
    }

    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 = (
                analyze {
                    findTopLevelCallables(FqName(qualifier), Name.identifier(propertyName)).singleOrNull {
                        it is KaPropertySymbol
                    }?.toKSDeclaration() as? KSPropertyDeclaration
                }
                )
            if (topLevelResult != null && nonTopLevelResult != null) {
                throw IllegalStateException("Found multiple properties with same qualified name")
            }
            nonTopLevelResult ?: topLevelResult
        }
    }

    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
    }
    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
    }
    // Currently, all annotation types are imlemented by KSTypeReferenceResolvedImpl.
    // The short-name-check optimization doesn't help.
    override fun getSymbolsWithAnnotation(annotationName: String, inDepth: Boolean): Sequence {
        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 newSymbols = if (inDepth) newAnnotatedSymbolsWithLocals else newAnnotatedSymbols
        return (newSymbols + deferredSymbolsRestored).asSequence().filter(::checkAnnotated)
    }

    private fun collectAnnotatedSymbols(inDepth: Boolean): Collection {
        val visitor = CollectAnnotatedSymbolsVisitor(inDepth)

        for (file in newKSFiles) {
            file.accept(visitor, Unit)
        }

        return visitor.symbols
    }

    private val deferredSymbolsRestored: Set by lazy {
        deferredSymbols.values.flatten().mapNotNull { it.restore() }.toSet()
    }

    private val newAnnotatedSymbols: Collection by lazy {
        collectAnnotatedSymbols(false)
    }

    private val newAnnotatedSymbolsWithLocals: Collection by lazy {
        collectAnnotatedSymbols(true)
    }

    override fun getTypeArgument(typeRef: KSTypeReference, variance: Variance): KSTypeArgument {
        return KSTypeArgumentLiteImpl.getCached(typeRef, variance)
    }

    override fun isJavaRawType(type: KSType): Boolean {
        return type is KSTypeImpl && (type.type as KaFirType).coneType.isRaw()
    }

    internal fun KSFile.getPackageAnnotations() = (this as? KSFileJavaImpl)?.psi?.packageStatement?.annotationList
        ?.annotations?.map { KSAnnotationJavaImpl.getCached(it, this) } ?: emptyList()

    @KspExperimental
    override fun getPackageAnnotations(packageName: String): Sequence {
        return packageInfoFiles.singleOrNull { it.packageName.asString() == packageName }
            ?.getPackageAnnotations()?.asSequence() ?: emptySequence()
    }

    @KspExperimental
    override fun getPackagesWithAnnotation(annotationName: String): Sequence {
        return packageInfoFiles.filter {
            it.getPackageAnnotations().any {
                (it.annotationType.element as? KSClassifierReference)?.referencedName()
                    ?.substringAfterLast(".") == annotationName.substringAfterLast(".") &&
                    it.annotationType.resolve().declaration.qualifiedName?.asString() == annotationName
            }
        }.map { it.packageName.asString() }
    }

    @OptIn(KaExperimentalApi::class)
    @KspExperimental
    override fun getModuleName(): KSName {
        return KSNameImpl.getCached((ktModule.stableModuleName ?: ktModule.name).removeSurrounding("<", ">"))
    }

    @KspExperimental
    override fun mapJavaNameToKotlin(javaName: KSName): KSName? {
        return JavaToKotlinClassMap.mapJavaToKotlin(FqName(javaName.asString()))?.toKSName()
    }

    @KspExperimental
    override fun mapKotlinNameToJava(kotlinName: KSName): KSName? {
        return JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(kotlinName.asString()))?.toKSName()
    }

    @KspExperimental
    override fun mapToJvmSignature(declaration: KSDeclaration): String? {
        return mapToJvmSignatureInternal(declaration)
    }

    internal fun mapToJvmSignature(accessor: KSPropertyAccessor): String {
        return mapToJvmSignatureInternal(accessor) ?: ""
    }

    override fun overrides(overrider: KSDeclaration, overridee: KSDeclaration): Boolean {
        val overriderSymbol = when (overrider) {
            is KSFunctionDeclarationImpl -> if (overrider.ktFunctionSymbol is KaPropertyAccessorSymbol) {
                (overrider.parent as KSPropertyDeclarationImpl).ktPropertySymbol
            } else {
                overrider.ktFunctionSymbol
            }
            is KSPropertyDeclarationImpl -> overrider.ktPropertySymbol
            else -> return false
        }
        val overrideeSymbol = when (overridee) {
            is KSFunctionDeclarationImpl -> overridee.ktFunctionSymbol
            is KSPropertyDeclarationImpl -> overridee.ktPropertySymbol
            else -> return false
        }
        overrider.closestClassDeclaration()?.asStarProjectedType()?.let {
            recordLookupWithSupertypes((it as KSTypeImpl).type)
        }
        recordLookupForPropertyOrMethod(overrider)
        recordLookupForPropertyOrMethod(overridee)
        return analyze {
            overriderSymbol.allOverriddenSymbols.contains(overrideeSymbol) ||
                overriderSymbol.intersectionOverriddenSymbols.contains(overrideeSymbol)
        }
    }

    override fun overrides(
        overrider: KSDeclaration,
        overridee: KSDeclaration,
        containingClass: KSClassDeclaration
    ): Boolean {
        recordLookupForPropertyOrMethod(overrider)
        recordLookupForPropertyOrMethod(overridee)
        return when (overrider) {
            is KSPropertyDeclaration -> containingClass.getAllProperties().singleOrNull {
                recordLookupForPropertyOrMethod(it)
                it.simpleName.asString() == overrider.simpleName.asString()
            }?.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
                        }
                    ).any { overrides(it, overridee) }
                } else {
                    candidates.any {
                        recordLookupForPropertyOrMethod(it)
                        overrides(it, overridee)
                    }
                }
            }
            else -> false
        }
    }

    @OptIn(KaExperimentalApi::class)
    internal fun mapToJvmSignatureInternal(ksAnnotated: KSAnnotated): String? {
        fun KaType.toSignature(): String {
            return analyze {
                [email protected]().descriptor.let {
                    when (it) {
                        "Ljava.lang.Void;" -> "Ljava/lang/Void;"
                        "Lkotlin/Unit;" -> "V"
                        else -> it
                    }
                }
            }
        }

        fun KSType.toSignature(): String {
            return if (this is KSTypeImpl) {
                analyze {
                    val decl = ([email protected] as? KSClassDeclaration)
                    // Force inline value class to be unwrapped.
                    // Do not remove until AA fixes this.
                    decl?.primaryConstructor
                    type.toSignature()
                }
            } else {
                ""
            }
        }

        return when (ksAnnotated) {
            is KSClassDeclaration -> analyze {
                (ksAnnotated.asStarProjectedType() as KSTypeImpl).toSignature()
            }
            is KSFunctionDeclarationImpl -> {
                analyze {
                    buildString {
                        append("(")
                        ksAnnotated.parameters.forEach { append(it.type.resolve().toSignature()) }
                        append(")")
                        if (ksAnnotated.isConstructor()) {
                            append("V")
                        } else {
                            append(ksAnnotated.returnType!!.resolve().toSignature())
                        }
                    }
                }
            }
            is KSPropertyDeclaration -> {
                analyze {
                    ksAnnotated.type.resolve().toSignature()
                }
            }
            is KSPropertyAccessorImpl -> {
                analyze {
                    buildString {
                        append("(")
                        ksAnnotated.ktPropertyAccessorSymbol.valueParameters.forEach {
                            append(it.returnType.toSignature())
                        }
                        append(")")
                        append(ksAnnotated.ktPropertyAccessorSymbol.returnType.toSignature())
                    }
                }
            }
            else -> null
        }
    }

    @OptIn(KaExperimentalApi::class)
    internal fun computeAsMemberOf(property: KSPropertyDeclaration, containing: KSType): KSType {
        val declaredIn = property.closestClassDeclaration()
            ?: throw IllegalArgumentException(
                "Cannot call asMemberOf with a property that is not declared in a class or an interface"
            )
        val key = property to containing
        return propertyAsMemberOfCache.getOrPut(key) {
            val resolved = property.type.resolve()
            if (containing is KSTypeImpl && resolved is KSTypeImpl) {
                recordLookupWithSupertypes(containing.type)
                recordLookupForPropertyOrMethod(property)
                val isSubTypeOf = analyze {
                    (declaredIn.asStarProjectedType() as? KSTypeImpl)?.type?.let {
                        containing.type.isSubtypeOf(it)
                    } ?: false
                }
                if (!isSubTypeOf) {
                    throw IllegalArgumentException(
                        "$containing is not a sub type of the class/interface that contains `$property` ($declaredIn)"
                    )
                }
                analyze {
                    buildSubstitutor {
                        fillInDeepSubstitutor(containing.type, this@buildSubstitutor)
                    }.let {
                        // recursively substitute to ensure transitive substitution works.
                        // should fix in upstream as well.
                        var result = resolved.type
                        var cnt = 0
                        while (it.substitute(result) != result) {
                            if (cnt > 100) {
                                throw IllegalStateException(
                                    "Potential infinite loop in type substitution for computeAsMemberOf"
                                )
                            }
                            result = it.substitute(result)
                            cnt += 1
                        }
                        KSTypeImpl.getCached(result)
                    }
                }
            } else {
                return if (resolved.isError) resolved else KSErrorType(resolved.toString())
            }
        }
    }

    @OptIn(KaExperimentalApi::class)
    internal fun computeAsMemberOf(function: KSFunctionDeclaration, containing: KSType): KSFunction {
        val propertyDeclaredIn = function.closestClassDeclaration()
            ?: throw IllegalArgumentException(
                "Cannot call asMemberOf with a function that is not declared in a class or an interface"
            )
        val key = function to containing
        return functionAsMemberOfCache.getOrPut(key) {
            if (containing is KSTypeImpl) {
                recordLookupWithSupertypes(containing.type)
                recordLookupForPropertyOrMethod(function)
                val isSubTypeOf = analyze {
                    (propertyDeclaredIn.asStarProjectedType() as? KSTypeImpl)?.type?.let {
                        containing.type.isSubtypeOf(it)
                    } ?: false
                }
                if (!isSubTypeOf) {
                    throw IllegalArgumentException(
                        "$containing is not a sub type of the class/interface that contains `$function` " +
                            "($propertyDeclaredIn)"
                    )
                }
                analyze {
                    buildSubstitutor {
                        fillInDeepSubstitutor(containing.type, this@buildSubstitutor)
                    }.let {
                        // recursively substitute to ensure transitive substitution works.
                        // should fix in upstream as well.
                        // for functions we need to test both returnType and value parameters converges.
                        var funcToSub = (function as KSFunctionDeclarationImpl).ktFunctionSymbol.substitute(it)
                        var next = funcToSub.substitute(it)
                        var cnt = 0
                        while (
                            funcToSub.returnType != next.returnType ||
                            funcToSub.valueParameters.zip(next.valueParameters)
                                .any { it.first.returnType != it.second.returnType } ||
                            funcToSub.receiverType != next.receiverType
                        ) {
                            if (cnt > 100) {
                                throw IllegalStateException(
                                    "Potential infinite loop in type substitution for computeAsMemberOf"
                                )
                            }
                            funcToSub = next
                            next = funcToSub.substitute(it)
                            cnt += 1
                        }
                        KSFunctionImpl(funcToSub, it)
                    }
                }
            } else KSFunctionErrorImpl(function)
        }
    }

    internal val isJvm = ktModule.targetPlatform.all { it is JvmPlatform }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy