Skip to content

Commit

Permalink
manually look up in scope when annotation argument type is nested ann…
Browse files Browse the repository at this point in the history
…otation.

* fixes #989
  • Loading branch information
neetopia committed Sep 13, 2022
1 parent 58c6c83 commit 35a12d6
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 4 deletions.
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<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.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(
Expand Down
Expand Up @@ -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
Expand Down
@@ -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
3 changes: 3 additions & 0 deletions compiler-plugin/testData/api/getByName.kt
Expand Up @@ -28,6 +28,8 @@ open class Foo {
fun overload(a: Int) = "Overload"
fun overload() = "Overload"
val lib1MemberProp = 1.0

class FooNested {}
}

fun lib1TopFun(): Int {
Expand Down Expand Up @@ -61,6 +63,7 @@ package source
class FooInSource {
fun sourceMemberFun() = 1
val sourceMemberProp = 1.0
class FooInSourceNested
}

val propInSource = 1
Expand Down
@@ -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<String>()
private val annotationKClass = KotlinAnnotationWithInnerDefaults::class

override fun toResult(): List<String> {
return results
}

@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
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()
}
}
@@ -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
Expand All @@ -13,6 +14,12 @@ class GetByNameProcessor : AbstractTestProcessor() {
}

override fun process(resolver: Resolver): List<KSAnnotated> {
val classNames = listOf(
"lib1.Foo",
"lib1.Foo.FooNested",
"source.FooInSource",
"source.FooInSource.FooInSourceNested"
)
val funNames = listOf(
"lib1.Foo.lib1MemberFun",
"lib1.lib1TopFun",
Expand All @@ -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")
Expand Down

0 comments on commit 35a12d6

Please sign in to comment.