diff options
Diffstat (limited to 'compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl')
69 files changed, 6294 insertions, 0 deletions
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSAnnotationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSAnnotationDescriptorImpl.kt new file mode 100644 index 00000000..e52920c7 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSAnnotationDescriptorImpl.kt @@ -0,0 +1,347 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.findPsi +import com.google.devtools.ksp.symbol.impl.java.KSAnnotationJavaImpl +import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType +import com.google.devtools.ksp.symbol.impl.kotlin.KSValueArgumentLiteImpl +import com.google.devtools.ksp.symbol.impl.kotlin.getKSTypeCached +import com.google.devtools.ksp.symbol.impl.synthetic.KSTypeReferenceSyntheticImpl +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiAnnotationMethod +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.NotFoundClasses +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.load.java.components.JavaAnnotationDescriptor +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaAnnotationDescriptor +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaAnnotationVisitor +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethod +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext +import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass +import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue +import org.jetbrains.kotlin.resolve.constants.* +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.org.objectweb.asm.AnnotationVisitor +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION + +class KSAnnotationDescriptorImpl private constructor( + val descriptor: AnnotationDescriptor, + override val parent: KSNode? +) : KSAnnotation { + companion object : KSObjectCache<Pair<AnnotationDescriptor, KSNode?>, KSAnnotationDescriptorImpl>() { + fun getCached(descriptor: AnnotationDescriptor, parent: KSNode?) = cache.getOrPut(Pair(descriptor, parent)) { + KSAnnotationDescriptorImpl(descriptor, parent) + } + } + + override val origin = + when (descriptor) { + is JavaAnnotationDescriptor, is LazyJavaAnnotationDescriptor -> Origin.JAVA_LIB + else -> Origin.KOTLIN_LIB + } + + override val location: Location = NonExistLocation + + override val annotationType: KSTypeReference by lazy { + KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, this) + } + + override val arguments: List<KSValueArgument> by lazy { + descriptor.createKSValueArguments(this) + } + + override val defaultArguments: List<KSValueArgument> by lazy { + descriptor.getDefaultArguments(this) + } + + override val shortName: KSName by lazy { + KSNameImpl.getCached(descriptor.fqName!!.shortName().asString()) + } + + override val useSiteTarget: AnnotationUseSiteTarget? = null + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitAnnotation(this, data) + } + + override fun toString(): String { + return "@${shortName.asString()}" + } +} + +private fun ClassId.findKSClassDeclaration(): KSClassDeclaration? { + val ksName = KSNameImpl.getCached(this.asSingleFqName().asString().replace("$", ".")) + return ResolverImpl.instance!!.getClassDeclarationByName(ksName) +} + +private fun ClassId.findKSType(): KSType? = findKSClassDeclaration()?.asStarProjectedType() + +private fun <T> ConstantValue<T>.toValue(parent: KSNode): Any? = when (this) { + is AnnotationValue -> KSAnnotationDescriptorImpl.getCached(value, parent) + is ArrayValue -> value.map { it.toValue(parent) } + is EnumValue -> value.first.findKSClassDeclaration()?.declarations?.find { + it is KSClassDeclaration && it.classKind == ClassKind.ENUM_ENTRY && + it.simpleName.asString() == value.second.asString() + }?.let { (it as KSClassDeclaration).asStarProjectedType() } + is KClassValue -> when (val classValue = value) { + is KClassValue.Value.NormalClass -> if (classValue.arrayDimensions > 0) { + classValue.value.classId.findKSType()?.let { componentType -> + var resultingType = componentType + for (i in 1..classValue.arrayDimensions) { + resultingType = ResolverImpl.instance!!.builtIns.arrayType.replace( + listOf( + ResolverImpl.instance!!.getTypeArgument( + KSTypeReferenceSyntheticImpl.getCached(resultingType, null), Variance.INVARIANT + ) + ) + ) + } + resultingType + } + } else classValue.classId.findKSType() + is KClassValue.Value.LocalClass -> getKSTypeCached(classValue.type) + } + is ErrorValue, is NullValue -> null + else -> value +} + +fun AnnotationDescriptor.createKSValueArguments(ownerAnnotation: KSAnnotation): List<KSValueArgument> { + val presentValueArguments = allValueArguments.map { (name, constantValue) -> + KSValueArgumentLiteImpl.getCached( + KSNameImpl.getCached(name.asString()), + constantValue.toValue(ownerAnnotation), + ownerAnnotation + ) + } + val presentValueArgumentNames = presentValueArguments.map { it.name.asString() } + val argumentsFromDefault = this.type.getDefaultConstructorArguments(presentValueArgumentNames, ownerAnnotation) + return presentValueArguments.plus(argumentsFromDefault) +} + +internal fun AnnotationDescriptor.getDefaultArguments(ownerAnnotation: KSAnnotation): List<KSValueArgument> { + return this.type.getDefaultConstructorArguments(emptyList(), ownerAnnotation) +} + +internal fun TypeConstructor.toDeclarationDescriptor(): ClassDescriptor? { + if (this.declarationDescriptor !is NotFoundClasses.MockClassDescriptor) { + return this.declarationDescriptor as? ClassDescriptor + } + val fqName = (this.declarationDescriptor as? ClassDescriptor)?.fqNameSafe ?: return null + val shortNames = fqName.shortName().asString().split("$") + var parent = ResolverImpl.instance!! + .getClassDeclarationByName("${fqName.parent().asString()}.${shortNames.first()}") + for (i in 1 until shortNames.size) { + if (parent == null) { + return null + } + parent = parent.declarations + .filterIsInstance<KSClassDeclaration>() + .singleOrNull { it.simpleName.asString() == shortNames[i] } + } + return parent?.let { ResolverImpl.instance!!.resolveClassDeclaration(it) } +} + +internal fun KotlinType.getDefaultConstructorArguments( + excludeNames: List<String>, + ownerAnnotation: KSAnnotation +): List<KSValueArgument> { + return this.constructor.toDeclarationDescriptor()?.constructors?.single() + ?.getAbsentDefaultArguments(excludeNames, ownerAnnotation) ?: emptyList() +} + +fun ClassConstructorDescriptor.getAbsentDefaultArguments( + excludeNames: List<String>, + ownerAnnotation: KSAnnotation +): List<KSValueArgument> { + return this.valueParameters + .filterNot { param -> excludeNames.contains(param.name.asString()) || !param.hasDefaultValue() } + .map { param -> + KSValueArgumentLiteImpl.getCached( + KSNameImpl.getCached(param.name.asString()), + param.getDefaultValue(ownerAnnotation), + ownerAnnotation, + Origin.SYNTHETIC + ) + } +} + +fun ValueParameterDescriptor.getDefaultValue(ownerAnnotation: KSAnnotation): Any? { + + // Copied from kotlin compiler + // TODO: expose in upstream + fun convertTypeToKClassValue(javaType: JavaType): KClassValue? { + var type = javaType + var arrayDimensions = 0 + while (type is JavaArrayType) { + type = type.componentType + arrayDimensions++ + } + return when (type) { + is JavaPrimitiveType -> { + val primitiveType = type.type + // void.class is not representable in Kotlin, we approximate it by Unit::class + ?: return KClassValue(ClassId.topLevel(StandardNames.FqNames.unit.toSafe()), 0) + if (arrayDimensions > 0) { + KClassValue(ClassId.topLevel(primitiveType.arrayTypeFqName), arrayDimensions - 1) + } else { + KClassValue(ClassId.topLevel(primitiveType.typeFqName), arrayDimensions) + } + } + is JavaClassifierType -> { + val fqName = FqName(type.classifierQualifiedName) + // TODO: support nested classes somehow + val classId = JavaToKotlinClassMap.mapJavaToKotlin(fqName) ?: ClassId.topLevel(fqName) + KClassValue(classId, arrayDimensions) + } + else -> null + } + } + + // Copied from kotlin compiler + // TODO: expose in upstream + fun JavaAnnotationArgument.convert(expectedType: KotlinType): ConstantValue<*>? { + return when (this) { + is JavaLiteralAnnotationArgument -> value?.let { + when (value) { + // Note: `value` expression may be of class that does not match field type in some cases + // tested for Int, left other checks just in case + is Byte, is Short, is Int, is Long -> { + ConstantValueFactory.createIntegerConstantValue((value as Number).toLong(), expectedType, false) + } + else -> { + ConstantValueFactory.createConstantValue(value) + } + } + } + is JavaEnumValueAnnotationArgument -> { + enumClassId?.let { enumClassId -> + entryName?.let { entryName -> + EnumValue(enumClassId, entryName) + } + } + } + is JavaArrayAnnotationArgument -> { + val elementType = expectedType.builtIns.getArrayElementType(expectedType) + ConstantValueFactory.createArrayValue( + getElements().mapNotNull { it.convert(elementType) }, + expectedType + ) + } + is JavaAnnotationAsAnnotationArgument -> { + AnnotationValue( + LazyJavaAnnotationDescriptor(ResolverImpl.instance!!.lazyJavaResolverContext, this.getAnnotation()) + ) + } + is JavaClassObjectAnnotationArgument -> { + convertTypeToKClassValue(getReferencedType()) + } + else -> null + } + } + + val psi = this.findPsi() + return when (psi) { + null -> { + val file = if (this.source is JavaSourceElement) { + ( + ((this.source as JavaSourceElement).javaElement as? BinaryJavaMethod) + ?.containingClass as? VirtualFileBoundJavaClass + )?.virtualFile?.contentsToByteArray() + } else { + (this.containingDeclaration.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass) + ?.file?.contentsToByteArray() + } + if (file == null) { + null + } else { + var defaultValue: JavaAnnotationArgument? = null + ClassReader(file).accept( + object : ClassVisitor(API_VERSION) { + override fun visitMethod( + access: Int, + name: String?, + desc: String?, + signature: String?, + exceptions: Array<out String>? + ): MethodVisitor { + return if (name == this@getDefaultValue.name.asString()) { + object : MethodVisitor(API_VERSION) { + override fun visitAnnotationDefault(): AnnotationVisitor = + BinaryJavaAnnotationVisitor( + ClassifierResolutionContext { null }, + BinaryClassSignatureParser() + ) { + defaultValue = it + } + } + } else { + object : MethodVisitor(API_VERSION) {} + } + } + }, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES + ) + if (!this.type.isError) { + defaultValue?.convert(this.type)?.toValue(ownerAnnotation) + } else { + KSErrorType + } + } + } + is KtParameter -> if (!this.type.isError) { + ResolverImpl.instance!!.evaluateConstant(psi.defaultValue, this.type)?.toValue(ownerAnnotation) + } else { + KSErrorType + } + is PsiAnnotationMethod -> { + when (psi.defaultValue) { + is PsiAnnotation -> KSAnnotationJavaImpl.getCached(psi.defaultValue as PsiAnnotation) + else -> JavaPsiFacade.getInstance(psi.project).constantEvaluationHelper + .computeConstantExpression((psi).defaultValue) + } + } + else -> throw IllegalStateException("Unexpected psi ${psi.javaClass}, $ExceptionMessage") + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt new file mode 100644 index 00000000..94f788fb --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt @@ -0,0 +1,203 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.* +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.java.KSFunctionDeclarationJavaImpl +import com.google.devtools.ksp.symbol.impl.java.KSPropertyDeclarationJavaImpl +import com.google.devtools.ksp.symbol.impl.kotlin.* +import com.google.devtools.ksp.symbol.impl.replaceTypeArguments +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.calls.tower.isSynthesized +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections +import org.jetbrains.kotlin.descriptors.ClassKind as KtClassKind + +class KSClassDeclarationDescriptorImpl private constructor(val descriptor: ClassDescriptor) : + KSClassDeclaration, + KSDeclarationDescriptorImpl(descriptor), + KSExpectActual by KSExpectActualDescriptorImpl(descriptor) { + companion object : KSObjectCache<ClassDescriptor, KSClassDeclarationDescriptorImpl>() { + fun getCached(descriptor: ClassDescriptor) = cache.getOrPut(descriptor) { + KSClassDeclarationDescriptorImpl(descriptor) + } + } + + override val classKind: ClassKind by lazy { + when (descriptor.kind) { + KtClassKind.INTERFACE -> ClassKind.INTERFACE + KtClassKind.CLASS -> ClassKind.CLASS + KtClassKind.OBJECT -> ClassKind.OBJECT + KtClassKind.ENUM_CLASS -> ClassKind.ENUM_CLASS + KtClassKind.ENUM_ENTRY -> ClassKind.ENUM_ENTRY + KtClassKind.ANNOTATION_CLASS -> ClassKind.ANNOTATION_CLASS + } + } + + override val isCompanionObject by lazy { + descriptor.isCompanionObject + } + + override fun getSealedSubclasses(): Sequence<KSClassDeclaration> { + return descriptor.sealedSubclassesSequence() + } + + override fun getAllFunctions(): Sequence<KSFunctionDeclaration> = descriptor.getAllFunctions() + + override fun getAllProperties(): Sequence<KSPropertyDeclaration> = descriptor.getAllProperties() + + override val primaryConstructor: KSFunctionDeclaration? by lazy { + descriptor.unsubstitutedPrimaryConstructor?.let { KSFunctionDeclarationDescriptorImpl.getCached(it) } + } + + // Workaround for https://github.com/google/ksp/issues/195 + private val mockSerializableType = ResolverImpl.instance!!.mockSerializableType + private val javaSerializableType = ResolverImpl.instance!!.javaSerializableType + + override val superTypes: Sequence<KSTypeReference> by lazy { + + descriptor.defaultType.constructor.supertypes.asSequence().map { kotlinType -> + KSTypeReferenceDescriptorImpl.getCached( + javaSerializableType?.let { if (kotlinType === mockSerializableType) it else kotlinType } + ?: kotlinType, + origin, + this + ) + }.memoized() + } + + override val typeParameters: List<KSTypeParameter> by lazy { + descriptor.declaredTypeParameters.map { KSTypeParameterDescriptorImpl.getCached(it) } + } + + override val declarations: Sequence<KSDeclaration> by lazy { + sequenceOf( + descriptor.unsubstitutedMemberScope.getDescriptorsFiltered(), + // FIXME: Support static, synthetic `entries` for enums when the language feature is enabled. + descriptor.staticScope.getDescriptorsFiltered().filterNot { + descriptor.kind == KtClassKind.ENUM_CLASS && + it is CallableDescriptor && + it.isSynthesized && + it.name == StandardNames.ENUM_ENTRIES + }, + descriptor.constructors + ).flatten() + .filter { + it is MemberDescriptor && + it.visibility != DescriptorVisibilities.INHERITED && + it.visibility != DescriptorVisibilities.INVISIBLE_FAKE && + (it !is CallableMemberDescriptor || it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) + } + .map { + when (it) { + is PropertyDescriptor -> KSPropertyDeclarationDescriptorImpl.getCached(it) + is FunctionDescriptor -> KSFunctionDeclarationDescriptorImpl.getCached(it) + is ClassDescriptor -> getCached(it) + else -> throw IllegalStateException("Unexpected descriptor type ${it.javaClass}, $ExceptionMessage") + } + }.memoized() + } + + override val modifiers: Set<Modifier> by lazy { + val modifiers = mutableSetOf<Modifier>() + modifiers.addAll(descriptor.toKSModifiers()) + if (descriptor.isData) { + modifiers.add(Modifier.DATA) + } + if (descriptor.isInline) { + modifiers.add(Modifier.INLINE) + } + if (descriptor.kind == KtClassKind.ANNOTATION_CLASS) { + modifiers.add(Modifier.ANNOTATION) + } + if (descriptor.isInner) { + modifiers.add(Modifier.INNER) + } + if (descriptor.isFun) { + modifiers.add(Modifier.FUN) + } + if (descriptor.isValue) { + modifiers.add(Modifier.VALUE) + } + modifiers + } + + override fun asType(typeArguments: List<KSTypeArgument>): KSType = + descriptor.defaultType.replaceTypeArguments(typeArguments)?.let { + getKSTypeCached(it, typeArguments) + } ?: KSErrorType + + override fun asStarProjectedType(): KSType { + return getKSTypeCached(descriptor.defaultType.replaceArgumentsWithStarProjections()) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitClassDeclaration(this, data) + } +} + +internal fun ClassDescriptor.getAllFunctions(): Sequence<KSFunctionDeclaration> { + ResolverImpl.instance!!.incrementalContext.recordLookupForGetAllFunctions(this) + val functionDescriptors = unsubstitutedMemberScope.getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) + .asSequence() + .filter { (it as FunctionDescriptor).visibility != DescriptorVisibilities.INVISIBLE_FAKE } + return functionDescriptors.plus(constructors).map { + when (val psi = it.findPsi()) { + is KtFunction -> KSFunctionDeclarationImpl.getCached(psi) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(psi) + else -> KSFunctionDeclarationDescriptorImpl.getCached(it as FunctionDescriptor) + } + } +} + +internal fun ClassDescriptor.getAllProperties(): Sequence<KSPropertyDeclaration> { + ResolverImpl.instance!!.incrementalContext.recordLookupForGetAllProperties(this) + return unsubstitutedMemberScope.getDescriptorsFiltered(DescriptorKindFilter.VARIABLES).asSequence() + .filter { (it as PropertyDescriptor).visibility != DescriptorVisibilities.INVISIBLE_FAKE } + .map { + when (val psi = it.findPsi()) { + is KtParameter -> KSPropertyDeclarationParameterImpl.getCached(psi) + is KtProperty -> KSPropertyDeclarationImpl.getCached(psi) + is PsiField -> KSPropertyDeclarationJavaImpl.getCached(psi) + else -> KSPropertyDeclarationDescriptorImpl.getCached(it as PropertyDescriptor) + } + } +} + +internal fun ClassDescriptor.sealedSubclassesSequence(): Sequence<KSClassDeclaration> { + // TODO record incremental subclass lookups in Kotlin 1.5.x? + return sealedSubclasses + .asSequence() + .map { sealedSubClass -> + when (val psi = sealedSubClass.findPsi()) { + is KtClassOrObject -> KSClassDeclarationImpl.getCached(psi) + else -> KSClassDeclarationDescriptorImpl.getCached(sealedSubClass) + } + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassifierReferenceDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassifierReferenceDescriptorImpl.kt new file mode 100644 index 00000000..96e086ee --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassifierReferenceDescriptorImpl.kt @@ -0,0 +1,86 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection + +class KSClassifierReferenceDescriptorImpl private constructor( + val descriptor: ClassifierDescriptor, + val arguments: List<TypeProjection>, + override val origin: Origin, + override val parent: KSNode? +) : + KSClassifierReference { + companion object : KSObjectCache<Triple<ClassifierDescriptor, List<TypeProjection>, Pair<KSNode?, Origin>>, + KSClassifierReferenceDescriptorImpl>() { + fun getCached(kotlinType: KotlinType, origin: Origin, parent: KSNode?) = cache.getOrPut( + Triple( + kotlinType.constructor.declarationDescriptor!!, + kotlinType.arguments, + Pair(parent, origin) + ) + ) { + KSClassifierReferenceDescriptorImpl( + kotlinType.constructor.declarationDescriptor!!, kotlinType.arguments, origin, parent + ) + } + + fun getCached( + descriptor: ClassifierDescriptor, + arguments: List<TypeProjection>, + origin: Origin, + parent: KSNode? + ) = cache.getOrPut( + Triple(descriptor, arguments, Pair(parent, origin)) + ) { KSClassifierReferenceDescriptorImpl(descriptor, arguments, origin, parent) } + } + + private val nDeclaredArgs by lazy { + (descriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters?.size ?: 0 + } + + override val location: Location = NonExistLocation + + override val qualifier: KSClassifierReference? by lazy { + val outerDescriptor = descriptor.containingDeclaration as? ClassifierDescriptor ?: return@lazy null + val outerArguments = arguments.drop(nDeclaredArgs) + getCached(outerDescriptor, outerArguments, origin, parent) + } + + override val typeArguments: List<KSTypeArgument> by lazy { + arguments.map { KSTypeArgumentDescriptorImpl.getCached(it, origin, this.parent) } + } + + override fun referencedName(): String { + val declaredArgs = if (arguments.isEmpty() || nDeclaredArgs == 0) + emptyList() + else + arguments.subList(0, nDeclaredArgs) + return descriptor.name.asString() + if (declaredArgs.isNotEmpty()) "<${ + declaredArgs.map { it.toString() } + .joinToString(", ") + }>" else "" + } + + override fun toString() = referencedName() +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSDeclarationDescriptorImpl.kt new file mode 100644 index 00000000..db041b26 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSDeclarationDescriptorImpl.kt @@ -0,0 +1,84 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.backend.common.serialization.findPackage +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.parents +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull + +abstract class KSDeclarationDescriptorImpl(private val descriptor: DeclarationDescriptor) : KSDeclaration { + + override val origin by lazy { + descriptor.origin + } + + override val containingFile: KSFile? = null + + override val location: Location = NonExistLocation + + override val annotations: Sequence<KSAnnotation> by lazy { + descriptor.annotations.asSequence().map { KSAnnotationDescriptorImpl.getCached(it, this) }.memoized() + } + + override val parentDeclaration: KSDeclaration? by lazy { + val containingDescriptor = descriptor.parents.first() + when (containingDescriptor) { + is ClassDescriptor -> KSClassDeclarationDescriptorImpl.getCached(containingDescriptor) + is FunctionDescriptor -> KSFunctionDeclarationDescriptorImpl.getCached(containingDescriptor) + else -> null + } as KSDeclaration? + } + + override val parent: KSNode? by lazy { + parentDeclaration + } + + override val packageName: KSName by lazy { + KSNameImpl.getCached(descriptor.findPackage().fqName.asString()) + } + + override val qualifiedName: KSName by lazy { + KSNameImpl.getCached(descriptor.fqNameSafe.asString()) + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(descriptor.name.asString()) + } + + override fun toString(): String { + return this.simpleName.asString() + } + + override val docString = null +} + +val DeclarationDescriptor.origin: Origin + get() = if (parentsWithSelf.firstIsInstanceOrNull<JavaClassDescriptor>() != null) { + Origin.JAVA_LIB + } else { + Origin.KOTLIN_LIB + } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSExpectActualDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSExpectActualDescriptorImpl.kt new file mode 100644 index 00000000..7e527d77 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSExpectActualDescriptorImpl.kt @@ -0,0 +1,50 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.processing.impl.findActualsInKSDeclaration +import com.google.devtools.ksp.processing.impl.findExpectsInKSDeclaration +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSExpectActual +import org.jetbrains.kotlin.descriptors.MemberDescriptor + +class KSExpectActualDescriptorImpl(val descriptor: MemberDescriptor) : KSExpectActual { + override val isExpect: Boolean = descriptor.isExpect + + override val isActual: Boolean = descriptor.isActual + + private val expects: Sequence<KSDeclaration> by lazy { + descriptor.findExpectsInKSDeclaration() + } + + override fun findExpects(): Sequence<KSDeclaration> { + if (!isActual) + return emptySequence() + return expects + } + + private val actuals: Sequence<KSDeclaration> by lazy { + descriptor.findActualsInKSDeclaration() + } + + override fun findActuals(): Sequence<KSDeclaration> { + if (!isExpect) + return emptySequence() + return actuals + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt new file mode 100644 index 00000000..0749a095 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt @@ -0,0 +1,114 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.toFunctionKSModifiers +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor +import org.jetbrains.kotlin.load.java.isFromJava +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.org.objectweb.asm.Opcodes + +class KSFunctionDeclarationDescriptorImpl private constructor(val descriptor: FunctionDescriptor) : + KSFunctionDeclaration, + KSDeclarationDescriptorImpl(descriptor), + KSExpectActual by KSExpectActualDescriptorImpl(descriptor) { + companion object : KSObjectCache<FunctionDescriptor, KSFunctionDeclarationDescriptorImpl>() { + fun getCached(descriptor: FunctionDescriptor) = + cache.getOrPut(descriptor) { KSFunctionDeclarationDescriptorImpl(descriptor) } + } + + override fun findOverridee(): KSDeclaration? { + return descriptor?.findClosestOverridee()?.toKSDeclaration() + } + + override val typeParameters: List<KSTypeParameter> by lazy { + descriptor.typeParameters.map { KSTypeParameterDescriptorImpl.getCached(it) } + } + + override val declarations: Sequence<KSDeclaration> = emptySequence() + + override val extensionReceiver: KSTypeReference? by lazy { + val extensionReceiver = descriptor.extensionReceiverParameter?.type + if (extensionReceiver != null) { + KSTypeReferenceDescriptorImpl.getCached(extensionReceiver, origin, this) + } else { + null + } + } + + override val functionKind: FunctionKind by lazy { + + when { + descriptor.dispatchReceiverParameter == null -> when { + descriptor.isFromJava -> FunctionKind.STATIC + else -> FunctionKind.TOP_LEVEL + } + !descriptor.name.isSpecial && !descriptor.name.asString().isEmpty() -> FunctionKind.MEMBER + descriptor is AnonymousFunctionDescriptor -> FunctionKind.ANONYMOUS + else -> throw IllegalStateException( + "Unable to resolve FunctionKind for ${descriptor.fqNameSafe}, $ExceptionMessage" + ) + } + } + + override val isAbstract: Boolean by lazy { + this.modifiers.contains(Modifier.ABSTRACT) + } + + override val modifiers: Set<Modifier> by lazy { + val modifiers = mutableSetOf<Modifier>() + modifiers.addAll(descriptor.toKSModifiers()) + modifiers.addAll(descriptor.toFunctionKSModifiers()) + + if (this.origin == Origin.JAVA_LIB) { + if (this.jvmAccessFlag and Opcodes.ACC_STRICT != 0) + modifiers.add(Modifier.JAVA_STRICT) + if (this.jvmAccessFlag and Opcodes.ACC_SYNCHRONIZED != 0) + modifiers.add(Modifier.JAVA_SYNCHRONIZED) + } + + modifiers + } + + override val parameters: List<KSValueParameter> by lazy { + descriptor.valueParameters.map { KSValueParameterDescriptorImpl.getCached(it, this) } + } + + override val returnType: KSTypeReference? by lazy { + val returnType = descriptor.returnType + if (returnType == null) { + null + } else { + KSTypeReferenceDescriptorImpl.getCached(returnType, origin, this) + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFunctionDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSFunction = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSNodeDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSNodeDescriptorImpl.kt new file mode 100644 index 00000000..0c82f750 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSNodeDescriptorImpl.kt @@ -0,0 +1,9 @@ +package com.google.devtools.ksp.symbol.impl.binary + +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation + +abstract class KSNodeDescriptorImpl(override val parent: KSNode?) : KSNode { + override val location: Location = NonExistLocation +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyAccessorDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyAccessorDescriptorImpl.kt new file mode 100644 index 00000000..505bb569 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyAccessorDescriptorImpl.kt @@ -0,0 +1,62 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toKSPropertyDeclaration +import com.google.devtools.ksp.toFunctionKSModifiers +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor + +abstract class KSPropertyAccessorDescriptorImpl(val descriptor: PropertyAccessorDescriptor) : KSPropertyAccessor { + override val origin: Origin by lazy { + when (receiver.origin) { + // if receiver is kotlin source, that means we are a synthetic where developer + // didn't declare an explicit accessor so we used the descriptor instead + Origin.KOTLIN -> Origin.SYNTHETIC + else -> descriptor.origin + } + } + + override val receiver: KSPropertyDeclaration by lazy { + descriptor.correspondingProperty.toKSPropertyDeclaration() + } + + override val parent: KSNode? by lazy { + receiver + } + + override val location: Location + get() { + // if receiver is kotlin source, that means `this` is synthetic hence we want the property's location + // Otherwise, receiver is also from a .class file where the location will be NoLocation + return receiver.location + } + + override val annotations: Sequence<KSAnnotation> by lazy { + descriptor.annotations.asSequence().map { KSAnnotationDescriptorImpl.getCached(it, this) }.memoized() + } + + override val modifiers: Set<Modifier> by lazy { + val modifiers = mutableSetOf<Modifier>() + modifiers.addAll(descriptor.toKSModifiers()) + modifiers.addAll(descriptor.toFunctionKSModifiers()) + modifiers + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyDeclarationDescriptorImpl.kt new file mode 100644 index 00000000..ad9e52f3 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyDeclarationDescriptorImpl.kt @@ -0,0 +1,124 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.hasBackingFieldWithBinaryClassSupport +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.org.objectweb.asm.Opcodes + +class KSPropertyDeclarationDescriptorImpl private constructor(val descriptor: PropertyDescriptor) : + KSPropertyDeclaration, + KSDeclarationDescriptorImpl(descriptor), + KSExpectActual by KSExpectActualDescriptorImpl(descriptor) { + companion object : KSObjectCache<PropertyDescriptor, KSPropertyDeclarationDescriptorImpl>() { + fun getCached(descriptor: PropertyDescriptor) = cache.getOrPut(descriptor) { + KSPropertyDeclarationDescriptorImpl(descriptor) + } + } + + override val extensionReceiver: KSTypeReference? by lazy { + if (descriptor.extensionReceiverParameter != null) { + KSTypeReferenceDescriptorImpl.getCached(descriptor.extensionReceiverParameter!!.type, origin, this) + } else { + null + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + // annotations on backing field will not visible in the property declaration so we query it directly to load + // its annotations as well. + val backingFieldAnnotations = descriptor.backingField?.annotations?.map { + KSAnnotationDescriptorImpl.getCached(it, this) + }.orEmpty() + (super.annotations + backingFieldAnnotations).memoized() + } + + override val isMutable: Boolean by lazy { + descriptor.isVar + } + + override val modifiers: Set<Modifier> by lazy { + val modifiers = mutableSetOf<Modifier>() + modifiers.addAll(descriptor.toKSModifiers()) + if (descriptor.isConst) { + modifiers.add(Modifier.CONST) + } + if (descriptor.isLateInit) { + modifiers.add(Modifier.LATEINIT) + } + + if (this.origin == Origin.JAVA_LIB) { + if (this.jvmAccessFlag and Opcodes.ACC_TRANSIENT != 0) + modifiers.add(Modifier.JAVA_TRANSIENT) + if (this.jvmAccessFlag and Opcodes.ACC_VOLATILE != 0) + modifiers.add(Modifier.JAVA_VOLATILE) + } + + modifiers + } + + override val setter: KSPropertySetter? by lazy { + if (descriptor.setter != null) { + KSPropertySetterDescriptorImpl.getCached(descriptor.setter as PropertySetterDescriptor) + } else { + null + } + } + + override val getter: KSPropertyGetter? by lazy { + if (descriptor.getter != null) { + KSPropertyGetterDescriptorImpl.getCached(descriptor.getter as PropertyGetterDescriptor) + } else { + null + } + } + + override val typeParameters: List<KSTypeParameter> by lazy { + descriptor.typeParameters.map { KSTypeParameterDescriptorImpl.getCached(it) } + } + + override val type: KSTypeReference by lazy { + KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, this) + } + + override val hasBackingField: Boolean by lazy { + descriptor.hasBackingFieldWithBinaryClassSupport() + } + + override fun findOverridee(): KSPropertyDeclaration? { + val propertyDescriptor = ResolverImpl.instance!!.resolvePropertyDeclaration(this) + return propertyDescriptor?.findClosestOverridee()?.toKSPropertyDeclaration() + } + + override fun isDelegated(): Boolean { + return (descriptor as? PropertyDescriptor)?.delegateField != null + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSType = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyGetterDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyGetterDescriptorImpl.kt new file mode 100644 index 00000000..a0951aad --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertyGetterDescriptorImpl.kt @@ -0,0 +1,47 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor + +class KSPropertyGetterDescriptorImpl private constructor(descriptor: PropertyGetterDescriptor) : + KSPropertyAccessorDescriptorImpl(descriptor), KSPropertyGetter { + companion object : KSObjectCache<PropertyGetterDescriptor, KSPropertyGetterDescriptorImpl>() { + fun getCached(descriptor: PropertyGetterDescriptor) = cache.getOrPut(descriptor) { + KSPropertyGetterDescriptorImpl(descriptor) + } + } + + override val returnType: KSTypeReference? by lazy { + if (descriptor.returnType != null) { + KSTypeReferenceDescriptorImpl.getCached(descriptor.returnType!!, origin, this) + } else { + null + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyGetter(this, data) + } + + override fun toString(): String { + return "$receiver.getter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertySetterDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertySetterDescriptorImpl.kt new file mode 100644 index 00000000..834089b0 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSPropertySetterDescriptorImpl.kt @@ -0,0 +1,44 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor + +class KSPropertySetterDescriptorImpl private constructor(descriptor: PropertySetterDescriptor) : + KSPropertyAccessorDescriptorImpl(descriptor), KSPropertySetter { + companion object : KSObjectCache<PropertySetterDescriptor, KSPropertySetterDescriptorImpl>() { + fun getCached(descriptor: PropertySetterDescriptor) = cache.getOrPut(descriptor) { + KSPropertySetterDescriptorImpl(descriptor) + } + } + + override val parameter: KSValueParameter by lazy { + descriptor.valueParameters.singleOrNull()?.let { KSValueParameterDescriptorImpl.getCached(it, this) } + ?: throw IllegalStateException("Failed to resolve property type") + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertySetter(this, data) + } + + override fun toString(): String { + return "$receiver.setter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeAliasDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeAliasDescriptorImpl.kt new file mode 100644 index 00000000..29dc0613 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeAliasDescriptorImpl.kt @@ -0,0 +1,38 @@ +package com.google.devtools.ksp.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor + +class KSTypeAliasDescriptorImpl(descriptor: TypeAliasDescriptor) : + KSTypeAlias, + KSDeclarationDescriptorImpl(descriptor), + KSExpectActual by KSExpectActualDescriptorImpl(descriptor) { + companion object : KSObjectCache<TypeAliasDescriptor, KSTypeAliasDescriptorImpl>() { + fun getCached(descriptor: TypeAliasDescriptor) = KSTypeAliasDescriptorImpl.cache.getOrPut(descriptor) { + KSTypeAliasDescriptorImpl(descriptor) + } + } + + override val name: KSName by lazy { + KSNameImpl.getCached(descriptor.name.asString()) + } + + override val modifiers: Set<Modifier> by lazy { + descriptor.toKSModifiers() + } + + override val typeParameters: List<KSTypeParameter> by lazy { + descriptor.declaredTypeParameters.map { KSTypeParameterDescriptorImpl.getCached(it) } + } + + override val type: KSTypeReference by lazy { + KSTypeReferenceDescriptorImpl.getCached(descriptor.underlyingType, origin, this) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeAlias(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeArgumentDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeArgumentDescriptorImpl.kt new file mode 100644 index 00000000..5b76e27a --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeArgumentDescriptorImpl.kt @@ -0,0 +1,65 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.IdKeyTriple +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.Variance +import com.google.devtools.ksp.symbol.impl.kotlin.KSTypeArgumentImpl +import org.jetbrains.kotlin.types.TypeProjection + +class KSTypeArgumentDescriptorImpl private constructor( + val descriptor: TypeProjection, + override val origin: Origin, + override val parent: KSNode? +) : KSTypeArgumentImpl() { + companion object : KSObjectCache<IdKeyTriple<TypeProjection, Origin, KSNode?>, KSTypeArgumentDescriptorImpl>() { + fun getCached(descriptor: TypeProjection, origin: Origin, parent: KSNode?) = cache + .getOrPut(IdKeyTriple(descriptor, origin, parent)) { + KSTypeArgumentDescriptorImpl(descriptor, origin, parent) + } + } + + override val location: Location = NonExistLocation + + override val type: KSTypeReference by lazy { + KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, if (parent != null) this else null) + } + + override val variance: Variance by lazy { + if (descriptor.isStarProjection) + Variance.STAR + else { + when (descriptor.projectionKind) { + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Variance.CONTRAVARIANT + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Variance.COVARIANT + else -> Variance.INVARIANT + } + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + descriptor.type.annotations.asSequence().map { KSAnnotationDescriptorImpl.getCached(it, this) } + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeParameterDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeParameterDescriptorImpl.kt new file mode 100644 index 00000000..98de3b98 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeParameterDescriptorImpl.kt @@ -0,0 +1,74 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.toKSVariance +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe + +class KSTypeParameterDescriptorImpl private constructor(val descriptor: TypeParameterDescriptor) : + KSTypeParameter, + KSDeclarationDescriptorImpl(descriptor), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<TypeParameterDescriptor, KSTypeParameterDescriptorImpl>() { + fun getCached(descriptor: TypeParameterDescriptor) = cache.getOrPut(descriptor) { + KSTypeParameterDescriptorImpl(descriptor) + } + } + + override val bounds: Sequence<KSTypeReference> by lazy { + descriptor.upperBounds.asSequence().map { KSTypeReferenceDescriptorImpl.getCached(it, origin, this) } + } + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override val parentDeclaration: KSDeclaration? by lazy { + when (val parent = descriptor.containingDeclaration) { + is ClassDescriptor -> KSClassDeclarationDescriptorImpl.getCached(parent) + is FunctionDescriptor -> KSFunctionDeclarationDescriptorImpl.getCached(parent) + is PropertyDescriptor -> KSPropertyDeclarationDescriptorImpl.getCached(parent) + else -> throw IllegalStateException( + "Unexpected containing declaration for ${descriptor.fqNameSafe}, $ExceptionMessage" + ) + } + } + + override val modifiers: Set<Modifier> = emptySet() + + override val isReified: Boolean by lazy { + descriptor.isReified + } + + override val name: KSName by lazy { + KSNameImpl.getCached(descriptor.name.asString()) + } + + override val variance: Variance = descriptor.variance.toKSVariance() + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeParameter(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeReferenceDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeReferenceDescriptorImpl.kt new file mode 100644 index 00000000..91f68fa4 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSTypeReferenceDescriptorImpl.kt @@ -0,0 +1,82 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.IdKeyTriple +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.kotlin.getKSTypeCached +import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype +import org.jetbrains.kotlin.types.AbbreviatedType +import org.jetbrains.kotlin.types.KotlinType + +class KSTypeReferenceDescriptorImpl private constructor( + val kotlinType: KotlinType, + override val origin: Origin, + override val parent: KSNode? +) : KSTypeReference { + companion object : KSObjectCache<IdKeyTriple<KotlinType, Origin, KSNode?>, KSTypeReferenceDescriptorImpl>() { + fun getCached(kotlinType: KotlinType, origin: Origin, parent: KSNode?) = cache + .getOrPut(IdKeyTriple(kotlinType, origin, parent)) { + KSTypeReferenceDescriptorImpl(kotlinType, origin, parent) + } + } + + private fun KotlinType.findAlias(): KotlinType = + (kotlinType as? AbbreviatedType)?.abbreviation ?: this + + override val location: Location = NonExistLocation + + override val element: KSReferenceElement by lazy { + KSClassifierReferenceDescriptorImpl.getCached(kotlinType.findAlias(), origin, this) + } + + override val annotations: Sequence<KSAnnotation> by lazy { + kotlinType.annotations.asSequence().map { KSAnnotationDescriptorImpl.getCached(it, this) }.memoized() + } + + override val modifiers: Set<Modifier> by lazy { + if (kotlinType.isSuspendFunctionTypeOrSubtype) { + setOf(Modifier.SUSPEND) + } else { + emptySet<Modifier>() + } + } + + override fun resolve(): KSType { + return getKSTypeCached(kotlinType) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun toString(): String { + return element.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSValueParameterDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSValueParameterDescriptorImpl.kt new file mode 100644 index 00000000..29f53a45 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSValueParameterDescriptorImpl.kt @@ -0,0 +1,78 @@ +/* + * 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.symbol.impl.binary + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue +import org.jetbrains.kotlin.resolve.calls.components.isVararg + +class KSValueParameterDescriptorImpl private constructor( + val descriptor: ValueParameterDescriptor, + override val parent: KSNode? +) : KSValueParameter { + companion object : KSObjectCache<Pair<ValueParameterDescriptor, KSNode?>, KSValueParameterDescriptorImpl>() { + fun getCached(descriptor: ValueParameterDescriptor, parent: KSNode?) = cache + .getOrPut(Pair(descriptor, parent)) { KSValueParameterDescriptorImpl(descriptor, parent) } + } + + override val origin by lazy { + descriptor.origin + } + + override val location: Location = NonExistLocation + + override val annotations: Sequence<KSAnnotation> by lazy { + descriptor.annotations.asSequence().map { KSAnnotationDescriptorImpl.getCached(it, this) } + } + + override val isCrossInline: Boolean = descriptor.isCrossinline + + override val isNoInline: Boolean = descriptor.isNoinline + + override val isVararg: Boolean = descriptor.isVararg + + override val isVal: Boolean = !descriptor.isVar + + override val isVar: Boolean = descriptor.isVar + + override val name: KSName? by lazy { + KSNameImpl.getCached(descriptor.name.asString()) + } + + override val type: KSTypeReference by lazy { + // Descriptor wraps vararg with Array<>, to align with the actual behavior in source. + if (isVararg) { + KSTypeReferenceDescriptorImpl.getCached(descriptor.varargElementType!!, origin, this) + } else { + KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, this) + } + } + + override val hasDefault: Boolean = descriptor.hasDefaultValue() + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitValueParameter(this, data) + } + + override fun toString(): String { + return name?.asString() ?: "_" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSAnnotationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSAnnotationJavaImpl.kt new file mode 100644 index 00000000..f6ae397b --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSAnnotationJavaImpl.kt @@ -0,0 +1,160 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.binary.getAbsentDefaultArguments +import com.google.devtools.ksp.symbol.impl.binary.getDefaultConstructorArguments +import com.google.devtools.ksp.symbol.impl.kotlin.KSTypeImpl +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.lang.jvm.JvmClassKind +import com.intellij.psi.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor + +class KSAnnotationJavaImpl private constructor(val psi: PsiAnnotation) : KSAnnotation { + companion object : KSObjectCache<PsiAnnotation, KSAnnotationJavaImpl>() { + fun getCached(psi: PsiAnnotation) = cache.getOrPut(psi) { KSAnnotationJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val parent: KSNode? by lazy { + var parentPsi = psi.parent + while (true) { + when (parentPsi) { + null, is PsiJavaFile, is PsiClass, is PsiMethod, is PsiParameter, is PsiTypeParameter, is PsiType -> + break + else -> parentPsi = parentPsi.parent + } + } + when (parentPsi) { + is PsiJavaFile -> KSFileJavaImpl.getCached(parentPsi) + is PsiClass -> KSClassDeclarationJavaImpl.getCached(parentPsi) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(parentPsi) + is PsiParameter -> KSValueParameterJavaImpl.getCached(parentPsi) + is PsiTypeParameter -> KSTypeParameterJavaImpl.getCached(parentPsi) + is PsiType -> + if (parentPsi.parent is PsiClassType) KSTypeArgumentJavaImpl.getCached(parentPsi, this) + else KSTypeReferenceJavaImpl.getCached(parentPsi, this) + else -> null + } + } + + override val annotationType: KSTypeReference by lazy { + KSTypeReferenceLiteJavaImpl.getCached(psi, this) + } + + override val arguments: List<KSValueArgument> by lazy { + val annotationConstructor = ( + (annotationType.resolve() as? KSTypeImpl)?.kotlinType?.constructor + ?.declarationDescriptor as? ClassDescriptor + )?.constructors?.single() + val presentValueArguments = psi.parameterList.attributes + .mapIndexed { index, it -> + // use the name in the attribute if it is explicitly specified, otherwise, fall back to index. + val name = it.name ?: annotationConstructor?.valueParameters?.getOrNull(index)?.name?.asString() + val value = it.value + val calculatedValue: Any? = if (value is PsiArrayInitializerMemberValue) { + value.initializers.map { + calcValue(it) + } + } else { + calcValue(it.value) + } + KSValueArgumentJavaImpl.getCached( + name = name?.let(KSNameImpl::getCached), + value = calculatedValue, + this + ) + } + val presentValueArgumentNames = presentValueArguments.map { it.name?.asString() ?: "" } + val argumentsFromDefault = annotationConstructor?.let { + it.getAbsentDefaultArguments(presentValueArgumentNames, this) + } ?: emptyList() + presentValueArguments.plus(argumentsFromDefault) + } + + override val defaultArguments: List<KSValueArgument> by lazy { + val kotlinType = (annotationType.resolve() as? KSTypeImpl)?.kotlinType + kotlinType?.getDefaultConstructorArguments(emptyList(), this) ?: emptyList() + } + + private fun calcValue(value: PsiAnnotationMemberValue?): Any? { + if (value is PsiAnnotation) { + return getCached(value) + } + val result = when (value) { + is PsiReference -> value.resolve()?.let { resolved -> + JavaPsiFacade.getInstance(value.project).constantEvaluationHelper.computeConstantExpression(value) + ?: resolved + } + else -> value?.let { + JavaPsiFacade.getInstance(value.project).constantEvaluationHelper.computeConstantExpression(value) + } + } + return when (result) { + is PsiType -> { + ResolverImpl.instance!!.resolveJavaTypeInAnnotations(result) + } + is PsiLiteralValue -> { + result.value + } + is PsiField -> { + // manually handle enums as constant expression evaluator does not seem to be resolving them. + val containingClass = result.containingClass + if (containingClass?.classKind == JvmClassKind.ENUM) { + // this is an enum entry + containingClass.qualifiedName?.let { + ResolverImpl.instance!!.getClassDeclarationByName(it) + }?.declarations?.find { + it is KSClassDeclaration && it.classKind == ClassKind.ENUM_ENTRY && + it.simpleName.asString() == result.name + }?.let { (it as KSClassDeclaration).asStarProjectedType() } + ?.let { + return it + } + } else { + null + } + } + else -> result + } + } + + override val shortName: KSName by lazy { + KSNameImpl.getCached(psi.qualifiedName!!.split(".").last()) + } + + override val useSiteTarget: AnnotationUseSiteTarget? = null + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitAnnotation(this, data) + } + + override fun toString(): String { + return "@${shortName.asString()}" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt new file mode 100644 index 00000000..05f017be --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt @@ -0,0 +1,111 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.getAllFunctions +import com.google.devtools.ksp.symbol.impl.binary.getAllProperties +import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.symbol.impl.kotlin.getKSTypeCached +import com.google.devtools.ksp.toKSModifiers +import com.intellij.psi.PsiEnumConstant +import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.descriptors.ClassDescriptor + +class KSClassDeclarationJavaEnumEntryImpl private constructor(val psi: PsiEnumConstant) : + KSClassDeclaration, + KSDeclarationJavaImpl(psi), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<PsiEnumConstant, KSClassDeclarationJavaEnumEntryImpl>() { + fun getCached(psi: PsiEnumConstant) = cache.getOrPut(psi) { KSClassDeclarationJavaEnumEntryImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) } + } + + override val classKind: ClassKind = ClassKind.ENUM_ENTRY + + override val containingFile: KSFile? by lazy { + KSFileJavaImpl.getCached(psi.containingFile as PsiJavaFile) + } + + override val isCompanionObject = false + + override fun getSealedSubclasses(): Sequence<KSClassDeclaration> = emptySequence() + + private val descriptor: ClassDescriptor? by lazy { + ResolverImpl.instance!!.resolveJavaDeclaration(psi) as ClassDescriptor + } + + override fun getAllFunctions(): Sequence<KSFunctionDeclaration> = + descriptor?.getAllFunctions() ?: emptySequence() + + override fun getAllProperties(): Sequence<KSPropertyDeclaration> = + descriptor?.getAllProperties() ?: emptySequence() + + override val declarations: Sequence<KSDeclaration> = emptySequence() + + override val modifiers: Set<Modifier> by lazy { + psi.toKSModifiers() + } + + override val parentDeclaration: KSDeclaration? by lazy { + psi.findParentDeclaration() + } + + override val primaryConstructor: KSFunctionDeclaration? = null + + override val qualifiedName: KSName by lazy { + KSNameImpl.getCached("${parentDeclaration!!.qualifiedName!!.asString()}.${psi.name}") + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(psi.name) + } + + override val superTypes: Sequence<KSTypeReference> = emptySequence() + + override val typeParameters: List<KSTypeParameter> = emptyList() + + // Enum can't have type parameters. + override fun asType(typeArguments: List<KSTypeArgument>): KSType { + if (typeArguments.isNotEmpty()) + return KSErrorType + return asStarProjectedType() + } + + override fun asStarProjectedType(): KSType { + return getKSTypeCached(descriptor!!.defaultType) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitClassDeclaration(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt new file mode 100644 index 00000000..f12933f8 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt @@ -0,0 +1,173 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.isConstructor +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.processing.impl.workaroundForNested +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.getAllFunctions +import com.google.devtools.ksp.symbol.impl.binary.getAllProperties +import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.symbol.impl.kotlin.getKSTypeCached +import com.google.devtools.ksp.symbol.impl.replaceTypeArguments +import com.google.devtools.ksp.symbol.impl.synthetic.KSConstructorSyntheticImpl +import com.google.devtools.ksp.toKSModifiers +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiEnumConstant +import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections + +class KSClassDeclarationJavaImpl private constructor(val psi: PsiClass) : + KSClassDeclaration, + KSDeclarationJavaImpl(psi), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<PsiClass, KSClassDeclarationJavaImpl>() { + fun getCached(psi: PsiClass) = cache.getOrPut(psi) { KSClassDeclarationJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + override val classKind: ClassKind by lazy { + when { + psi.isAnnotationType -> ClassKind.ANNOTATION_CLASS + psi.isInterface -> ClassKind.INTERFACE + psi.isEnum -> ClassKind.ENUM_CLASS + else -> ClassKind.CLASS + } + } + + override val containingFile: KSFile? by lazy { + KSFileJavaImpl.getCached(psi.containingFile as PsiJavaFile) + } + + override val isCompanionObject = false + + // Could the resolution ever fail? + private val descriptor: ClassDescriptor? by lazy { + ResolverImpl.instance!!.moduleClassResolver.resolveClass(JavaClassImpl(psi).apply { workaroundForNested() }) + } + + // TODO in 1.5 + jvmTarget 15, will we return Java permitted types? + override fun getSealedSubclasses(): Sequence<KSClassDeclaration> = emptySequence() + + override fun getAllFunctions(): Sequence<KSFunctionDeclaration> = + descriptor?.getAllFunctions() ?: emptySequence() + + override fun getAllProperties(): Sequence<KSPropertyDeclaration> = + descriptor?.getAllProperties() ?: emptySequence() + + override val declarations: Sequence<KSDeclaration> by lazy { + val allDeclarations = ( + psi.fields.asSequence().map { + when (it) { + is PsiEnumConstant -> KSClassDeclarationJavaEnumEntryImpl.getCached(it) + else -> KSPropertyDeclarationJavaImpl.getCached(it) + } + } + + psi.innerClasses.map { KSClassDeclarationJavaImpl.getCached(it) } + + psi.constructors.map { KSFunctionDeclarationJavaImpl.getCached(it) } + + psi.methods.map { KSFunctionDeclarationJavaImpl.getCached(it) } + ) + .distinct() + // java annotation classes are interface. they get a constructor in .class + // hence they should get one here. + if (classKind == ClassKind.ANNOTATION_CLASS || !psi.isInterface) { + val hasConstructor = allDeclarations.any { + it is KSFunctionDeclaration && it.isConstructor() + } + if (hasConstructor) { + allDeclarations.memoized() + } else { + (allDeclarations + KSConstructorSyntheticImpl.getCached(this)).memoized() + } + } else { + allDeclarations.memoized() + } + } + + override val modifiers: Set<Modifier> by lazy { + val modifiers = mutableSetOf<Modifier>() + modifiers.addAll(psi.toKSModifiers()) + if (psi.isAnnotationType) { + modifiers.add(Modifier.ANNOTATION) + } + if (psi.isEnum) { + modifiers.add(Modifier.ENUM) + } + modifiers + } + + override val primaryConstructor: KSFunctionDeclaration? = null + + override val qualifiedName: KSName by lazy { + KSNameImpl.getCached(psi.qualifiedName!!) + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(psi.name!!) + } + + override val superTypes: Sequence<KSTypeReference> by lazy { + val adjusted = if (!psi.isInterface && psi.superTypes.size > 1) { + psi.superTypes.filterNot { + it.className == "Object" && it.canonicalText == "java.lang.Object" + } + } else { + psi.superTypes.toList() + } + adjusted.asSequence().map { KSTypeReferenceJavaImpl.getCached(it, this) }.memoized() + } + + override val typeParameters: List<KSTypeParameter> by lazy { + psi.typeParameters.map { KSTypeParameterJavaImpl.getCached(it) } + } + + override fun asType(typeArguments: List<KSTypeArgument>): KSType { + return descriptor?.let { + it.defaultType.replaceTypeArguments(typeArguments)?.let { + getKSTypeCached(it, typeArguments) + } + } ?: KSErrorType + } + + override fun asStarProjectedType(): KSType { + return descriptor?.let { + getKSTypeCached(it.defaultType.replaceArgumentsWithStarProjections()) + } ?: KSErrorType + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitClassDeclaration(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceJavaImpl.kt new file mode 100644 index 00000000..748a0b93 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceJavaImpl.kt @@ -0,0 +1,66 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.getInstanceForCurrentRound +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiJavaCodeReferenceElement +import com.intellij.psi.impl.source.PsiClassReferenceType + +class KSClassifierReferenceJavaImpl private constructor( + val psi: PsiClassType, + override val parent: KSNode +) : KSClassifierReference { + companion object : KSObjectCache<Pair<PsiClassType, KSNode>, KSClassifierReferenceJavaImpl>() { + fun getCached(psi: PsiClassType, parent: KSNode): KSClassifierReferenceJavaImpl { + val curParent = getInstanceForCurrentRound(parent) as KSTypeReference + return cache.getOrPut(Pair(psi, curParent)) { KSClassifierReferenceJavaImpl(psi, curParent) } + } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + (psi as? PsiJavaCodeReferenceElement)?.toLocation() ?: NonExistLocation + } + + override val qualifier: KSClassifierReference? by lazy { + val qualifierReference = (psi as? PsiClassReferenceType)?.reference?.qualifier as? PsiJavaCodeReferenceElement + ?: return@lazy null + val qualifierType = PsiClassReferenceType(qualifierReference, psi.languageLevel) + getCached(qualifierType, parent) + } + + // PsiClassType.parameters is semantically argument + override val typeArguments: List<KSTypeArgument> by lazy { + psi.parameters.map { KSTypeArgumentJavaImpl.getCached(it, this) } + } + + override fun referencedName(): String { + return psi.className + if (psi.parameterCount > 0) "<${ + psi.parameters.joinToString(", ") { + it.presentableText + } + }>" else "" + } + + override fun toString() = referencedName() +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceLiteImplForJava.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceLiteImplForJava.kt new file mode 100644 index 00000000..3d2a0624 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassifierReferenceLiteImplForJava.kt @@ -0,0 +1,50 @@ +package com.google.devtools.ksp.symbol.impl.java + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSClassifierReference +import com.google.devtools.ksp.symbol.KSTypeArgument +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiMethod + +class KSClassifierReferenceLiteImplForJava( + override val parent: KSTypeReferenceLiteJavaImpl, + private val name: String? +) : KSClassifierReference { + companion object : KSObjectCache<Pair<KSTypeReferenceLiteJavaImpl, String?>, KSClassifierReference>() { + fun getCached(parent: KSTypeReferenceLiteJavaImpl, name: String? = null) = + KSClassifierReferenceLiteImplForJava.cache + .getOrPut(Pair(parent, name)) { KSClassifierReferenceLiteImplForJava(parent, name) } + } + override val qualifier: KSClassifierReference? by lazy { + val referencedName = referencedName() + if (referencedName.lastIndexOf('.') == -1) { + null + } else { + getCached(parent, referencedName.substringBeforeLast('.')) + } + } + + override fun referencedName(): String { + return name ?: when (parent.psiElement) { + is PsiAnnotation -> parent.psiElement.nameReferenceElement?.text ?: "<ERROR>" + is PsiMethod -> parent.psiElement.name + else -> throw IllegalStateException( + "Unexpected psi type in KSTypeReferenceLiteJavaImpl: ${parent.psiElement.javaClass}, $ExceptionMessage" + ) + } + } + + override val typeArguments: List<KSTypeArgument> = emptyList() + + override val origin: Origin = Origin.JAVA + + override val location: Location = NonExistLocation + + override fun toString(): String { + return referencedName() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSDeclarationJavaImpl.kt new file mode 100644 index 00000000..3609a5a3 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSDeclarationJavaImpl.kt @@ -0,0 +1,48 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.getDocString +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.impl.findParentAnnotated +import com.google.devtools.ksp.symbol.impl.findParentDeclaration +import com.intellij.psi.PsiElement + +abstract class KSDeclarationJavaImpl(private val psi: PsiElement) : KSDeclaration { + override val packageName: KSName by lazy { + this.containingFile!!.packageName + } + + override fun toString(): String { + return this.simpleName.asString() + } + + override val docString by lazy { + psi.getDocString() + } + + override val parentDeclaration: KSDeclaration? by lazy { + psi.findParentDeclaration() + } + + override val parent: KSNode? by lazy { + psi.findParentAnnotated() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFileJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFileJavaImpl.kt new file mode 100644 index 00000000..e49eea06 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFileJavaImpl.kt @@ -0,0 +1,62 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiJavaFile + +class KSFileJavaImpl private constructor(val psi: PsiJavaFile) : KSFile { + companion object : KSObjectCache<PsiJavaFile, KSFileJavaImpl>() { + fun getCached(psi: PsiJavaFile) = cache.getOrPut(psi) { KSFileJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + override val parent: KSNode? = null + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val declarations: Sequence<KSDeclaration> by lazy { + psi.classes.asSequence().map { KSClassDeclarationJavaImpl.getCached(it) }.memoized() + } + + override val fileName: String by lazy { + psi.name + } + + override val filePath: String by lazy { + psi.virtualFile.path + } + + override val packageName: KSName = KSNameImpl.getCached(psi.packageName) + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFile(this, data) + } + + override fun toString(): String { + return "File: ${this.fileName}" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt new file mode 100644 index 00000000..1513fffc --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt @@ -0,0 +1,125 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.toKSModifiers +import com.intellij.lang.jvm.JvmModifier +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.descriptors.FunctionDescriptor + +class KSFunctionDeclarationJavaImpl private constructor(val psi: PsiMethod) : + KSFunctionDeclaration, + KSDeclarationJavaImpl(psi), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<PsiMethod, KSFunctionDeclarationJavaImpl>() { + fun getCached(psi: PsiMethod) = cache.getOrPut(psi) { KSFunctionDeclarationJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + override val containingFile: KSFile? by lazy { + KSFileJavaImpl.getCached(psi.containingFile as PsiJavaFile) + } + + override fun findOverridee(): KSDeclaration? { + val descriptor = ResolverImpl.instance!!.resolveFunctionDeclaration(this) + return (descriptor as? FunctionDescriptor)?.findClosestOverridee()?.toKSDeclaration() + } + + override val declarations: Sequence<KSDeclaration> = emptySequence() + + override val extensionReceiver: KSTypeReference? = null + + override val functionKind: FunctionKind = when { + psi.hasModifier(JvmModifier.STATIC) -> FunctionKind.STATIC + else -> FunctionKind.MEMBER + } + + override val isAbstract: Boolean by lazy { + this.modifiers.contains(Modifier.ABSTRACT) || + ( + (this.parentDeclaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE && + !this.modifiers.contains(Modifier.JAVA_DEFAULT) && functionKind != FunctionKind.STATIC + ) + } + + override val modifiers: Set<Modifier> by lazy { + psi.toKSModifiers() + } + + override val parameters: List<KSValueParameter> by lazy { + psi.parameterList.parameters.map { KSValueParameterJavaImpl.getCached(it) } + } + + override val parentDeclaration: KSDeclaration? by lazy { + psi.findParentDeclaration() + } + + override val qualifiedName: KSName by lazy { + KSNameImpl.getCached("${parentDeclaration?.qualifiedName?.asString()}.${this.simpleName.asString()}") + } + + override val returnType: KSTypeReference? by lazy { + when { + psi.returnType != null -> { + KSTypeReferenceJavaImpl.getCached(psi.returnType!!, this) + } + psi.isConstructor -> { + KSTypeReferenceLiteJavaImpl.getCached(psi, this) + } + else -> { + null + } + } + } + + override val simpleName: KSName by lazy { + if (psi.isConstructor) { + KSNameImpl.getCached("<init>") + } else { + KSNameImpl.getCached(psi.name) + } + } + + override val typeParameters: List<KSTypeParameter> by lazy { + psi.typeParameters.map { KSTypeParameterJavaImpl.getCached(it) } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFunctionDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSFunction = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSNodeJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSNodeJavaImpl.kt new file mode 100644 index 00000000..860fb5fa --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSNodeJavaImpl.kt @@ -0,0 +1,12 @@ +package com.google.devtools.ksp.symbol.impl.java + +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiElement + +abstract class KSNodeJavaImpl(private val psi: PsiElement, override val parent: KSNode?) : KSNode { + override val location: Location by lazy { + psi.toLocation() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSPropertyDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSPropertyDeclarationJavaImpl.kt new file mode 100644 index 00000000..57ff5941 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSPropertyDeclarationJavaImpl.kt @@ -0,0 +1,101 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.toKSModifiers +import com.intellij.psi.PsiField +import com.intellij.psi.PsiJavaFile + +class KSPropertyDeclarationJavaImpl private constructor(val psi: PsiField) : + KSPropertyDeclaration, + KSDeclarationJavaImpl(psi), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<PsiField, KSPropertyDeclarationJavaImpl>() { + fun getCached(psi: PsiField) = cache.getOrPut(psi) { KSPropertyDeclarationJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val isMutable: Boolean + get() = !modifiers.contains(Modifier.FINAL) + + override val hasBackingField: Boolean + get() = true + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + override val containingFile: KSFile? by lazy { + KSFileJavaImpl.getCached(psi.containingFile as PsiJavaFile) + } + + override val extensionReceiver: KSTypeReference? = null + + override val getter: KSPropertyGetter? = null + + override val setter: KSPropertySetter? = null + + override val modifiers: Set<Modifier> by lazy { + psi.toKSModifiers() + } + + override val parentDeclaration: KSDeclaration? by lazy { + psi.findParentDeclaration() + } + + override val qualifiedName: KSName by lazy { + KSNameImpl.getCached("${parentDeclaration?.qualifiedName?.asString()}.${this.simpleName.asString()}") + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(psi.name) + } + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override val type: KSTypeReference by lazy { + KSTypeReferenceJavaImpl.getCached(psi.type, this) + } + + override fun findOverridee(): KSPropertyDeclaration? { + return null + } + + override fun isDelegated(): Boolean { + return false + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSType = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeArgumentJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeArgumentJavaImpl.kt new file mode 100644 index 00000000..211cf4c8 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeArgumentJavaImpl.kt @@ -0,0 +1,64 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.kotlin.KSTypeArgumentImpl +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiType +import com.intellij.psi.PsiWildcardType +import com.intellij.psi.impl.source.PsiClassReferenceType + +class KSTypeArgumentJavaImpl private constructor( + val psi: PsiType, + override val parent: KSNode? +) : KSTypeArgumentImpl() { + companion object : KSObjectCache<PsiType, KSTypeArgumentJavaImpl>() { + fun getCached(psi: PsiType, parent: KSNode?) = cache.getOrPut(psi) { KSTypeArgumentJavaImpl(psi, parent) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + (psi as? PsiClassReferenceType)?.reference?.toLocation() ?: NonExistLocation + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + // Could be unbounded, need to model unbdouned type argument. + override val type: KSTypeReference? by lazy { + KSTypeReferenceJavaImpl.getCached(psi, this) + } + + override val variance: Variance by lazy { + if (psi is PsiWildcardType) { + when { + psi.isExtends -> Variance.COVARIANT + psi.isSuper -> Variance.CONTRAVARIANT + psi.bound == null -> Variance.STAR + else -> Variance.INVARIANT + } + } else { + Variance.INVARIANT + } + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeParameterJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeParameterJavaImpl.kt new file mode 100644 index 00000000..e3c41286 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeParameterJavaImpl.kt @@ -0,0 +1,82 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.findParentDeclaration +import com.google.devtools.ksp.symbol.impl.kotlin.KSExpectActualNoImpl +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiTypeParameter + +class KSTypeParameterJavaImpl private constructor(val psi: PsiTypeParameter) : + KSTypeParameter, + KSDeclarationJavaImpl(psi), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<PsiTypeParameter, KSTypeParameterJavaImpl>() { + fun getCached(psi: PsiTypeParameter) = cache.getOrPut(psi) { KSTypeParameterJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + override val bounds: Sequence<KSTypeReference> by lazy { + psi.extendsListTypes.asSequence().map { KSTypeReferenceJavaImpl.getCached(it, this) }.memoized() + } + override val simpleName: KSName by lazy { + KSNameImpl.getCached(psi.name ?: "_") + } + + override val qualifiedName: KSName? by lazy { + KSNameImpl.getCached(parentDeclaration?.qualifiedName?.asString() ?: "" + "." + simpleName.asString()) + } + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override val parentDeclaration: KSDeclaration? by lazy { + psi.findParentDeclaration() + } + + override val containingFile: KSFile? by lazy { + KSFileJavaImpl.getCached(psi.containingFile as PsiJavaFile) + } + + override val modifiers: Set<Modifier> = emptySet() + + override val isReified: Boolean = false + + override val name: KSName by lazy { + KSNameImpl.getCached(psi.name!!) + } + + override val variance: Variance = Variance.INVARIANT + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeParameter(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceJavaImpl.kt new file mode 100644 index 00000000..6b035244 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceJavaImpl.kt @@ -0,0 +1,148 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.binary.KSClassDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSClassifierReferenceDescriptorImpl +import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType +import com.google.devtools.ksp.symbol.impl.kotlin.KSTypeImpl +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiArrayType +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiPrimitiveType +import com.intellij.psi.PsiType +import com.intellij.psi.PsiWildcardType +import com.intellij.psi.impl.source.PsiClassReferenceType +import org.jetbrains.kotlin.descriptors.NotFoundClasses +import org.jetbrains.kotlin.load.java.NOT_NULL_ANNOTATIONS +import org.jetbrains.kotlin.load.java.NULLABLE_ANNOTATIONS +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.makeNullable + +class KSTypeReferenceJavaImpl private constructor(val psi: PsiType, override val parent: KSNode?) : KSTypeReference { + companion object : KSObjectCache<Pair<PsiType, KSNode?>, KSTypeReferenceJavaImpl>() { + fun getCached(psi: PsiType, parent: KSNode?) = cache + .getOrPut(Pair(psi, parent)) { KSTypeReferenceJavaImpl(psi, parent) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + (psi as? PsiClassReferenceType)?.reference?.toLocation() ?: NonExistLocation + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) }.memoized() + } + + override val modifiers: Set<Modifier> = emptySet() + + override val element: KSReferenceElement by lazy { + fun PsiPrimitiveType.toKotlinType(): KotlinType { + return when (this.name) { + "int" -> ResolverImpl.instance!!.module.builtIns.intType + "short" -> ResolverImpl.instance!!.module.builtIns.shortType + "byte" -> ResolverImpl.instance!!.module.builtIns.byteType + "long" -> ResolverImpl.instance!!.module.builtIns.longType + "float" -> ResolverImpl.instance!!.module.builtIns.floatType + "double" -> ResolverImpl.instance!!.module.builtIns.doubleType + "char" -> ResolverImpl.instance!!.module.builtIns.charType + "boolean" -> ResolverImpl.instance!!.module.builtIns.booleanType + "void" -> ResolverImpl.instance!!.module.builtIns.unitType + else -> throw IllegalStateException("Unexpected primitive type ${this.name}, $ExceptionMessage") + } + } + + val type = if (psi is PsiWildcardType) { + psi.bound + } else { + psi + } + when (type) { + is PsiClassType -> KSClassifierReferenceJavaImpl.getCached(type, this) + is PsiWildcardType -> KSClassifierReferenceJavaImpl.getCached(type.extendsBound as PsiClassType, this) + is PsiPrimitiveType -> KSClassifierReferenceDescriptorImpl.getCached(type.toKotlinType(), origin, this) + is PsiArrayType -> { + val componentType = ResolverImpl.instance!!.resolveJavaType(type.componentType, this) + if (type.componentType !is PsiPrimitiveType) { + KSClassifierReferenceDescriptorImpl.getCached( + ResolverImpl.instance!!.module.builtIns.getArrayType(Variance.INVARIANT, componentType), + origin, + this + ) + } else { + KSClassifierReferenceDescriptorImpl.getCached( + ResolverImpl.instance!!.module.builtIns + .getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(componentType)!!, + origin, this + ) + } + } + null -> + KSClassifierReferenceDescriptorImpl.getCached( + (ResolverImpl.instance!!.builtIns.anyType as KSTypeImpl).kotlinType.makeNullable(), origin, this + ) + else -> throw IllegalStateException("Unexpected psi type for ${type.javaClass}, $ExceptionMessage") + } + } + + override fun resolve(): KSType { + val resolvedType = ResolverImpl.instance!!.resolveUserType(this) + val relatedAnnotations = (annotations + ((parent as? KSAnnotated)?.annotations ?: emptySequence())) + .mapNotNull { + (it.annotationType.resolve() as? KSTypeImpl)?.kotlinType?.constructor?.declarationDescriptor?.fqNameSafe + } + val resolved = if ((resolvedType.declaration as? KSClassDeclarationDescriptorImpl) + ?.descriptor is NotFoundClasses.MockClassDescriptor + ) { + KSErrorType + } else resolvedType + val hasNotNull = relatedAnnotations.any { it in NOT_NULL_ANNOTATIONS } + val hasNullable = relatedAnnotations.any { it in NULLABLE_ANNOTATIONS } + return if (hasNullable && !hasNotNull) { + resolved.makeNullable() + } else if (!hasNullable && hasNotNull) { + resolved.makeNotNullable() + } else resolved + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun toString(): String { + return element.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceLiteJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceLiteJavaImpl.kt new file mode 100644 index 00000000..601c8bee --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSTypeReferenceLiteJavaImpl.kt @@ -0,0 +1,90 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiMethod + +class KSTypeReferenceLiteJavaImpl private constructor(val psiElement: PsiElement, override val parent: KSNode) : + KSTypeReference { + companion object : KSObjectCache<KSNode, KSTypeReferenceLiteJavaImpl>() { + fun getCached(psiElement: PsiElement, parent: KSNode) = cache + .getOrPut(parent) { KSTypeReferenceLiteJavaImpl(psiElement, parent) } + } + + val type: KSType by lazy { + when (psiElement) { + is PsiAnnotation -> { + val psiClass = psiElement.nameReferenceElement!!.resolve() as? PsiClass + psiClass?.let { + (psiElement.containingFile as? PsiJavaFile)?.let { + ResolverImpl.instance!!.incrementalContext.recordLookup(it, psiClass.qualifiedName!!) + } + KSClassDeclarationJavaImpl.getCached(psiClass).asStarProjectedType() + } ?: KSErrorType + } + is PsiMethod -> { + KSClassDeclarationJavaImpl.getCached(psiElement.containingClass!!).asStarProjectedType() + } + else -> throw IllegalStateException( + "Unexpected psi type in KSTypeReferenceLiteJavaImpl: ${psiElement.javaClass}, $ExceptionMessage" + ) + } + } + + override val origin = Origin.JAVA + + override val location: Location = NonExistLocation + + override val element: KSReferenceElement by lazy { + KSClassifierReferenceLiteImplForJava.getCached(this) + } + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val modifiers: Set<Modifier> = emptySet() + + override fun resolve(): KSType { + return type + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun toString(): String { + return type.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueArgumentJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueArgumentJavaImpl.kt new file mode 100644 index 00000000..1424b9e4 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueArgumentJavaImpl.kt @@ -0,0 +1,50 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.kotlin.KSValueArgumentImpl + +class KSValueArgumentJavaImpl private constructor( + override val name: KSName?, + override val value: Any?, + override val parent: KSNode? +) : KSValueArgumentImpl() { + companion object : KSObjectCache<Triple<KSName?, Any?, KSNode?>, KSValueArgumentJavaImpl>() { + fun getCached(name: KSName?, value: Any?, parent: KSNode?) = + cache.getOrPut(Triple(name, value, parent)) { KSValueArgumentJavaImpl(name, value, parent) } + } + + override val origin = Origin.JAVA + + override val location: Location = NonExistLocation + + override val isSpread: Boolean = false + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override fun toString(): String { + return "${name?.asString() ?: ""}:$value" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueParameterJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueParameterJavaImpl.kt new file mode 100644 index 00000000..086b670a --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSValueParameterJavaImpl.kt @@ -0,0 +1,88 @@ +/* + * 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.symbol.impl.java + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toLocation +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiParameter + +class KSValueParameterJavaImpl private constructor(val psi: PsiParameter) : KSValueParameter { + companion object : KSObjectCache<PsiParameter, KSValueParameterJavaImpl>() { + fun getCached(psi: PsiParameter) = cache.getOrPut(psi) { KSValueParameterJavaImpl(psi) } + } + + override val origin = Origin.JAVA + + override val location: Location by lazy { + psi.toLocation() + } + override val parent: KSNode? by lazy { + var parentPsi = psi.parent + while (true) { + when (parentPsi) { + null, is PsiMethod, is PsiAnnotation -> break + else -> parentPsi = parentPsi.parent + } + } + when (parentPsi) { + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(parentPsi) + is PsiAnnotation -> KSAnnotationJavaImpl.getCached(parentPsi) + else -> null + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + psi.annotations.asSequence().map { KSAnnotationJavaImpl.getCached(it) } + } + + override val isCrossInline: Boolean = false + + override val isNoInline: Boolean = false + + override val isVararg: Boolean = psi.isVarArgs + + override val isVal: Boolean = false + + override val isVar: Boolean = false + + override val name: KSName? by lazy { + if (psi.name != null) { + KSNameImpl.getCached(psi.name!!) + } else { + null + } + } + + override val type: KSTypeReference by lazy { + KSTypeReferenceJavaImpl.getCached(psi.type, this) + } + + override val hasDefault: Boolean = false + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitValueParameter(this, data) + } + + override fun toString(): String { + return name?.asString() ?: "_" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSAnnotationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSAnnotationImpl.kt new file mode 100644 index 00000000..19bde157 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSAnnotationImpl.kt @@ -0,0 +1,120 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.binary.createKSValueArguments +import com.google.devtools.ksp.symbol.impl.binary.getDefaultArguments +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.* +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.KtTypeAlias +import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.psi.KtTypeProjection +import org.jetbrains.kotlin.psi.KtTypeReference + +class KSAnnotationImpl private constructor(val ktAnnotationEntry: KtAnnotationEntry) : KSAnnotation { + companion object : KSObjectCache<KtAnnotationEntry, KSAnnotationImpl>() { + fun getCached(ktAnnotationEntry: KtAnnotationEntry) = cache.getOrPut(ktAnnotationEntry) { + KSAnnotationImpl(ktAnnotationEntry) + } + } + + override val origin = Origin.KOTLIN + + override val parent: KSNode? by lazy { + var parentPsi = ktAnnotationEntry.parent + while (true) { + when (parentPsi) { + null, is KtFile, is KtClassOrObject, is KtFunction, is KtParameter, is KtTypeParameter, + is KtTypeAlias, is KtProperty, is KtPropertyAccessor, is KtTypeProjection, is KtTypeReference -> break + else -> parentPsi = parentPsi.parent + } + } + when (parentPsi) { + is KtFile -> KSFileImpl.getCached(parentPsi) + is KtClassOrObject -> KSClassDeclarationImpl.getCached(parentPsi) + is KtFunction -> KSFunctionDeclarationImpl.getCached(parentPsi) + is KtParameter -> KSValueParameterImpl.getCached(parentPsi) + is KtTypeParameter -> KSTypeParameterImpl.getCached(parentPsi) + is KtTypeAlias -> KSTypeAliasImpl.getCached(parentPsi) + is KtProperty -> KSPropertyDeclarationImpl.getCached(parentPsi) + is KtPropertyAccessor -> KSPropertyAccessorImpl.getCached(parentPsi) + is KtTypeProjection -> KSTypeArgumentKtImpl.getCached(parentPsi) + is KtTypeReference -> KSTypeReferenceImpl.getCached(parentPsi) + else -> null + } + } + + override val location: Location by lazy { + ktAnnotationEntry.toLocation() + } + + override val annotationType: KSTypeReference by lazy { + KSTypeReferenceImpl.getCached(ktAnnotationEntry.typeReference!!) + } + + override val arguments: List<KSValueArgument> by lazy { + resolved?.createKSValueArguments(this) ?: emptyList() + } + + override val defaultArguments: List<KSValueArgument> by lazy { + resolved?.getDefaultArguments(this) ?: emptyList() + } + + override val shortName: KSName by lazy { + KSNameImpl.getCached(ktAnnotationEntry.shortName!!.asString()) + } + + override val useSiteTarget: AnnotationUseSiteTarget? by lazy { + when (ktAnnotationEntry.useSiteTarget?.getAnnotationUseSiteTarget()) { + null -> null + FILE -> AnnotationUseSiteTarget.FILE + PROPERTY -> AnnotationUseSiteTarget.PROPERTY + FIELD -> AnnotationUseSiteTarget.FIELD + PROPERTY_GETTER -> AnnotationUseSiteTarget.GET + PROPERTY_SETTER -> AnnotationUseSiteTarget.SET + RECEIVER -> AnnotationUseSiteTarget.RECEIVER + CONSTRUCTOR_PARAMETER -> AnnotationUseSiteTarget.PARAM + SETTER_PARAMETER -> AnnotationUseSiteTarget.SETPARAM + PROPERTY_DELEGATE_FIELD -> AnnotationUseSiteTarget.DELEGATE + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitAnnotation(this, data) + } + + private val resolved: AnnotationDescriptor? by lazy { + ResolverImpl.instance!!.resolveAnnotationEntry(ktAnnotationEntry) + } + + override fun toString(): String { + return "@${shortName.asString()}" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSCallableReferenceImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSCallableReferenceImpl.kt new file mode 100644 index 00000000..c71f37d4 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSCallableReferenceImpl.kt @@ -0,0 +1,68 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.findParentOfType +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.KtFunctionType +import org.jetbrains.kotlin.psi.KtTypeReference + +class KSCallableReferenceImpl private constructor(val ktFunctionType: KtFunctionType) : KSCallableReference { + companion object : KSObjectCache<KtFunctionType, KSCallableReferenceImpl>() { + fun getCached(ktFunctionType: KtFunctionType) = cache.getOrPut(ktFunctionType) { + KSCallableReferenceImpl(ktFunctionType) + } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + ktFunctionType.toLocation() + } + + override val parent: KSNode? by lazy { + ktFunctionType.findParentOfType<KtTypeReference>()?.let { KSTypeReferenceImpl.getCached(it) } + } + + override val typeArguments: List<KSTypeArgument> by lazy { + ktFunctionType.typeArgumentsAsTypes.map { KSTypeArgumentLiteImpl.getCached(it) } + } + + override val functionParameters: List<KSValueParameter> by lazy { + ktFunctionType.parameters.map { KSValueParameterImpl.getCached(it) } + } + + override val receiverType: KSTypeReference? by lazy { + if (ktFunctionType.receiver != null) { + KSTypeReferenceImpl.getCached(ktFunctionType.receiverTypeReference!!) + } else { + null + } + } + + override val returnType: KSTypeReference by lazy { + KSTypeReferenceImpl.getCached(ktFunctionType.returnTypeReference!!) + } + + override fun toString(): String { + return "${receiverType?.let { "$it." } ?: ""}(${functionParameters + .joinToString(", ") { it.type.toString() }}) -> $returnType" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt new file mode 100644 index 00000000..89bb2b4f --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt @@ -0,0 +1,138 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.getClassType +import com.google.devtools.ksp.isConstructor +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.getAllFunctions +import com.google.devtools.ksp.symbol.impl.binary.getAllProperties +import com.google.devtools.ksp.symbol.impl.binary.sealedSubclassesSequence +import com.google.devtools.ksp.symbol.impl.synthetic.KSConstructorSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSTypeReferenceSyntheticImpl +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.psi.KtSecondaryConstructor +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections + +class KSClassDeclarationImpl private constructor(val ktClassOrObject: KtClassOrObject) : + KSClassDeclaration, + KSDeclarationImpl(ktClassOrObject), + KSExpectActual by KSExpectActualImpl(ktClassOrObject) { + companion object : KSObjectCache<KtClassOrObject, KSClassDeclarationImpl>() { + fun getCached(ktClassOrObject: KtClassOrObject) = + cache.getOrPut(ktClassOrObject) { KSClassDeclarationImpl(ktClassOrObject) } + } + + override val classKind: ClassKind by lazy { + ktClassOrObject.getClassType() + } + + override val isCompanionObject by lazy { + (ktClassOrObject is KtObjectDeclaration) && (ktClassOrObject.isCompanion()) + } + + override fun getSealedSubclasses(): Sequence<KSClassDeclaration> { + return if (Modifier.SEALED in modifiers) { + ResolverImpl.instance!!.incrementalContext.recordGetSealedSubclasses(this) + descriptor.sealedSubclassesSequence() + } else { + emptySequence() + } + } + + override fun getAllFunctions(): Sequence<KSFunctionDeclaration> = descriptor.getAllFunctions() + + override fun getAllProperties(): Sequence<KSPropertyDeclaration> = descriptor.getAllProperties() + + override val declarations: Sequence<KSDeclaration> by lazy { + val propertiesFromConstructor = primaryConstructor?.parameters + ?.asSequence() + ?.filter { it.isVar || it.isVal } + ?.map { KSPropertyDeclarationParameterImpl.getCached((it as KSValueParameterImpl).ktParameter) } + ?: emptySequence() + var result = propertiesFromConstructor.plus(ktClassOrObject.declarations.asSequence().getKSDeclarations()) + primaryConstructor?.let { primaryConstructor: KSFunctionDeclaration -> + // if primary constructor is from source, it won't show up in declarations + // hence add it as well. + if (primaryConstructor.origin == Origin.KOTLIN) { + result = sequenceOf(primaryConstructor).plus(result) + } + } + if (classKind != ClassKind.INTERFACE) { + // check if we need to add a synthetic constructor + val hasConstructor = result.any { + it is KSFunctionDeclaration && it.isConstructor() + } + if (hasConstructor) { + result.memoized() + } else { + (result + KSConstructorSyntheticImpl.getCached(this)).memoized() + } + } else { + result.memoized() + } + } + + override val primaryConstructor: KSFunctionDeclaration? by lazy { + ktClassOrObject.primaryConstructor?.let { KSFunctionDeclarationImpl.getCached(it) } + ?: if ((classKind == ClassKind.CLASS || classKind == ClassKind.ENUM_CLASS) && + ktClassOrObject.declarations.none { it is KtSecondaryConstructor } + ) + KSConstructorSyntheticImpl.getCached(this) else null + } + + override val superTypes: Sequence<KSTypeReference> by lazy { + val resolver = ResolverImpl.instance!! + ktClassOrObject.superTypeListEntries + .asSequence() + .map { KSTypeReferenceImpl.getCached(it.typeReference!!) } + .ifEmpty { + sequenceOf( + KSTypeReferenceSyntheticImpl.getCached( + resolver.builtIns.anyType, + this + ) + ) + } + .memoized() + } + + private val descriptor: ClassDescriptor by lazy { + (ResolverImpl.instance!!.resolveDeclaration(ktClassOrObject) as ClassDescriptor) + } + + override fun asType(typeArguments: List<KSTypeArgument>): KSType { + return descriptor.defaultType.replaceTypeArguments(typeArguments)?.let { + getKSTypeCached(it, typeArguments) + } ?: KSErrorType + } + + override fun asStarProjectedType(): KSType { + return getKSTypeCached(descriptor.defaultType.replaceArgumentsWithStarProjections()) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitClassDeclaration(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassifierReferenceImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassifierReferenceImpl.kt new file mode 100644 index 00000000..575c15d5 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassifierReferenceImpl.kt @@ -0,0 +1,62 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.findParentOfType +import com.google.devtools.ksp.symbol.KSClassifierReference +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSTypeArgument +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.* + +class KSClassifierReferenceImpl private constructor(val ktUserType: KtUserType) : KSClassifierReference { + companion object : KSObjectCache<KtUserType, KSClassifierReferenceImpl>() { + fun getCached(ktUserType: KtUserType) = cache.getOrPut(ktUserType) { KSClassifierReferenceImpl(ktUserType) } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + ktUserType.toLocation() + } + + override val parent: KSNode? by lazy { + ktUserType.findParentOfType<KtTypeReference>()?.let { KSTypeReferenceImpl.getCached(it) } + } + + override val typeArguments: List<KSTypeArgument> by lazy { + ktUserType.typeArguments.map { KSTypeArgumentKtImpl.getCached(it) } + } + + override fun referencedName(): String { + return ktUserType.referencedName ?: "" + } + + override val qualifier: KSClassifierReference? by lazy { + if (ktUserType.qualifier == null) { + null + } else { + KSClassifierReferenceImpl.getCached(ktUserType.qualifier!!) + } + } + + override fun toString() = referencedName() +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDeclarationImpl.kt new file mode 100644 index 00000000..3e596664 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDeclarationImpl.kt @@ -0,0 +1,96 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.getDocString +import com.google.devtools.ksp.isConstructor +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.findParentAnnotated +import com.google.devtools.ksp.symbol.impl.findParentDeclaration +import com.google.devtools.ksp.symbol.impl.toLocation +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.psi.* + +abstract class KSDeclarationImpl(val ktDeclaration: KtDeclaration) : KSDeclaration { + override val origin: Origin = Origin.KOTLIN + + override val location: Location by lazy { + ktDeclaration.toLocation() + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(ktDeclaration.name!!) + } + + override val qualifiedName: KSName? by lazy { + (ktDeclaration as? KtNamedDeclaration)?.fqName?.let { KSNameImpl.getCached(it.asString()) } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + ktDeclaration.annotationEntries.asSequence().map { KSAnnotationImpl.getCached(it) }.memoized() + } + + override val modifiers: Set<Modifier> by lazy { + // we do not check for JVM_STATIC here intentionally as it actually means static in parent class, + // not in this class. + // see: https://github.com/google/ksp/issues/378 + if (this is KSFunctionDeclaration && this.isConstructor() && + (this.parentDeclaration as? KSClassDeclaration)?.classKind == ClassKind.ENUM_CLASS + ) { + setOf(Modifier.FINAL, Modifier.PRIVATE) + } else { + ktDeclaration.toKSModifiers() + } + } + + override val containingFile: KSFile? by lazy { + KSFileImpl.getCached(ktDeclaration.containingKtFile) + } + + override val packageName: KSName by lazy { + this.containingFile!!.packageName + } + + override val typeParameters: List<KSTypeParameter> by lazy { + (ktDeclaration as? KtTypeParameterListOwner)?.let { + it.typeParameters.map { KSTypeParameterImpl.getCached(it) } + } ?: emptyList() + } + + override val parentDeclaration: KSDeclaration? by lazy { + ktDeclaration.findParentDeclaration() + } + + override val parent: KSNode? by lazy { + ktDeclaration.findParentAnnotated() + } + + override fun toString(): String { + return this.simpleName.asString() + } + + internal val originalAnnotations: List<KSAnnotation> by lazy { + ktDeclaration.annotationEntries.map { KSAnnotationImpl.getCached(it) } + } + + override val docString by lazy { + ktDeclaration.getDocString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDefNonNullReferenceImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDefNonNullReferenceImpl.kt new file mode 100644 index 00000000..37d07c92 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDefNonNullReferenceImpl.kt @@ -0,0 +1,41 @@ +package com.google.devtools.ksp.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.findParentOfType +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.KtIntersectionType +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.psi.KtUserType + +class KSDefNonNullReferenceImpl private constructor(val ktIntersectionType: KtIntersectionType) : + KSDefNonNullReference { + companion object : KSObjectCache<KtIntersectionType, KSDefNonNullReferenceImpl>() { + fun getCached(ktIntersectionType: KtIntersectionType) = KSDefNonNullReferenceImpl + .cache.getOrPut(ktIntersectionType) { KSDefNonNullReferenceImpl(ktIntersectionType) } + } + + override val enclosedType: KSClassifierReference by lazy { + val lhs = ktIntersectionType.getLeftTypeRef()?.typeElement + if (lhs is KtUserType) { + KSClassifierReferenceImpl.getCached(lhs) + } else { + throw IllegalStateException("LHS operand of definitely non null type should be a user type") + } + } + + override val typeArguments: List<KSTypeArgument> + get() = emptyList() + + override val origin: Origin + get() = Origin.KOTLIN + + override val location: Location + get() = ktIntersectionType.toLocation() + + override val parent: KSNode? by lazy { + ktIntersectionType.findParentOfType<KtTypeReference>()?.let { KSTypeReferenceImpl.getCached(it) } + } + + override fun toString() = "${enclosedType.referencedName()} & Any" +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDynamicReferenceImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDynamicReferenceImpl.kt new file mode 100644 index 00000000..27579fce --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSDynamicReferenceImpl.kt @@ -0,0 +1,43 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.* + +class KSDynamicReferenceImpl private constructor(override val parent: KSNode?) : KSDynamicReference { + companion object : KSObjectCache<KSTypeReference, KSDynamicReferenceImpl>() { + fun getCached(parent: KSTypeReference) = cache.getOrPut(parent) { KSDynamicReferenceImpl(parent) } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + NonExistLocation + } + + override val typeArguments: List<KSTypeArgument> = emptyList() + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitDynamicReference(this, data) + } + + override fun toString(): String { + return "<dynamic type>" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt new file mode 100644 index 00000000..8cf66385 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt @@ -0,0 +1,71 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.synthetic.KSErrorTypeClassDeclaration + +object KSErrorType : KSType { + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val arguments: List<KSTypeArgument> = emptyList() + + override val declaration: KSDeclaration = KSErrorTypeClassDeclaration + + override val isError: Boolean = true + + override val nullability: Nullability = Nullability.NULLABLE + + override fun isAssignableFrom(that: KSType): Boolean { + return false + } + + override fun isCovarianceFlexible(): Boolean { + return false + } + + override fun isMutabilityFlexible(): Boolean { + return false + } + + override fun makeNotNullable(): KSType { + return this + } + + override fun makeNullable(): KSType { + return this + } + + override val isMarkedNullable: Boolean = false + + override fun replace(arguments: List<KSTypeArgument>): KSType { + return this + } + + override fun starProjection(): KSType { + return this + } + + override fun toString(): String { + return "<ERROR TYPE>" + } + + override val isFunctionType: Boolean = false + + override val isSuspendFunctionType: Boolean = false +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualImpl.kt new file mode 100644 index 00000000..18c3f9c5 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualImpl.kt @@ -0,0 +1,64 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.processing.impl.findActualsInKSDeclaration +import com.google.devtools.ksp.processing.impl.findExpectsInKSDeclaration +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSExpectActual +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier +import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier + +class KSExpectActualImpl(val declaration: KtDeclaration) : KSExpectActual { + /** + * "All actual declarations that match any part of an expected declaration need to be marked as actual." + */ + override val isActual: Boolean = declaration.hasActualModifier() + + private fun KtDeclaration.isExpect(): Boolean = hasExpectModifier() || containingClassOrObject?.isExpect() == true + + override val isExpect: Boolean = declaration.isExpect() + + private val expects: Sequence<KSDeclaration> by lazy { + descriptor?.findExpectsInKSDeclaration() ?: emptySequence() + } + + override fun findExpects(): Sequence<KSDeclaration> { + if (!isActual) + return emptySequence() + return expects + } + + private val actuals: Sequence<KSDeclaration> by lazy { + descriptor?.findActualsInKSDeclaration() ?: emptySequence() + } + + override fun findActuals(): Sequence<KSDeclaration> { + if (!isExpect) + return emptySequence() + return actuals + } + + private val descriptor: DeclarationDescriptor? by lazy { + ResolverImpl.instance!!.resolveDeclaration(declaration) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualNoImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualNoImpl.kt new file mode 100644 index 00000000..c06cd2ca --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSExpectActualNoImpl.kt @@ -0,0 +1,31 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSExpectActual + +class KSExpectActualNoImpl : KSExpectActual { + override val isActual: Boolean = false + + override val isExpect: Boolean = false + + override fun findActuals(): Sequence<KSDeclaration> = emptySequence() + + override fun findExpects(): Sequence<KSDeclaration> = emptySequence() +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFileImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFileImpl.kt new file mode 100644 index 00000000..3e333a2e --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFileImpl.kt @@ -0,0 +1,68 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.getKSDeclarations +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.KtFile + +class KSFileImpl private constructor(val file: KtFile) : KSFile { + companion object : KSObjectCache<KtFile, KSFileImpl>() { + fun getCached(file: KtFile) = cache.getOrPut(file) { KSFileImpl(file) } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + file.toLocation() + } + + override val parent: KSNode? = null + + override val packageName: KSName by lazy { + KSNameImpl.getCached(file.packageFqName.asString()) + } + + override val annotations: Sequence<KSAnnotation> by lazy { + file.annotationEntries.asSequence().map { KSAnnotationImpl.getCached(it) }.memoized() + } + + override val declarations: Sequence<KSDeclaration> by lazy { + file.declarations.asSequence().getKSDeclarations().memoized() + } + + override val fileName: String by lazy { + file.name + } + + override val filePath: String by lazy { + file.virtualFilePath + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFile(this, data) + } + + override fun toString(): String { + return "File: ${this.fileName}" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt new file mode 100644 index 00000000..75017fbb --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt @@ -0,0 +1,123 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.KSPCompilationError +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing + +class KSFunctionDeclarationImpl private constructor(val ktFunction: KtFunction) : + KSFunctionDeclaration, + KSDeclarationImpl(ktFunction), + KSExpectActual by KSExpectActualImpl(ktFunction) { + companion object : KSObjectCache<KtFunction, KSFunctionDeclarationImpl>() { + fun getCached(ktFunction: KtFunction) = cache.getOrPut(ktFunction) { KSFunctionDeclarationImpl(ktFunction) } + } + + override fun findOverridee(): KSDeclaration? { + val descriptor = ResolverImpl.instance!!.resolveFunctionDeclaration(this) + return (descriptor as? FunctionDescriptor)?.findClosestOverridee()?.toKSDeclaration() + } + + override val simpleName: KSName by lazy { + if (ktFunction is KtConstructor<*>) { + KSNameImpl.getCached("<init>") + } else { + if (ktFunction.name == null) { + throw KSPCompilationError( + ktFunction.containingFile, + ktFunction.startOffset, + "Function declaration must have a name" + ) + } + KSNameImpl.getCached(ktFunction.name!!) + } + } + + override val declarations: Sequence<KSDeclaration> by lazy { + if (!ktFunction.hasBlockBody()) { + emptySequence() + } else { + ktFunction.bodyBlockExpression?.statements?.asSequence()?.getKSDeclarations()?.memoized() ?: emptySequence() + } + } + + override val extensionReceiver: KSTypeReference? by lazy { + if (ktFunction.receiverTypeReference != null) { + KSTypeReferenceImpl.getCached(ktFunction.receiverTypeReference!!) + } else { + null + } + } + + override val functionKind: FunctionKind by lazy { + if (parentDeclaration == null) { + FunctionKind.TOP_LEVEL + } else { + when (ktFunction) { + is KtNamedFunction, is KtConstructor<*> -> FunctionKind.MEMBER + is KtFunctionLiteral -> if (ktFunction.node.findChildByType(KtTokens.FUN_KEYWORD) != null) + FunctionKind.ANONYMOUS else FunctionKind.LAMBDA + else -> throw IllegalStateException("Unexpected psi type ${ktFunction.javaClass}, $ExceptionMessage") + } + } + } + + override val isAbstract: Boolean by lazy { + this.modifiers.contains(Modifier.ABSTRACT) || + ( + (this.parentDeclaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE && + !this.ktFunction.hasBody() + ) + } + + override val parameters: List<KSValueParameter> by lazy { + ktFunction.valueParameters.map { KSValueParameterImpl.getCached(it) } + } + + override val returnType: KSTypeReference by lazy { + if (ktFunction.typeReference != null) { + KSTypeReferenceImpl.getCached(ktFunction.typeReference!!) + } else { + KSTypeReferenceDeferredImpl.getCached(this) { + val desc = ResolverImpl.instance!!.resolveDeclaration(ktFunction) as FunctionDescriptor + getKSTypeCached(desc.returnTypeOrNothing) + } + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFunctionDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSFunction = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt new file mode 100644 index 00000000..5100e367 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt @@ -0,0 +1,60 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.symbol.KSFunction +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeParameter + +/** + * Used when `ResolverImpl.asMemberOf` is called with an error type or function declaration cannot be found. + */ +class KSFunctionErrorImpl( + private val declaration: KSFunctionDeclaration +) : KSFunction { + override val isError: Boolean = true + + override val returnType: KSType = KSErrorType + + override val parameterTypes: List<KSType?> + get() = declaration.parameters.map { + KSErrorType + } + override val typeParameters: List<KSTypeParameter> + get() = emptyList() + + override val extensionReceiverType: KSType? + get() = declaration.extensionReceiver?.let { + KSErrorType + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as KSFunctionErrorImpl + + if (declaration != other.declaration) return false + + return true + } + + override fun hashCode(): Int { + return declaration.hashCode() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt new file mode 100644 index 00000000..55eebfbf --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt @@ -0,0 +1,76 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.symbol.KSFunction +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeParameter +import com.google.devtools.ksp.symbol.impl.binary.KSTypeParameterDescriptorImpl +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import java.util.Objects + +class KSFunctionImpl(val descriptor: CallableDescriptor) : KSFunction { + + override val isError: Boolean = false + + private val cachedHashCode by lazy(LazyThreadSafetyMode.PUBLICATION) { + Objects.hash( + returnType ?: 0, + parameterTypes, + typeParameters, + extensionReceiverType ?: 0 + ) + } + + override val returnType by lazy(LazyThreadSafetyMode.PUBLICATION) { + descriptor.returnType?.let(::getKSTypeCached) + } + override val parameterTypes: List<KSType> by lazy(LazyThreadSafetyMode.PUBLICATION) { + descriptor.valueParameters.map { + getKSTypeCached(it.type) + } + } + override val typeParameters: List<KSTypeParameter> by lazy(LazyThreadSafetyMode.PUBLICATION) { + descriptor.typeParameters.map { + KSTypeParameterDescriptorImpl.getCached(it) + } + } + + override val extensionReceiverType: KSType? by lazy(LazyThreadSafetyMode.PUBLICATION) { + descriptor.extensionReceiverParameter?.type?.let(::getKSTypeCached) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as KSFunctionImpl + + if (returnType != other.returnType) return false + if (parameterTypes != other.parameterTypes) return false + if (typeParameters != other.typeParameters) return false + if (extensionReceiverType != other.extensionReceiverType) return false + + return true + } + + override fun hashCode() = cachedHashCode + + override fun toString(): String { + return "KSFunctionImpl(descriptor=$descriptor)" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSNodeKtImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSNodeKtImpl.kt new file mode 100644 index 00000000..8c44b145 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSNodeKtImpl.kt @@ -0,0 +1,12 @@ +package com.google.devtools.ksp.symbol.impl.kotlin + +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.KtElement + +abstract class KSNodeKtImpl(private val element: KtElement) : KSNode { + override val location: Location by lazy { + element.toLocation() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyAccessorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyAccessorImpl.kt new file mode 100644 index 00000000..d7559bf7 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyAccessorImpl.kt @@ -0,0 +1,66 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.processing.impl.findAnnotationFromUseSiteTarget +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.toLocation +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyAccessor + +abstract class KSPropertyAccessorImpl(val ktPropertyAccessor: KtPropertyAccessor) : KSPropertyAccessor { + companion object { + fun getCached(ktPropertyAccessor: KtPropertyAccessor): KSPropertyAccessor { + return if (ktPropertyAccessor.isGetter) { + KSPropertyGetterImpl.getCached(ktPropertyAccessor) + } else { + KSPropertySetterImpl.getCached(ktPropertyAccessor) + } + } + } + override val receiver: KSPropertyDeclaration by lazy { + KSPropertyDeclarationImpl.getCached(ktPropertyAccessor.property as KtProperty) + } + override val annotations: Sequence<KSAnnotation> by lazy { + ktPropertyAccessor.filterUseSiteTargetAnnotations().map { KSAnnotationImpl.getCached(it) } + .plus(this.findAnnotationFromUseSiteTarget()) + } + + override val parent: KSNode? by lazy { + receiver + } + + override val location: Location by lazy { + ktPropertyAccessor.toLocation() + } + + override val modifiers: Set<Modifier> by lazy { + ktPropertyAccessor.toKSModifiers() + } + + override val origin: Origin = Origin.KOTLIN + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyAccessor(this, data) + } + + internal val originalAnnotations: List<KSAnnotation> by lazy { + ktPropertyAccessor.annotationEntries.map { KSAnnotationImpl.getCached(it) } + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationImpl.kt new file mode 100644 index 00000000..312b73d1 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationImpl.kt @@ -0,0 +1,129 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.KSPropertyGetterDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSPropertySetterDescriptorImpl +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtAnnotated +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration +import org.jetbrains.kotlin.resolve.BindingContext + +class KSPropertyDeclarationImpl private constructor(val ktProperty: KtProperty) : + KSPropertyDeclaration, + KSDeclarationImpl(ktProperty), + KSExpectActual by KSExpectActualImpl(ktProperty) { + companion object : KSObjectCache<KtProperty, KSPropertyDeclarationImpl>() { + fun getCached(ktProperty: KtProperty) = cache.getOrPut(ktProperty) { KSPropertyDeclarationImpl(ktProperty) } + } + + private val propertyDescriptor by lazy { + ResolverImpl.instance!!.resolveDeclaration(ktProperty) as? PropertyDescriptor + } + + override val annotations: Sequence<KSAnnotation> by lazy { + ktProperty.filterUseSiteTargetAnnotations().map { KSAnnotationImpl.getCached(it) } + } + + override val extensionReceiver: KSTypeReference? by lazy { + if (ktProperty.isExtensionDeclaration()) { + KSTypeReferenceImpl.getCached(ktProperty.receiverTypeReference!!) + } else { + null + } + } + + override val isMutable: Boolean by lazy { + ktProperty.isVar + } + + override val hasBackingField: Boolean by lazy { + // taken from: https://github.com/JetBrains/kotlin/blob/master/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt#L104 + when { + ktProperty.initializer != null -> true + ktProperty.hasModifier(KtTokens.LATEINIT_KEYWORD) -> true + else -> { + val context = ResolverImpl.instance!!.bindingTrace.bindingContext + val descriptor = ResolverImpl.instance!!.resolveDeclaration(ktProperty) + descriptor is PropertyDescriptor && context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] == true + } + } + } + + override val getter: KSPropertyGetter? by lazy { + ktProperty.getter?.let { + KSPropertyGetterImpl.getCached(it) + } ?: propertyDescriptor?.getter?.let { + KSPropertyGetterDescriptorImpl.getCached(it) + } + } + + override val setter: KSPropertySetter? by lazy { + ktProperty.setter?.let { + KSPropertySetterImpl.getCached(it) + } ?: propertyDescriptor?.setter?.let { + KSPropertySetterDescriptorImpl.getCached(it) + } + } + + override val type: KSTypeReference by lazy { + if (ktProperty.typeReference != null) { + KSTypeReferenceImpl.getCached(ktProperty.typeReference!!) + } else { + KSTypeReferenceDeferredImpl.getCached(this) { + val desc = propertyDescriptor as? VariableDescriptorWithAccessors + if (desc == null) { + KSErrorType + } else { + getKSTypeCached(desc.type) + } + } + } + } + + override fun isDelegated(): Boolean = ktProperty.hasDelegate() + + override fun findOverridee(): KSPropertyDeclaration? { + return propertyDescriptor?.findClosestOverridee()?.toKSPropertyDeclaration() + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSType = + ResolverImpl.instance!!.asMemberOf(this, containing) +} + +internal fun KtAnnotated.filterUseSiteTargetAnnotations(): Sequence<KtAnnotationEntry> { + return this.annotationEntries.asSequence().filter { property -> + property.useSiteTarget?.getAnnotationUseSiteTarget()?.let { + it != AnnotationUseSiteTarget.PROPERTY_GETTER && it != AnnotationUseSiteTarget.PROPERTY_SETTER && + it != AnnotationUseSiteTarget.SETTER_PARAMETER && it != AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER + } ?: true + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationParameterImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationParameterImpl.kt new file mode 100644 index 00000000..1e69d243 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationParameterImpl.kt @@ -0,0 +1,107 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.isPrivate +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertyGetterSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertySetterSyntheticImpl +import org.jetbrains.kotlin.psi.KtParameter + +class KSPropertyDeclarationParameterImpl private constructor(val ktParameter: KtParameter) : + KSPropertyDeclaration, + KSDeclarationImpl(ktParameter), + KSExpectActual by KSExpectActualImpl(ktParameter) { + companion object : KSObjectCache<KtParameter, KSPropertyDeclarationParameterImpl>() { + fun getCached(ktParameter: KtParameter) = cache.getOrPut(ktParameter) { + KSPropertyDeclarationParameterImpl(ktParameter) + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + ktParameter.filterUseSiteTargetAnnotations().map { KSAnnotationImpl.getCached(it) } + .filterNot { valueParameterAnnotation -> + valueParameterAnnotation.useSiteTarget == AnnotationUseSiteTarget.PARAM || + ( + valueParameterAnnotation.annotationType.resolve() + .declaration.annotations.any { metaAnnotation -> + metaAnnotation.annotationType.resolve().declaration.qualifiedName + ?.asString() == "kotlin.annotation.Target" && + (metaAnnotation.arguments.singleOrNull()?.value as? ArrayList<*>)?.any { + (it as? KSType)?.declaration?.qualifiedName + ?.asString() == "kotlin.annotation.AnnotationTarget.VALUE_PARAMETER" + } ?: false + } && valueParameterAnnotation.useSiteTarget == null + ) + } + } + + override val parentDeclaration: KSDeclaration? by lazy { + ktParameter.findParentDeclaration()!!.parentDeclaration + } + + override val hasBackingField: Boolean + get() = true + + override val extensionReceiver: KSTypeReference? = null + + override val isMutable: Boolean by lazy { + ktParameter.isMutable + } + + override val getter: KSPropertyGetter? by lazy { + if (this.isPrivate()) { + null + } else { + KSPropertyGetterSyntheticImpl.getCached(this) + } + } + + override val setter: KSPropertySetter? by lazy { + if (ktParameter.isMutable && !this.isPrivate()) { + KSPropertySetterSyntheticImpl.getCached(this) + } else { + null + } + } + + override val type: KSTypeReference by lazy { + if (ktParameter.typeReference != null) { + KSTypeReferenceImpl.getCached(ktParameter.typeReference!!) + } else { + throw IllegalStateException("properties in parameter must have explicit type") + } + } + + override fun isDelegated(): Boolean = false + + override fun findOverridee(): KSPropertyDeclaration? { + return ResolverImpl.instance!!.resolvePropertyDeclaration(this)?.original + ?.findClosestOverridee()?.toKSPropertyDeclaration() + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyDeclaration(this, data) + } + + override fun asMemberOf(containing: KSType): KSType = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyGetterImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyGetterImpl.kt new file mode 100644 index 00000000..bf328c29 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyGetterImpl.kt @@ -0,0 +1,53 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.binary.KSTypeReferenceDescriptorImpl +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.psi.KtPropertyAccessor + +class KSPropertyGetterImpl private constructor(ktPropertyGetter: KtPropertyAccessor) : + KSPropertyAccessorImpl(ktPropertyGetter), + KSPropertyGetter { + companion object : KSObjectCache<KtPropertyAccessor, KSPropertyGetterImpl>() { + fun getCached(ktPropertyGetter: KtPropertyAccessor) = cache.getOrPut(ktPropertyGetter) { + KSPropertyGetterImpl(ktPropertyGetter) + } + } + + override val returnType: KSTypeReference? by lazy { + val property = ktPropertyGetter.property + if (property.typeReference != null) { + KSTypeReferenceImpl.getCached(property.typeReference!!) + } else { + val desc = ResolverImpl.instance!!.resolveDeclaration(property) as PropertyDescriptor + KSTypeReferenceDescriptorImpl.getCached(desc.returnType!!, origin, this) + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyGetter(this, data) + } + + override fun toString(): String { + return "$receiver.getter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertySetterImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertySetterImpl.kt new file mode 100644 index 00000000..177567b0 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertySetterImpl.kt @@ -0,0 +1,50 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.impl.synthetic.KSValueParameterSyntheticImpl +import org.jetbrains.kotlin.psi.KtPropertyAccessor + +class KSPropertySetterImpl private constructor(ktPropertySetter: KtPropertyAccessor) : + KSPropertyAccessorImpl(ktPropertySetter), + KSPropertySetter { + companion object : KSObjectCache<KtPropertyAccessor, KSPropertySetterImpl>() { + fun getCached(ktPropertySetter: KtPropertyAccessor) = cache.getOrPut(ktPropertySetter) { + KSPropertySetterImpl(ktPropertySetter) + } + } + + override val parameter: KSValueParameter by lazy { + ktPropertySetter.parameterList?.parameters?.singleOrNull()?.let { KSValueParameterImpl.getCached(it) } + ?: KSValueParameterSyntheticImpl.getCached(this) { + ResolverImpl.instance!!.resolvePropertyAccessorDeclaration(this) + ?.valueParameters?.singleOrNull() + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertySetter(this, data) + } + + override fun toString(): String { + return "$receiver.setter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeAliasImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeAliasImpl.kt new file mode 100644 index 00000000..7ccbcec6 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeAliasImpl.kt @@ -0,0 +1,44 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.symbol.* +import org.jetbrains.kotlin.psi.* + +class KSTypeAliasImpl private constructor(val ktTypeAlias: KtTypeAlias) : + KSTypeAlias, + KSDeclarationImpl(ktTypeAlias), + KSExpectActual by KSExpectActualImpl(ktTypeAlias) { + companion object : KSObjectCache<KtTypeAlias, KSTypeAliasImpl>() { + fun getCached(ktTypeAlias: KtTypeAlias) = cache.getOrPut(ktTypeAlias) { KSTypeAliasImpl(ktTypeAlias) } + } + + override val name: KSName by lazy { + KSNameImpl.getCached(ktTypeAlias.name!!) + } + + override val type: KSTypeReference by lazy { + KSTypeReferenceImpl.getCached(ktTypeAlias.getTypeReference()!!) + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeAlias(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentImpl.kt new file mode 100644 index 00000000..d3cb3975 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentImpl.kt @@ -0,0 +1,83 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.findParentOfType +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSTypeArgument +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.Variance +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.psi.KtProjectionKind +import org.jetbrains.kotlin.psi.KtTypeProjection +import org.jetbrains.kotlin.psi.KtUserType + +abstract class KSTypeArgumentImpl : KSTypeArgument { + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeArgument(this, data) + } + + override fun toString(): String { + return "$variance $type" + } +} + +class KSTypeArgumentKtImpl private constructor(val ktTypeArgument: KtTypeProjection) : KSTypeArgumentImpl() { + companion object : KSObjectCache<KtTypeProjection, KSTypeArgumentKtImpl>() { + fun getCached(ktTypeArgument: KtTypeProjection) = cache.getOrPut(ktTypeArgument) { + KSTypeArgumentKtImpl(ktTypeArgument) + } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + ktTypeArgument.toLocation() + } + + override val parent: KSNode? by lazy { + ktTypeArgument.findParentOfType<KtUserType>()?.let { KSClassifierReferenceImpl.getCached(it) } + } + + override val variance: Variance by lazy { + when (ktTypeArgument.projectionKind) { + KtProjectionKind.STAR -> Variance.STAR + KtProjectionKind.IN -> Variance.CONTRAVARIANT + KtProjectionKind.NONE -> Variance.INVARIANT + KtProjectionKind.OUT -> Variance.COVARIANT + } + } + + override val type: KSTypeReference? by lazy { + if (ktTypeArgument.typeReference != null) { + KSTypeReferenceImpl.getCached(ktTypeArgument.typeReference!!) + } else { + null + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + ktTypeArgument.annotationEntries.asSequence().map { KSAnnotationImpl.getCached(it) }.memoized() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentLiteImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentLiteImpl.kt new file mode 100644 index 00000000..d080fd87 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeArgumentLiteImpl.kt @@ -0,0 +1,56 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.findParentOfType +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.Variance +import org.jetbrains.kotlin.psi.KtFunctionType +import org.jetbrains.kotlin.psi.KtTypeReference + +class KSTypeArgumentLiteImpl private constructor(override val type: KSTypeReference, override val variance: Variance) : + KSTypeArgumentImpl() { + companion object : KSObjectCache<Pair<KSTypeReference, Variance>, KSTypeArgumentLiteImpl>() { + fun getCached(type: KSTypeReference, variance: Variance) = cache.getOrPut(Pair(type, variance)) { + KSTypeArgumentLiteImpl(type, variance) + } + + fun getCached(type: KtTypeReference) = cache.getOrPut( + Pair(KSTypeReferenceImpl.getCached(type), Variance.INVARIANT) + ) { + KSTypeArgumentLiteImpl(KSTypeReferenceImpl.getCached(type), Variance.INVARIANT) + } + } + + override val origin = Origin.KOTLIN + + override val location: Location = NonExistLocation + + override val parent: KSNode? by lazy { + (type as? KSTypeReferenceImpl)?.ktTypeReference + ?.findParentOfType<KtFunctionType>()?.let { KSCallableReferenceImpl.getCached(it) } + } + + override val annotations: Sequence<KSAnnotation> = type.annotations +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt new file mode 100644 index 00000000..9e8929a8 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt @@ -0,0 +1,154 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.IdKey +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeArgument +import com.google.devtools.ksp.symbol.Nullability +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.binary.KSAnnotationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSTypeArgumentDescriptorImpl +import com.google.devtools.ksp.symbol.impl.convertKotlinType +import com.google.devtools.ksp.symbol.impl.replaceTypeArguments +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.builtins.isKFunctionType +import org.jetbrains.kotlin.builtins.isKSuspendFunctionType +import org.jetbrains.kotlin.builtins.isSuspendFunctionType +import org.jetbrains.kotlin.descriptors.NotFoundClasses +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.TypeNullability +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable +import org.jetbrains.kotlin.types.typeUtil.makeNullable +import org.jetbrains.kotlin.types.typeUtil.nullability +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections + +class KSTypeImpl private constructor( + val kotlinType: KotlinType, + private val ksTypeArguments: List<KSTypeArgument>? = null, + override val annotations: Sequence<KSAnnotation> = sequenceOf() +) : KSType { + companion object : KSObjectCache<IdKey<KotlinType>, KSTypeImpl>() { + fun getCached( + kotlinType: KotlinType, + ksTypeArguments: List<KSTypeArgument>? = null, + annotations: Sequence<KSAnnotation> = sequenceOf() + ): KSTypeImpl { + return cache.getOrPut(IdKey(kotlinType)) { KSTypeImpl(kotlinType, ksTypeArguments, annotations) } + } + } + + override val declaration: KSDeclaration by lazy { + ResolverImpl.instance!!.findDeclaration(kotlinType.getAbbreviation() ?: kotlinType) + } + + override val nullability: Nullability by lazy { + when (kotlinType.nullability()) { + TypeNullability.NULLABLE -> Nullability.NULLABLE + TypeNullability.NOT_NULL -> Nullability.NOT_NULL + TypeNullability.FLEXIBLE -> Nullability.PLATFORM + } + } + + // TODO: fix calls to getKSTypeCached and use ksTypeArguments when available. + override val arguments: List<KSTypeArgument> by lazy { + (kotlinType.getAbbreviation() ?: kotlinType).arguments.map { + KSTypeArgumentDescriptorImpl.getCached(it, Origin.SYNTHETIC, null) + } + } + + override fun isAssignableFrom(that: KSType): Boolean { + val subType = (that as? KSTypeImpl)?.kotlinType?.convertKotlinType() ?: return false + ResolverImpl.instance!!.incrementalContext.recordLookupWithSupertypes(subType) + val thisType = (this as? KSTypeImpl)?.kotlinType?.convertKotlinType() ?: return false + return subType.isSubtypeOf(thisType) + } + + // TODO: find a better way to reuse the logic in [DescriptorRendererImpl.renderFlexibleType]. + override fun isMutabilityFlexible(): Boolean { + return kotlinType.toString().startsWith("(Mutable)") + } + + // TODO: find a better way to reuse the logic in [DescriptorRendererImpl.renderFlexibleType]. + override fun isCovarianceFlexible(): Boolean { + return kotlinType.toString().startsWith("Array<(out) ") + } + + override fun replace(arguments: List<KSTypeArgument>): KSType { + return kotlinType.replaceTypeArguments(arguments)?.let { + getKSTypeCached(it, arguments) + } ?: KSErrorType + } + + override fun starProjection(): KSType { + return getKSTypeCached(kotlinType.replaceArgumentsWithStarProjections()) + } + + private val meNullable: KSType by lazy { getKSTypeCached(kotlinType.makeNullable()) } + override fun makeNullable(): KSType = meNullable + + private val meNotNullable: KSType by lazy { getKSTypeCached(kotlinType.makeNotNullable()) } + override fun makeNotNullable(): KSType = meNotNullable + + override val isMarkedNullable: Boolean = kotlinType.isMarkedNullable + + override val isError: Boolean = false + + override fun equals(other: Any?): Boolean { + if (other !is KSTypeImpl) + return false + return kotlinType.equals(other.kotlinType) + } + + override fun hashCode(): Int = kotlinType.hashCode() + + override fun toString(): String = (kotlinType.getAbbreviation() ?: kotlinType).toString() + + override val isFunctionType: Boolean + get() = kotlinType.isFunctionType || kotlinType.isKFunctionType + + override val isSuspendFunctionType: Boolean + get() = kotlinType.isSuspendFunctionType || kotlinType.isKSuspendFunctionType +} + +fun getKSTypeCached( + kotlinType: KotlinType, + ksTypeArguments: List<KSTypeArgument>? = null, + annotations: Sequence<KSAnnotation> = sequenceOf() +): KSType { + return if (kotlinType.isError || + kotlinType.constructor.declarationDescriptor is NotFoundClasses.MockClassDescriptor + ) { + KSErrorType + } else { + KSTypeImpl.getCached( + kotlinType, + ksTypeArguments, + annotations + kotlinType.annotations + .filter { it.source == SourceElement.NO_SOURCE } + .map { KSAnnotationDescriptorImpl.getCached(it, null) } + .asSequence() + ) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeParameterImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeParameterImpl.kt new file mode 100644 index 00000000..340833a4 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeParameterImpl.kt @@ -0,0 +1,93 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSExpectActual +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSTypeParameter +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Variance +import com.google.devtools.ksp.symbol.impl.synthetic.KSTypeReferenceSyntheticImpl +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.psi.KtTypeParameterListOwner + +class KSTypeParameterImpl private constructor(val ktTypeParameter: KtTypeParameter) : + KSTypeParameter, + KSDeclarationImpl(ktTypeParameter), + KSExpectActual by KSExpectActualNoImpl() { + companion object : KSObjectCache<KtTypeParameter, KSTypeParameterImpl>() { + fun getCached(ktTypeParameter: KtTypeParameter) = + cache.getOrPut(ktTypeParameter) { KSTypeParameterImpl(ktTypeParameter) } + } + + override val name: KSName by lazy { + KSNameImpl.getCached(ktTypeParameter.name!!) + } + + override val isReified: Boolean by lazy { + ktTypeParameter.modifierList?.hasModifier(KtTokens.REIFIED_KEYWORD) ?: false + } + + override val variance: Variance by lazy { + when { + ktTypeParameter.modifierList == null -> Variance.INVARIANT + ktTypeParameter.modifierList!!.hasModifier(KtTokens.OUT_KEYWORD) -> Variance.COVARIANT + ktTypeParameter.modifierList!!.hasModifier(KtTokens.IN_KEYWORD) -> Variance.CONTRAVARIANT + else -> Variance.INVARIANT + } + } + + private val owner: KtTypeParameterListOwner by lazy { + (parentDeclaration as KSDeclarationImpl).ktDeclaration as KtTypeParameterListOwner + } + + override val bounds: Sequence<KSTypeReference> by lazy { + val list = sequenceOf(ktTypeParameter.extendsBound) + list.plus( + owner.typeConstraints + .filter { + it.subjectTypeParameterName!!.getReferencedName() == ktTypeParameter.nameAsSafeName.asString() + } + .map { it.boundTypeReference } + ).filterNotNull().map { KSTypeReferenceImpl.getCached(it) }.ifEmpty { + sequenceOf( + KSTypeReferenceSyntheticImpl.getCached(ResolverImpl.instance!!.builtIns.anyType.makeNullable(), this) + ) + }.memoized() + } + + override val qualifiedName: KSName? by lazy { + KSNameImpl.getCached("${this.parentDeclaration!!.qualifiedName!!.asString()}.${simpleName.asString()}") + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached(ktTypeParameter.name ?: "_") + } + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeParameter(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceDeferredImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceDeferredImpl.kt new file mode 100644 index 00000000..16a74374 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceDeferredImpl.kt @@ -0,0 +1,69 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.getInstanceForCurrentRound + +class KSTypeReferenceDeferredImpl private constructor( + private val resolver: () -> KSType, + override val parent: KSNode? +) : KSTypeReference { + companion object : KSObjectCache<KSDeclaration, KSTypeReferenceDeferredImpl>() { + fun getCached(parent: KSDeclaration, resolver: () -> KSType): KSTypeReferenceDeferredImpl { + val currentParent = parent.getInstanceForCurrentRound() as KSDeclaration + return cache + .getOrPut(currentParent) { KSTypeReferenceDeferredImpl(resolver, currentParent) } + } + } + + override val origin = Origin.KOTLIN + + override val location: Location = NonExistLocation + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val element: KSReferenceElement? = null + + override val modifiers: Set<Modifier> = emptySet() + + private val resolved: KSType by lazy { + resolver() + } + + override fun resolve(): KSType = resolved + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun toString(): String { + return resolved.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceImpl.kt new file mode 100644 index 00000000..9f751700 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeReferenceImpl.kt @@ -0,0 +1,129 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.toLocation +import com.google.devtools.ksp.toKSModifiers +import org.jetbrains.kotlin.psi.* + +class KSTypeReferenceImpl private constructor(val ktTypeReference: KtTypeReference) : KSTypeReference { + companion object : KSObjectCache<KtTypeReference, KSTypeReferenceImpl>() { + fun getCached(ktTypeReference: KtTypeReference) = cache.getOrPut(ktTypeReference) { + KSTypeReferenceImpl(ktTypeReference) + } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + ktTypeReference.toLocation() + } + override val parent: KSNode? by lazy { + var parentPsi = ktTypeReference.parent + while ( + parentPsi != null && parentPsi !is KtAnnotationEntry && parentPsi !is KtFunctionType && + parentPsi !is KtClassOrObject && parentPsi !is KtFunction && parentPsi !is KtUserType && + parentPsi !is KtProperty && parentPsi !is KtTypeAlias && parentPsi !is KtTypeProjection && + parentPsi !is KtTypeParameter && parentPsi !is KtParameter + ) { + parentPsi = parentPsi.parent + } + when (parentPsi) { + is KtAnnotationEntry -> KSAnnotationImpl.getCached(parentPsi) + is KtFunctionType -> KSCallableReferenceImpl.getCached(parentPsi) + is KtClassOrObject -> KSClassDeclarationImpl.getCached(parentPsi) + is KtFunction -> KSFunctionDeclarationImpl.getCached(parentPsi) + is KtUserType -> KSClassifierReferenceImpl.getCached(parentPsi) + is KtProperty -> KSPropertyDeclarationImpl.getCached(parentPsi) + is KtTypeAlias -> KSTypeAliasImpl.getCached(parentPsi) + is KtTypeProjection -> KSTypeArgumentKtImpl.getCached(parentPsi) + is KtTypeParameter -> KSTypeParameterImpl.getCached(parentPsi) + is KtParameter -> KSValueParameterImpl.getCached(parentPsi) + else -> null + } + } + + // Parenthesized type in grammar seems to be implemented as KtNullableType. + private fun visitNullableType(visit: (KtNullableType) -> Unit) { + var typeElement = ktTypeReference.typeElement + while (typeElement is KtNullableType) { + visit(typeElement) + typeElement = typeElement.innerType + } + } + + // Annotations and modifiers are only allowed in one of the parenthesized type. + // https://github.com/JetBrains/kotlin/blob/50e12239ef8141a45c4dca2bf0544be6191ecfb6/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java#L608 + override val annotations: Sequence<KSAnnotation> by lazy { + fun List<KtAnnotationEntry>.toKSAnnotations(): Sequence<KSAnnotation> = + asSequence().map { + KSAnnotationImpl.getCached(it) + } + + val innerAnnotations = mutableListOf<Sequence<KSAnnotation>>() + visitNullableType { + innerAnnotations.add(it.annotationEntries.toKSAnnotations()) + } + + (ktTypeReference.annotationEntries.toKSAnnotations() + innerAnnotations.asSequence().flatten()).memoized() + } + + override val modifiers: Set<Modifier> by lazy { + val innerModifiers = mutableSetOf<Modifier>() + visitNullableType { + innerModifiers.addAll(it.modifierList.toKSModifiers()) + } + ktTypeReference.toKSModifiers() + innerModifiers + } + + override val element: KSReferenceElement by lazy { + var typeElement = ktTypeReference.typeElement + while (typeElement is KtNullableType) + typeElement = typeElement.innerType + when (typeElement) { + is KtFunctionType -> KSCallableReferenceImpl.getCached(typeElement) + is KtUserType -> KSClassifierReferenceImpl.getCached(typeElement) + is KtDynamicType -> KSDynamicReferenceImpl.getCached(this) + is KtIntersectionType -> KSDefNonNullReferenceImpl.getCached(typeElement) + else -> throw IllegalStateException("Unexpected type element ${typeElement?.javaClass}, $ExceptionMessage") + } + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun resolve(): KSType = ResolverImpl.instance!!.resolveUserType(this) + + override fun toString(): String { + return element.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueArgumentImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueArgumentImpl.kt new file mode 100644 index 00000000..aac1f416 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueArgumentImpl.kt @@ -0,0 +1,70 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSValueArgument +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin + +class KSValueArgumentLiteImpl private constructor( + override val name: KSName, + override val value: Any?, + override val parent: KSNode?, + override val origin: Origin +) : KSValueArgumentImpl() { + companion object : KSObjectCache<Triple<KSName, Any?, KSNode>, KSValueArgumentLiteImpl>() { + + fun getCached(name: KSName, value: Any?, parent: KSNode, origin: Origin = Origin.KOTLIN) = cache + .getOrPut(Triple(name, value, parent)) { + KSValueArgumentLiteImpl(name, value, parent, origin) + } + } + + override val location: Location = NonExistLocation + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val isSpread: Boolean = false +} + +abstract class KSValueArgumentImpl : KSValueArgument { + override fun hashCode(): Int { + return name.hashCode() * 31 + value.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (other !is KSValueArgument) + return false + + return other.name == this.name && other.value == this.value + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitValueArgument(this, data) + } + + override fun toString(): String { + return "${name?.asString() ?: ""}:$value" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueParameterImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueParameterImpl.kt new file mode 100644 index 00000000..162d77fa --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueParameterImpl.kt @@ -0,0 +1,131 @@ +/* + * 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.symbol.impl.kotlin + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.memoized +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.findAnnotationFromUseSiteTarget +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSValueParameter +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.synthetic.KSTypeReferenceSyntheticImpl +import com.google.devtools.ksp.symbol.impl.toLocation +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.lexer.KtTokens.CROSSINLINE_KEYWORD +import org.jetbrains.kotlin.lexer.KtTokens.NOINLINE_KEYWORD +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtFunctionType +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyAccessor + +class KSValueParameterImpl private constructor(val ktParameter: KtParameter) : KSValueParameter { + companion object : KSObjectCache<KtParameter, KSValueParameterImpl>() { + fun getCached(ktParameter: KtParameter) = cache.getOrPut(ktParameter) { KSValueParameterImpl(ktParameter) } + } + + override val origin = Origin.KOTLIN + + override val location: Location by lazy { + ktParameter.toLocation() + } + + override val parent: KSNode? by lazy { + var parentPsi = ktParameter.parent + while ( + parentPsi != null && parentPsi !is KtAnnotationEntry && parentPsi !is KtFunctionType && + parentPsi !is KtFunction && parentPsi !is KtPropertyAccessor + ) { + parentPsi = parentPsi.parent + } + when (parentPsi) { + is KtAnnotationEntry -> KSAnnotationImpl.getCached(parentPsi) + is KtFunctionType -> KSCallableReferenceImpl.getCached(parentPsi) + is KtFunction -> KSFunctionDeclarationImpl.getCached(parentPsi) + is KtPropertyAccessor -> if (parentPsi.isSetter) KSPropertySetterImpl.getCached(parentPsi) else null + else -> null + } + } + + override val annotations: Sequence<KSAnnotation> by lazy { + ktParameter.annotationEntries.asSequence().filter { annotation -> + annotation.useSiteTarget?.getAnnotationUseSiteTarget()?.let { + it != AnnotationUseSiteTarget.PROPERTY_GETTER && + it != AnnotationUseSiteTarget.PROPERTY_SETTER && + it != AnnotationUseSiteTarget.SETTER_PARAMETER && + it != AnnotationUseSiteTarget.FIELD + } ?: true + }.map { KSAnnotationImpl.getCached(it) }.filterNot { valueParameterAnnotation -> + valueParameterAnnotation.annotationType.resolve().declaration.annotations.any { metaAnnotation -> + metaAnnotation.annotationType.resolve().declaration.qualifiedName + ?.asString() == "kotlin.annotation.Target" && + (metaAnnotation.arguments.singleOrNull()?.value as? ArrayList<*>)?.none { + (it as? KSType)?.declaration?.qualifiedName + ?.asString() == "kotlin.annotation.AnnotationTarget.VALUE_PARAMETER" + } ?: false + } + } + .plus(this.findAnnotationFromUseSiteTarget()).memoized() + } + + override val isCrossInline: Boolean = ktParameter.hasModifier(CROSSINLINE_KEYWORD) + + override val isNoInline: Boolean = ktParameter.hasModifier(NOINLINE_KEYWORD) + + override val isVararg: Boolean = ktParameter.isVarArg + + override val isVal = ktParameter.hasValOrVar() && !ktParameter.isMutable + + override val isVar = ktParameter.hasValOrVar() && ktParameter.isMutable + + override val name: KSName? by lazy { + if (ktParameter.name == null) { + null + } else { + KSNameImpl.getCached(ktParameter.name!!) + } + } + + override val type: KSTypeReference by lazy { + ktParameter.typeReference?.let { KSTypeReferenceImpl.getCached(it) } + ?: findPropertyForAccessor()?.type ?: KSTypeReferenceSyntheticImpl.getCached(KSErrorType, this) + } + + override val hasDefault: Boolean = ktParameter.hasDefaultValue() + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitValueParameter(this, data) + } + + override fun toString(): String { + return name?.asString() ?: "_" + } + + private fun findPropertyForAccessor(): KSPropertyDeclaration? { + return (ktParameter.parent?.parent?.parent as? KtProperty)?.let { KSPropertyDeclarationImpl.getCached(it) } + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt new file mode 100644 index 00000000..19108d99 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt @@ -0,0 +1,119 @@ +/* + * 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.symbol.impl.synthetic + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.isPublic +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* + +class KSConstructorSyntheticImpl private constructor(val ksClassDeclaration: KSClassDeclaration) : + KSFunctionDeclaration, + KSDeclaration + by ksClassDeclaration { + companion object : KSObjectCache<KSClassDeclaration, KSConstructorSyntheticImpl>() { + fun getCached(ksClassDeclaration: KSClassDeclaration) = + KSConstructorSyntheticImpl.cache.getOrPut(ksClassDeclaration) { + KSConstructorSyntheticImpl(ksClassDeclaration) + } + } + + override val isAbstract: Boolean = false + + override val extensionReceiver: KSTypeReference? = null + + override val parameters: List<KSValueParameter> = emptyList() + + override val functionKind: FunctionKind = FunctionKind.MEMBER + + override val qualifiedName: KSName? by lazy { + KSNameImpl.getCached(ksClassDeclaration.qualifiedName?.asString()?.plus(".<init>") ?: "") + } + + override val simpleName: KSName by lazy { + KSNameImpl.getCached("<init>") + } + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override val containingFile: KSFile? by lazy { + ksClassDeclaration.containingFile + } + + override val parentDeclaration: KSDeclaration? by lazy { + ksClassDeclaration + } + + override val parent: KSNode? by lazy { + parentDeclaration + } + + override val returnType: KSTypeReference by lazy { + KSTypeReferenceSyntheticImpl( + ksClassDeclaration.asStarProjectedType(), this + ) + } + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val isActual: Boolean = false + + override val isExpect: Boolean = false + + override val declarations: Sequence<KSDeclaration> = emptySequence() + + override val location: Location by lazy { + ksClassDeclaration.location + } + + override val modifiers: Set<Modifier> by lazy { + if (ksClassDeclaration.classKind == ClassKind.ENUM_CLASS) { + return@lazy setOf(Modifier.FINAL, Modifier.PRIVATE) + } + // add public if parent class is public + if (ksClassDeclaration.isPublic()) { + setOf(Modifier.FINAL, Modifier.PUBLIC) + } else { + setOf(Modifier.FINAL) + } + } + + override val origin: Origin = Origin.SYNTHETIC + + override fun findOverridee(): KSFunctionDeclaration? = null + + override fun findActuals(): Sequence<KSDeclaration> { + return emptySequence() + } + + override fun findExpects(): Sequence<KSDeclaration> { + return emptySequence() + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitFunctionDeclaration(this, data) + } + + override fun toString(): String { + return "synthetic constructor for ${this.parentDeclaration}" + } + + override fun asMemberOf(containing: KSType): KSFunction = + ResolverImpl.instance!!.asMemberOf(this, containing) +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt new file mode 100644 index 00000000..2a71233b --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt @@ -0,0 +1,96 @@ +/* + * 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.symbol.impl.synthetic + +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.* + +object KSErrorTypeClassDeclaration : KSClassDeclaration { + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val classKind: ClassKind = ClassKind.CLASS + + override val containingFile: KSFile? = null + + override val declarations: Sequence<KSDeclaration> = emptySequence() + + override val isActual: Boolean = false + + override val isExpect: Boolean = false + + override val isCompanionObject: Boolean = false + + override val location: Location = NonExistLocation + + override val parent: KSNode? = null + + override val modifiers: Set<Modifier> = emptySet() + + override val origin: Origin = Origin.SYNTHETIC + + override val packageName: KSName = KSNameImpl.getCached("") + + override val parentDeclaration: KSDeclaration? = null + + override val primaryConstructor: KSFunctionDeclaration? = null + + override val qualifiedName: KSName? = null + + override val simpleName: KSName = KSNameImpl.getCached("<Error>") + + override val superTypes: Sequence<KSTypeReference> = emptySequence() + + override val typeParameters: List<KSTypeParameter> = emptyList() + + override fun getSealedSubclasses(): Sequence<KSClassDeclaration> = emptySequence() + + override fun asStarProjectedType(): KSType { + return ResolverImpl.instance!!.builtIns.nothingType + } + + override fun asType(typeArguments: List<KSTypeArgument>): KSType { + return ResolverImpl.instance!!.builtIns.nothingType + } + + override fun findActuals(): Sequence<KSDeclaration> { + return emptySequence() + } + + override fun findExpects(): Sequence<KSDeclaration> { + return emptySequence() + } + + override fun getAllFunctions(): Sequence<KSFunctionDeclaration> { + return emptySequence() + } + + override fun getAllProperties(): Sequence<KSPropertyDeclaration> { + return emptySequence() + } + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitClassDeclaration(this, data) + } + + override fun toString(): String { + return "Error type synthetic declaration" + } + + override val docString = null +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyAccessorSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyAccessorSyntheticImpl.kt new file mode 100644 index 00000000..f6f0ad9f --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyAccessorSyntheticImpl.kt @@ -0,0 +1,41 @@ +/* + * 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.symbol.impl.synthetic + +import com.google.devtools.ksp.processing.impl.findAnnotationFromUseSiteTarget +import com.google.devtools.ksp.symbol.* + +abstract class KSPropertyAccessorSyntheticImpl(ksPropertyDeclaration: KSPropertyDeclaration) : KSPropertyAccessor { + override val annotations: Sequence<KSAnnotation> by lazy { + this.findAnnotationFromUseSiteTarget() + } + + override val location: Location by lazy { + ksPropertyDeclaration.location + } + + override val modifiers: Set<Modifier> = emptySet() + + override val origin: Origin = Origin.SYNTHETIC + + override val receiver: KSPropertyDeclaration = ksPropertyDeclaration + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyAccessor(this, data) + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyGetterSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyGetterSyntheticImpl.kt new file mode 100644 index 00000000..70455bb3 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertyGetterSyntheticImpl.kt @@ -0,0 +1,59 @@ +/* + * 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.symbol.impl.synthetic + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSPropertyGetter +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.impl.binary.KSTypeReferenceDescriptorImpl +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor + +class KSPropertyGetterSyntheticImpl(val ksPropertyDeclaration: KSPropertyDeclaration) : + KSPropertyAccessorSyntheticImpl(ksPropertyDeclaration), KSPropertyGetter { + companion object : KSObjectCache<KSPropertyDeclaration, KSPropertyGetterSyntheticImpl>() { + fun getCached(ksPropertyDeclaration: KSPropertyDeclaration) = + KSPropertyGetterSyntheticImpl.cache.getOrPut(ksPropertyDeclaration) { + KSPropertyGetterSyntheticImpl(ksPropertyDeclaration) + } + } + + private val descriptor: PropertyAccessorDescriptor by lazy { + ResolverImpl.instance!!.resolvePropertyDeclaration(ksPropertyDeclaration)!!.getter!! + } + + override val returnType: KSTypeReference? by lazy { + if (descriptor.returnType != null) { + KSTypeReferenceDescriptorImpl.getCached(descriptor.returnType!!, origin, this) + } else { + null + } + } + override val parent: KSNode? = ksPropertyDeclaration + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertyGetter(this, data) + } + + override fun toString(): String { + return "$receiver.getter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertySetterSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertySetterSyntheticImpl.kt new file mode 100644 index 00000000..c8120e55 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSPropertySetterSyntheticImpl.kt @@ -0,0 +1,57 @@ +/* + * 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.symbol.impl.synthetic + +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSPropertySetter +import com.google.devtools.ksp.symbol.KSValueParameter +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.impl.binary.KSValueParameterDescriptorImpl +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor + +class KSPropertySetterSyntheticImpl(val ksPropertyDeclaration: KSPropertyDeclaration) : + KSPropertyAccessorSyntheticImpl(ksPropertyDeclaration), KSPropertySetter { + companion object : KSObjectCache<KSPropertyDeclaration, KSPropertySetterSyntheticImpl>() { + fun getCached(ksPropertyDeclaration: KSPropertyDeclaration) = + KSPropertySetterSyntheticImpl.cache.getOrPut(ksPropertyDeclaration) { + KSPropertySetterSyntheticImpl(ksPropertyDeclaration) + } + } + + private val descriptor: PropertyAccessorDescriptor by lazy { + ResolverImpl.instance!!.resolvePropertyDeclaration(ksPropertyDeclaration)!!.setter!! + } + + override val parameter: KSValueParameter by lazy { + descriptor.valueParameters.singleOrNull()?.let { KSValueParameterDescriptorImpl.getCached(it, this) } + ?: throw IllegalStateException("Failed to resolve property type") + } + + override val parent: KSNode? = ksPropertyDeclaration + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitPropertySetter(this, data) + } + + override fun toString(): String { + return "$receiver.getter()" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSTypeReferenceSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSTypeReferenceSyntheticImpl.kt new file mode 100644 index 00000000..aa2631e4 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSTypeReferenceSyntheticImpl.kt @@ -0,0 +1,43 @@ +package com.google.devtools.ksp.symbol.impl.synthetic + +import com.google.devtools.ksp.IdKey +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSReferenceElement +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin + +class KSTypeReferenceSyntheticImpl(val ksType: KSType, override val parent: KSNode?) : KSTypeReference { + companion object : KSObjectCache<Pair<IdKey<KSType>, KSNode?>, KSTypeReferenceSyntheticImpl>() { + fun getCached(ksType: KSType, parent: KSNode?) = KSTypeReferenceSyntheticImpl.cache + .getOrPut(Pair(IdKey(ksType), parent)) { KSTypeReferenceSyntheticImpl(ksType, parent) } + } + + override val annotations: Sequence<KSAnnotation> = emptySequence() + + override val element: KSReferenceElement? = null + + override val location: Location = NonExistLocation + + override val modifiers: Set<Modifier> = emptySet() + + override val origin: Origin = Origin.SYNTHETIC + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitTypeReference(this, data) + } + + override fun resolve(): KSType { + return ksType + } + + override fun toString(): String { + return ksType.toString() + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSValueParameterSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSValueParameterSyntheticImpl.kt new file mode 100644 index 00000000..6e5692b2 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSValueParameterSyntheticImpl.kt @@ -0,0 +1,76 @@ +package com.google.devtools.ksp.symbol.impl.synthetic + +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KSObjectCache +import com.google.devtools.ksp.processing.impl.KSNameImpl +import com.google.devtools.ksp.processing.impl.findAnnotationFromUseSiteTarget +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSName +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSTypeReference +import com.google.devtools.ksp.symbol.KSValueParameter +import com.google.devtools.ksp.symbol.KSVisitor +import com.google.devtools.ksp.symbol.Location +import com.google.devtools.ksp.symbol.NonExistLocation +import com.google.devtools.ksp.symbol.Origin +import com.google.devtools.ksp.symbol.impl.binary.KSAnnotationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSTypeReferenceDescriptorImpl +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue +import org.jetbrains.kotlin.resolve.calls.components.isVararg + +class KSValueParameterSyntheticImpl(val owner: KSAnnotated?, resolve: () -> ValueParameterDescriptor?) : + KSValueParameter { + + companion object : + KSObjectCache<Pair<KSAnnotated?, () -> ValueParameterDescriptor?>, KSValueParameterSyntheticImpl>() { + fun getCached(owner: KSAnnotated? = null, resolve: () -> ValueParameterDescriptor?) = + KSValueParameterSyntheticImpl.cache.getOrPut(Pair(owner, resolve)) { + KSValueParameterSyntheticImpl(owner, resolve) + } + } + + private val descriptor by lazy { + resolve() ?: throw IllegalStateException("Failed to resolve for synthetic value parameter, $ExceptionMessage") + } + + override val name: KSName? by lazy { + KSNameImpl.getCached(descriptor.name.asString()) + } + + override val type: KSTypeReference by lazy { + KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, this) + } + + override val isVararg: Boolean = descriptor.isVararg + + override val isNoInline: Boolean = descriptor.isNoinline + + override val isCrossInline: Boolean = descriptor.isCrossinline + + override val isVal: Boolean = !descriptor.isVar + + override val isVar: Boolean = descriptor.isVar + + override val hasDefault: Boolean = descriptor.hasDefaultValue() + + override val annotations: Sequence<KSAnnotation> by lazy { + descriptor.annotations.asSequence() + .map { KSAnnotationDescriptorImpl.getCached(it, this) }.plus(this.findAnnotationFromUseSiteTarget()) + } + + override val origin: Origin = Origin.SYNTHETIC + + override val location: Location = NonExistLocation + + override val parent: KSNode? = owner + + override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R { + return visitor.visitValueParameter(this, data) + } + + override fun toString(): String { + return name?.asString() ?: "_" + } +} diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt new file mode 100644 index 00000000..4e19baf1 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt @@ -0,0 +1,559 @@ +/* + * 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.symbol.impl + +import com.google.devtools.ksp.BinaryClassInfoCache +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.processing.impl.workaroundForNested +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.Variance +import com.google.devtools.ksp.symbol.impl.binary.KSClassDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSPropertyDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSTypeArgumentDescriptorImpl +import com.google.devtools.ksp.symbol.impl.java.* +import com.google.devtools.ksp.symbol.impl.kotlin.* +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertyGetterSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertySetterSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSValueParameterSyntheticImpl +import com.intellij.psi.* +import com.intellij.psi.impl.light.LightMethod +import com.intellij.psi.impl.source.PsiClassImpl +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMapper +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl +import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaField +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethodBase +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor +import org.jetbrains.kotlin.types.* +import java.util.* +import kotlin.Comparator +import kotlin.collections.ArrayDeque + +fun PsiElement.findParentAnnotated(): KSAnnotated? { + var parent = when (this) { + // Unfortunately, LightMethod doesn't implement parent. + is LightMethod -> this.containingClass + else -> this.parent + } + + while (parent != null && parent !is KtDeclaration && parent !is KtFile && parent !is PsiClass && + parent !is PsiMethod && parent !is PsiJavaFile && parent !is KtTypeAlias + ) { + parent = parent.parent + } + + return when (parent) { + is KtClassOrObject -> KSClassDeclarationImpl.getCached(parent) + is KtFile -> KSFileImpl.getCached(parent) + is KtFunction -> KSFunctionDeclarationImpl.getCached(parent) + is PsiClass -> KSClassDeclarationJavaImpl.getCached(parent) + is PsiJavaFile -> KSFileJavaImpl.getCached(parent) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(parent) + is KtProperty -> KSPropertyDeclarationImpl.getCached(parent) + is KtPropertyAccessor -> if (parent.isGetter) { + KSPropertyGetterImpl.getCached(parent) + } else { + KSPropertySetterImpl.getCached(parent) + } + is KtTypeAlias -> KSTypeAliasImpl.getCached(parent) + else -> null + } +} + +fun PsiElement.findParentDeclaration(): KSDeclaration? { + return this.findParentAnnotated() as? KSDeclaration +} + +fun PsiElement.toLocation(): Location { + val file = this.containingFile + val document = ResolverImpl.instance!!.psiDocumentManager.getDocument(file) ?: return NonExistLocation + return FileLocation(file.virtualFile.path, document.getLineNumber(this.textOffset) + 1) +} + +// TODO: handle local functions/classes correctly +fun Sequence<KtElement>.getKSDeclarations(): Sequence<KSDeclaration> = + this.mapNotNull { + when (it) { + is KtFunction -> KSFunctionDeclarationImpl.getCached(it) + is KtProperty -> KSPropertyDeclarationImpl.getCached(it) + is KtClassOrObject -> KSClassDeclarationImpl.getCached(it) + is KtTypeAlias -> KSTypeAliasImpl.getCached(it) + else -> null + } + } + +fun List<PsiElement>.getKSJavaDeclarations() = + this.mapNotNull { + when (it) { + is PsiClass -> KSClassDeclarationJavaImpl.getCached(it) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(it) + is PsiField -> KSPropertyDeclarationJavaImpl.getCached(it) + else -> null + } + } + +fun org.jetbrains.kotlin.types.Variance.toKSVariance(): Variance { + return when (this) { + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Variance.CONTRAVARIANT + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Variance.COVARIANT + org.jetbrains.kotlin.types.Variance.INVARIANT -> Variance.INVARIANT + else -> throw IllegalStateException("Unexpected variance value $this, $ExceptionMessage") + } +} + +private fun KSTypeReference.toKotlinType() = (resolve() as? KSTypeImpl)?.kotlinType + +// returns null if error +internal fun KotlinType.replaceTypeArguments(newArguments: List<KSTypeArgument>): KotlinType? { + if (newArguments.isNotEmpty() && this.arguments.size != newArguments.size) + return null + return replace( + newArguments.mapIndexed { index, ksTypeArgument -> + val variance = when (ksTypeArgument.variance) { + Variance.INVARIANT -> org.jetbrains.kotlin.types.Variance.INVARIANT + Variance.COVARIANT -> org.jetbrains.kotlin.types.Variance.OUT_VARIANCE + Variance.CONTRAVARIANT -> org.jetbrains.kotlin.types.Variance.IN_VARIANCE + Variance.STAR -> return@mapIndexed StarProjectionImpl(constructor.parameters[index]) + } + + val type = when (ksTypeArgument) { + is KSTypeArgumentKtImpl, is KSTypeArgumentJavaImpl, is KSTypeArgumentLiteImpl -> ksTypeArgument.type!! + is KSTypeArgumentDescriptorImpl -> return@mapIndexed ksTypeArgument.descriptor + else -> throw IllegalStateException( + "Unexpected psi for type argument: ${ksTypeArgument.javaClass}, $ExceptionMessage" + ) + }.toKotlinType() ?: return null + + TypeProjectionImpl(variance, type) + } + ) +} + +internal fun FunctionDescriptor.toKSDeclaration(): KSDeclaration { + if (this.kind != CallableMemberDescriptor.Kind.DECLARATION) + return KSFunctionDeclarationDescriptorImpl.getCached(this) + val psi = this.findPsi() ?: return KSFunctionDeclarationDescriptorImpl.getCached(this) + // Java default constructor has a kind DECLARATION of while still being synthetic. + if (psi is PsiClassImpl && this is JavaClassConstructorDescriptor) { + return KSFunctionDeclarationDescriptorImpl.getCached(this) + } + return when (psi) { + is KtFunction -> KSFunctionDeclarationImpl.getCached(psi) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(psi) + is KtProperty -> KSPropertyDeclarationImpl.getCached(psi) + else -> throw IllegalStateException("unexpected psi: ${psi.javaClass}") + } +} + +internal fun PropertyDescriptor.toKSPropertyDeclaration(): KSPropertyDeclaration { + if (this.kind != CallableMemberDescriptor.Kind.DECLARATION) + return KSPropertyDeclarationDescriptorImpl.getCached(this) + val psi = this.findPsi() ?: return KSPropertyDeclarationDescriptorImpl.getCached(this) + return when (psi) { + is KtProperty -> KSPropertyDeclarationImpl.getCached(psi) + is KtParameter -> KSPropertyDeclarationParameterImpl.getCached(psi) + is PsiField -> KSPropertyDeclarationJavaImpl.getCached(psi) + is PsiMethod -> { + // happens when a java class implements a kotlin interface that declares properties. + KSPropertyDeclarationDescriptorImpl.getCached(this) + } + else -> throw IllegalStateException("unexpected psi: ${psi.javaClass}") + } +} + +/** + * @see KSFunctionDeclaration.findOverridee / [KSPropertyDeclaration.findOverridee] for docs. + */ +internal inline fun <reified T : CallableMemberDescriptor> T.findClosestOverridee(): T? { + // When there is an intermediate class between the overridden and our function, we might receive + // a FAKE_OVERRIDE function which is not desired as we are trying to find the actual + // declared method. + + // we also want to return the closes function declaration. That is either the closest + // class / interface method OR in case of equal distance (e.g. diamon dinheritance), pick the + // one declared first in the code. + + (getOwnerForEffectiveDispatchReceiverParameter() as? ClassDescriptor)?.defaultType?.let { + ResolverImpl.instance!!.incrementalContext.recordLookupWithSupertypes(it) + } + + val queue = ArrayDeque<T>() + queue.add(this) + + while (queue.isNotEmpty()) { + val current = queue.removeFirst() + ResolverImpl.instance!!.incrementalContext.recordLookupForCallableMemberDescriptor(current.original) + val overriddenDescriptors: Collection<T> = current.original.overriddenDescriptors.filterIsInstance<T>() + overriddenDescriptors.firstOrNull { + it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE + }?.let { + ResolverImpl.instance!!.incrementalContext.recordLookupForCallableMemberDescriptor(it.original) + return it.original as T? + } + // if all methods are fake, add them to the queue + queue.addAll(overriddenDescriptors) + } + return null +} + +internal fun ModuleClassResolver.resolveContainingClass(psiMethod: PsiMethod): ClassDescriptor? { + return if (psiMethod.isConstructor) { + resolveClass(JavaConstructorImpl(psiMethod).containingClass.apply { workaroundForNested() }) + } else { + resolveClass(JavaMethodImpl(psiMethod).containingClass.apply { workaroundForNested() }) + } +} + +internal fun getInstanceForCurrentRound(node: KSNode): KSNode? { + return when (node.origin) { + Origin.KOTLIN_LIB, Origin.JAVA_LIB -> null + else -> when (node) { + is KSClassDeclarationImpl -> KSClassDeclarationImpl.getCached(node.ktClassOrObject) + is KSFileImpl -> KSFileImpl.getCached(node.file) + is KSFunctionDeclarationImpl -> KSFunctionDeclarationImpl.getCached(node.ktFunction) + is KSPropertyDeclarationImpl -> KSPropertyDeclarationImpl.getCached(node.ktProperty) + is KSPropertyGetterImpl -> KSPropertyGetterImpl.getCached(node.ktPropertyAccessor) + is KSPropertySetterImpl -> KSPropertySetterImpl.getCached(node.ktPropertyAccessor) + is KSTypeAliasImpl -> KSTypeAliasImpl.getCached(node.ktTypeAlias) + is KSTypeArgumentLiteImpl -> KSTypeArgumentLiteImpl.getCached(node.type, node.variance) + is KSTypeArgumentKtImpl -> KSTypeArgumentKtImpl.getCached(node.ktTypeArgument) + is KSTypeParameterImpl -> KSTypeParameterImpl.getCached(node.ktTypeParameter) + is KSTypeReferenceImpl -> KSTypeReferenceImpl.getCached(node.ktTypeReference) + is KSValueParameterImpl -> KSValueParameterImpl.getCached(node.ktParameter) + is KSClassDeclarationJavaEnumEntryImpl -> KSClassDeclarationJavaEnumEntryImpl.getCached(node.psi) + is KSClassDeclarationJavaImpl -> KSClassDeclarationJavaImpl.getCached(node.psi) + is KSFileJavaImpl -> KSFileJavaImpl.getCached(node.psi) + is KSFunctionDeclarationJavaImpl -> KSFunctionDeclarationJavaImpl.getCached(node.psi) + is KSPropertyDeclarationJavaImpl -> KSPropertyDeclarationJavaImpl.getCached(node.psi) + is KSTypeArgumentJavaImpl -> KSTypeArgumentJavaImpl.getCached(node.psi, node.parent) + is KSTypeParameterJavaImpl -> KSTypeParameterJavaImpl.getCached(node.psi) + is KSTypeReferenceJavaImpl -> + KSTypeReferenceJavaImpl.getCached(node.psi, (node.parent as? KSAnnotated)?.getInstanceForCurrentRound()) + is KSValueParameterJavaImpl -> KSValueParameterJavaImpl.getCached(node.psi) + is KSPropertyGetterSyntheticImpl -> KSPropertyGetterSyntheticImpl.getCached(node.ksPropertyDeclaration) + is KSPropertySetterSyntheticImpl -> KSPropertySetterSyntheticImpl.getCached(node.ksPropertyDeclaration) + is KSValueParameterSyntheticImpl -> + KSPropertySetterImpl.getCached(node.owner as KtPropertyAccessor).parameter + is KSAnnotationJavaImpl -> KSAnnotationJavaImpl.getCached(node.psi) + is KSAnnotationImpl -> KSAnnotationImpl.getCached(node.ktAnnotationEntry) + is KSClassifierReferenceJavaImpl -> KSClassifierReferenceJavaImpl.getCached(node.psi, node.parent) + is KSValueArgumentJavaImpl -> + KSValueArgumentJavaImpl.getCached(node.name, node.value, getInstanceForCurrentRound(node.parent!!)) + else -> null + } + } +} + +internal fun KSAnnotated.getInstanceForCurrentRound(): KSAnnotated? = getInstanceForCurrentRound(this) as? KSAnnotated + +/** + * Helper class to read the order of fields/methods in a .class file compiled from Kotlin. + * + * When a compiled Kotlin class is read from descriptors, the order of fields / methods do not match + * the order in the original source file (or the .class file). + * This helper class reads the order from the binary class (using the visitor API) and allows + * [KSClassDeclarationDescriptorImpl] to sort its declarations based on the .class file. + * + * Note that the ordering is relevant only for fields and methods. For any other declaration, the + * order that was returned from the descriptor API is kept. + * + * see: https://github.com/google/ksp/issues/250 + */ +@KspExperimental +internal class DeclarationOrdering( + binaryClass: KotlinJvmBinaryClass +) : KotlinJvmBinaryClass.MemberVisitor { + // Map of fieldName -> Order + private val fieldOrdering = mutableMapOf<String, Int>() + // Map of method name to (jvm desc -> Order) map + // multiple methods might have the same name, hence we need to use signature matching for + // methods. That being said, we only do it when we find multiple methods with the same name + // otherwise, there is no reason to compute the jvm signature. + private val methodOrdering = mutableMapOf<String, MutableMap<String, Int>>() + // This map is built while we are sorting to ensure for the same declaration, we return the same + // order, in case it is not found in fields / methods. + private val declOrdering = IdentityHashMap<KSDeclaration, Int>() + // Helper class to generate ids that can be used for comparison. + private val orderProvider = OrderProvider() + + init { + binaryClass.visitMembers(this, null) + orderProvider.seal() + } + + val comparator = Comparator<KSDeclarationDescriptorImpl> { first, second -> + getOrder(first).compareTo(getOrder(second)) + } + + private fun getOrder(decl: KSDeclarationDescriptorImpl): Int { + return declOrdering.getOrPut(decl) { + when (decl) { + is KSPropertyDeclarationDescriptorImpl -> { + fieldOrdering[decl.simpleName.asString()]?.let { + return@getOrPut it + } + // might be a property without backing field. Use method ordering instead + decl.getter?.let { getter -> + return@getOrPut findMethodOrder( + ResolverImpl.instance!!.getJvmName(getter).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(getter) + } + } + decl.setter?.let { setter -> + return@getOrPut findMethodOrder( + ResolverImpl.instance!!.getJvmName(setter).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(setter) + } + } + orderProvider.next(decl) + } + is KSFunctionDeclarationDescriptorImpl -> { + findMethodOrder( + ResolverImpl.instance!!.getJvmName(decl).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(decl).toString() + } + } + else -> orderProvider.nextIgnoreSealed() + } + } + } + + private inline fun findMethodOrder( + jvmName: String, + crossinline getJvmDesc: () -> String + ): Int { + val methods = methodOrdering[jvmName] + // if there is 1 method w/ that name, just return. + // otherwise, we need signature matching + return when { + methods == null -> { + orderProvider.next(jvmName) + } + methods.size == 1 -> { + // only 1 method with this name, return it, no reason to resolve jvm + // signature + methods.values.first() + } + else -> { + // need to match using the jvm signature + val jvmDescriptor = getJvmDesc() + methods.getOrPut(jvmDescriptor) { + orderProvider.next(jvmName) + } + } + } + } + + override fun visitField( + name: Name, + desc: String, + initializer: Any? + ): KotlinJvmBinaryClass.AnnotationVisitor? { + fieldOrdering.getOrPut(name.asString()) { + orderProvider.next(name) + } + return null + } + + override fun visitMethod( + name: Name, + desc: String + ): KotlinJvmBinaryClass.MethodAnnotationVisitor? { + methodOrdering.getOrPut(name.asString()) { + mutableMapOf() + }.put(desc, orderProvider.next(name)) + return null + } + + /** + * Helper class to generate order values for items. + * Each time we see a new declaration, we give it an increasing order. + * + * This provider can also run in STRICT MODE to ensure that if we don't find an expected value + * during sorting, we can crash instead of picking the next ID. For now, it is only used for + * testing. + */ + private class OrderProvider { + private var nextId = 0 + private var sealed = false + + /** + * Seals the provider, preventing it from generating new IDs if [STRICT_MODE] is enabled. + */ + fun seal() { + sealed = true + } + + /** + * Returns the next available order value. + * + * @param ref Used for logging if the data is sealed and we shouldn't provide a new order. + */ + fun next(ref: Any): Int { + check(!sealed || !STRICT_MODE) { + "couldn't find item $ref" + } + return nextId ++ + } + + /** + * Returns the next ID without checking whether the model is sealed or not. This is useful + * for declarations where we don't care about the order (e.g. inner class declarations). + */ + fun nextIgnoreSealed(): Int { + return nextId ++ + } + } + companion object { + /** + * Used in tests to prevent fallback behavior of creating a new ID when we cannot find the + * order. + */ + var STRICT_MODE = false + } +} + +/** + * Same as KSDeclarationContainer.declarations, but sorted by declaration order in the source. + * + * Note that this is SLOW. AVOID IF POSSIBLE. + */ +@KspExperimental +internal val KSDeclarationContainer.declarationsInSourceOrder: Sequence<KSDeclaration> + get() { + // Only Kotlin libs can be out of order. + if (this !is KSClassDeclarationDescriptorImpl || origin != Origin.KOTLIN_LIB) + return declarations + + val declarationOrdering = ( + (descriptor as? DeserializedClassDescriptor)?.source as? KotlinJvmBinarySourceElement + )?.binaryClass?.let { + DeclarationOrdering(it) + } ?: return declarations + + return (declarations as? Sequence<KSDeclarationDescriptorImpl>)?.sortedWith(declarationOrdering.comparator) + ?: declarations + } + +internal val KSPropertyDeclaration.jvmAccessFlag: Int + get() = when (origin) { + Origin.KOTLIN_LIB -> { + val descriptor = (this as KSPropertyDeclarationDescriptorImpl).descriptor + val kotlinBinaryJavaClass = descriptor.getContainingKotlinJvmBinaryClass() + // 0 if no backing field + kotlinBinaryJavaClass?.let { + BinaryClassInfoCache.getCached(it).fieldAccFlags.get(this.simpleName.asString()) ?: 0 + } ?: 0 + } + Origin.JAVA_LIB -> { + val descriptor = (this as KSPropertyDeclarationDescriptorImpl).descriptor + ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaField)?.access ?: 0 + } + else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB") + } + +internal val KSFunctionDeclaration.jvmAccessFlag: Int + get() = when (origin) { + Origin.KOTLIN_LIB -> { + val jvmDesc = ResolverImpl.instance!!.mapToJvmSignatureInternal(this) + val descriptor = (this as KSFunctionDeclarationDescriptorImpl).descriptor + // Companion.<init> doesn't have containing KotlinJvmBinaryClass. + val kotlinBinaryJavaClass = descriptor.getContainingKotlinJvmBinaryClass() + kotlinBinaryJavaClass?.let { + BinaryClassInfoCache.getCached(it).methodAccFlags.get(this.simpleName.asString() + jvmDesc) ?: 0 + } ?: 0 + } + Origin.JAVA_LIB -> { + val descriptor = (this as KSFunctionDeclarationDescriptorImpl).descriptor + // Some functions, like `equals` in builtin types, doesn't have source. + ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaMethodBase)?.access ?: 0 + } + else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB") + } + +// Compiler subtype checking does not convert Java types to Kotlin types, while getting super types +// from a java type does the conversion, therefore resulting in subtype checking for Java types to fail. +// Check if candidate super type is a Java type, convert to Kotlin type for subtype checking. +// Also, if the type is a generic deserialized type, that actually represents a function type, +// a conversion is also required to yield a type with a correctly recognised descriptor. +internal fun KotlinType.convertKotlinType(): KotlinType { + val declarationDescriptor = this.constructor.declarationDescriptor + val base = if (declarationDescriptor?.shouldMapToKotlinForAssignabilityCheck() == true) { + JavaToKotlinClassMapper + .mapJavaToKotlin(declarationDescriptor.fqNameSafe, ResolverImpl.instance!!.module.builtIns) + ?.defaultType + ?.replace(this.arguments) + ?: this + } else this + val newarguments = + base.arguments.map { if (it !is StarProjectionImpl) it.replaceType(it.type.convertKotlinType()) else it } + val upperBound = if (base.unwrap() is FlexibleType) { + (base.unwrap() as FlexibleType).upperBound.arguments + .map { if (it !is StarProjectionImpl) it.replaceType(it.type.convertKotlinType()) else it } + } else newarguments + return base.replace( + newarguments, + annotations, + upperBound + ) +} + +private fun ClassifierDescriptor.shouldMapToKotlinForAssignabilityCheck(): Boolean { + return when (this) { + is JavaClassDescriptor -> true // All java types need to be mapped to kotlin + is DeserializedDescriptor -> { + // If this is a generic deserialized type descriptor, which actually is a kotlin function type. + // This may be the case if the client explicitly mapped a kotlin function type to the JVM one. + // Such types need to be remapped to be represented by a correct function class descriptor. + fqNameSafe.parent().asString() == "kotlin.jvm.functions" + } + else -> false + } +} + +fun DeclarationDescriptor.findPsi(): PsiElement? { + // For synthetic members. + if ((this is CallableMemberDescriptor) && this.kind != CallableMemberDescriptor.Kind.DECLARATION) return null + val psi = (this as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return null + if (psi is KtElement) return psi + + // Find Java PSIs loaded by KSP + val containingFile = ResolverImpl.instance!!.findPsiJavaFile(psi.containingFile.virtualFile.path) ?: return null + val leaf = containingFile.findElementAt(psi.textOffset) ?: return null + return leaf.parentsWithSelf.firstOrNull { psi.manager.areElementsEquivalent(it, psi) } +} |