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 index 2a32b5df97..69aff1e1ac 100644 --- 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 @@ -20,6 +20,7 @@ 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.findPsi +import com.google.devtools.ksp.getClassDeclarationByName import com.google.devtools.ksp.processing.impl.ResolverImpl import com.google.devtools.ksp.symbol.* import com.google.devtools.ksp.symbol.impl.java.KSAnnotationJavaImpl @@ -35,6 +36,7 @@ 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 @@ -53,7 +55,9 @@ 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 @@ -160,14 +164,31 @@ internal fun AnnotationDescriptor.getDefaultArguments(ownerAnnotation: KSAnnotat 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() + .singleOrNull { it.simpleName.asString() == shortNames[i] } + } + return parent?.let { ResolverImpl.instance!!.resolveClassDeclaration(it) } +} + internal fun KotlinType.getDefaultConstructorArguments( excludeNames: List, ownerAnnotation: KSAnnotation ): List { - return (this.constructor.declarationDescriptor as? ClassDescriptor)?.constructors?.single() - ?.let { argumentsFromDefault -> - argumentsFromDefault.getAbsentDefaultArguments(excludeNames, ownerAnnotation) - } ?: emptyList() + return this.constructor.toDeclarationDescriptor()?.constructors?.single() + ?.getAbsentDefaultArguments(excludeNames, ownerAnnotation) ?: emptyList() } fun ClassConstructorDescriptor.getAbsentDefaultArguments( diff --git a/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/KSPCompilerPluginTest.kt b/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/KSPCompilerPluginTest.kt index 58ebfee61b..aa3c850652 100644 --- a/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/KSPCompilerPluginTest.kt +++ b/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/KSPCompilerPluginTest.kt @@ -230,6 +230,12 @@ class KSPCompilerPluginTest : AbstractKSPCompilerPluginTest() { runTest("testData/api/functionTypes.kt") } + @TestMetadata("getAnnotationByTypeWithInnerDefault.kt") + @Test + fun testGetAnnotationByTypeWithInnerDefault() { + runTest("testData/api/getAnnotationByTypeWithInnerDefault.kt") + } + @DisabledOnOs(OS.WINDOWS) @TestMetadata("getPackage.kt") @Test diff --git a/compiler-plugin/testData/api/getAnnotationByTypeWithInnerDefault.kt b/compiler-plugin/testData/api/getAnnotationByTypeWithInnerDefault.kt new file mode 100644 index 0000000000..5634681352 --- /dev/null +++ b/compiler-plugin/testData/api/getAnnotationByTypeWithInnerDefault.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Google LLC + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// WITH_RUNTIME +// TEST PROCESSOR: GetAnnotationByTypeProcessor +// EXPECTED: +// com.google.devtools.ksp.processor.KotlinAnnotationWithInnerDefaults.InnerAnnotation[innerAnnotationDefault=7, moreInnerAnnotation=com.google.devtools.ksp.processor.KotlinAnnotationWithInnerDefaults.InnerAnnotation.MoreInnerAnnotation[moreInnerAnnotationDefault=OK]] +// END +// MODULE: lib +// FILE: KotlinAnnotationWithInnerDefaults.kt +package com.google.devtools.ksp.processor +annotation class KotlinAnnotationWithInnerDefaults( + val innerAnnotationVal: InnerAnnotation = InnerAnnotation(innerAnnotationDefault = 7) +) { + annotation class InnerAnnotation( + val innerAnnotationDefault: Int, + val moreInnerAnnotation: MoreInnerAnnotation = MoreInnerAnnotation("OK") + ) { + annotation class MoreInnerAnnotation(val moreInnerAnnotationDefault: String) + } +} + +// MODULE: main(lib) +// FILE: com/google/devtools/ksp/processor/a.kt +package com.google.devtools.ksp.processor + +@KotlinAnnotationWithInnerDefaults +class A diff --git a/compiler-plugin/testData/api/getByName.kt b/compiler-plugin/testData/api/getByName.kt index 5ab44ce56b..51f826b006 100644 --- a/compiler-plugin/testData/api/getByName.kt +++ b/compiler-plugin/testData/api/getByName.kt @@ -28,6 +28,8 @@ open class Foo { fun overload(a: Int) = "Overload" fun overload() = "Overload" val lib1MemberProp = 1.0 + + class FooNested {} } fun lib1TopFun(): Int { @@ -61,6 +63,7 @@ package source class FooInSource { fun sourceMemberFun() = 1 val sourceMemberProp = 1.0 + class FooInSourceNested } val propInSource = 1 diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetAnnotationByTypeProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetAnnotationByTypeProcessor.kt new file mode 100644 index 0000000000..8615cdb8f8 --- /dev/null +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetAnnotationByTypeProcessor.kt @@ -0,0 +1,36 @@ +package com.google.devtools.ksp.processor + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration + +annotation class KotlinAnnotationWithInnerDefaults( + val innerAnnotationVal: InnerAnnotation = InnerAnnotation(innerAnnotationDefault = 7) +) { + annotation class InnerAnnotation( + val innerAnnotationDefault: Int, + val moreInnerAnnotation: MoreInnerAnnotation = MoreInnerAnnotation("OK") + ) { + annotation class MoreInnerAnnotation(val moreInnerAnnotationDefault: String) + } +} + +class GetAnnotationByTypeProcessor : AbstractTestProcessor() { + val results = mutableListOf() + private val annotationKClass = KotlinAnnotationWithInnerDefaults::class + + override fun toResult(): List { + return results + } + + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + val decl = resolver.getAllFiles().single().declarations + .single { it.simpleName.asString() == "A" } as KSClassDeclaration + val anno = decl.getAnnotationsByType(annotationKClass).first() + results.add(anno.innerAnnotationVal.toString()) + return emptyList() + } +} diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetByNameProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetByNameProcessor.kt index bc406df203..c7e49eec9b 100644 --- a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetByNameProcessor.kt +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/GetByNameProcessor.kt @@ -1,5 +1,6 @@ package com.google.devtools.ksp.processor +import com.google.devtools.ksp.getClassDeclarationByName import com.google.devtools.ksp.getFunctionDeclarationsByName import com.google.devtools.ksp.getPropertyDeclarationByName import com.google.devtools.ksp.processing.Resolver @@ -13,6 +14,12 @@ class GetByNameProcessor : AbstractTestProcessor() { } override fun process(resolver: Resolver): List { + val classNames = listOf( + "lib1.Foo", + "lib1.Foo.FooNested", + "source.FooInSource", + "source.FooInSource.FooInSourceNested" + ) val funNames = listOf( "lib1.Foo.lib1MemberFun", "lib1.lib1TopFun", @@ -28,6 +35,11 @@ class GetByNameProcessor : AbstractTestProcessor() { "source.FooInSource.sourceMemberProp", "source.propInSource", ) + for (className in classNames) { + if (resolver.getClassDeclarationByName(className) == null) { + results.add("failed to get $className") + } + } for (funName in funNames) { if (resolver.getFunctionDeclarationsByName(funName, true).none()) { results.add("failed to get $funName")