diff --git a/api/src/main/kotlin/com/google/devtools/ksp/utils.kt b/api/src/main/kotlin/com/google/devtools/ksp/utils.kt index 42bd9fb9e9..d3c2f54f90 100644 --- a/api/src/main/kotlin/com/google/devtools/ksp/utils.kt +++ b/api/src/main/kotlin/com/google/devtools/ksp/utils.kt @@ -520,7 +520,7 @@ class KSTypesNotPresentException(val ksTypes: List, cause: Throwable) : @KspExperimental private fun KSType.asClass(proxyClass: Class<*>) = try { - Class.forName(this.declaration.qualifiedName!!.asString(), true, proxyClass.classLoader) + Class.forName(this.declaration.toJavaClassName(), true, proxyClass.classLoader) } catch (e: Exception) { throw KSTypeNotPresentException(this, e) } @@ -536,3 +536,28 @@ fun KSValueArgument.isDefault() = origin == Origin.SYNTHETIC @KspExperimental private fun Any.asArray(method: Method, proxyClass: Class<*>) = listOf(this).asArray(method, proxyClass) + +private fun KSDeclaration.toJavaClassName(): String { + val nameDelimiter = '.' + val packageNameString = packageName.asString() + val qualifiedNameString = qualifiedName!!.asString() + val simpleNames = qualifiedNameString + .removePrefix("${packageNameString}$nameDelimiter") + .split(nameDelimiter) + + return if (simpleNames.size > 1) { + buildString { + append(packageNameString) + append(nameDelimiter) + + simpleNames.forEachIndexed { index, s -> + if (index > 0) { + append('$') + } + append(s) + } + } + } else { + qualifiedNameString + } +} diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt index 3642024744..db7d460845 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt @@ -94,6 +94,12 @@ class KSPAATest : AbstractKSPAATest() { runTest("../test-utils/testData/api/annotationWithArbitraryClassValue.kt") } + @TestMetadata("annotationWithNestedClassValue.kt") + @Test + fun testAnnotationWithNestedClassValue() { + runTest("../test-utils/testData/api/annotationWithNestedClassValue.kt") + } + @TestMetadata("defaultKClassValue.kt") @Test fun testAnnotationValue_defaultKClassValue() { diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AnnotationNestedClassValueProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AnnotationNestedClassValueProcessor.kt new file mode 100644 index 0000000000..b60102c3b0 --- /dev/null +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AnnotationNestedClassValueProcessor.kt @@ -0,0 +1,55 @@ +/* + * 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.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 kotlin.reflect.KClass + +@KspExperimental +class AnnotationNestedClassValueProcessor : AbstractTestProcessor() { + val result = mutableListOf() + + override fun toResult(): List { + return result + } + + override fun process(resolver: Resolver): List { + val symbols = resolver.getSymbolsWithAnnotation( + "com.google.devtools.ksp.processor.NestedClassValueAnnotation" + ) + symbols.flatMap { + it.getAnnotationsByType(NestedClassValueAnnotation::class) + }.forEach { + logAnnotationValues(it) + } + return emptyList() + } + + private fun logAnnotationValues(classValueAnnotation: NestedClassValueAnnotation) { + result.add(classValueAnnotation.classValue.simpleName!!) + result.add(classValueAnnotation.classValues.joinToString { it.simpleName!! }) + } +} + +annotation class NestedClassValueAnnotation( + val classValue: KClass<*>, + val classValues: Array> +) diff --git a/test-utils/testData/api/annotationWithNestedClassValue.kt b/test-utils/testData/api/annotationWithNestedClassValue.kt new file mode 100644 index 0000000000..6bc025dd20 --- /dev/null +++ b/test-utils/testData/api/annotationWithNestedClassValue.kt @@ -0,0 +1,36 @@ +/* + * 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. + */ +// TEST PROCESSOR: AnnotationNestedClassValueProcessor +// EXPECTED: +// Entry +// Entry +// END +// FILE: a.kt +package com.google.devtools.ksp.processor + +import kotlin.reflect.KClass + +annotation class NestedClassValueAnnotation( + val classValue: KClass<*>, + val classValues: Array> +) + +@NestedClassValueAnnotation( + classValue = java.util.Map.Entry::class, + classValues = [java.util.Map.Entry::class] +) +class ClassValueAnnotated