From 8e21c422b277f1475ad1fcbd507c4399cfccb651 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Mon, 14 Aug 2023 16:16:42 +0200 Subject: [PATCH] Inner KSP classes workround and tests --- .../ast/utils/EnclosedElementsQuery.java | 11 +++-- .../visitor/KotlinVisitorContext.kt | 13 ++++++ .../beans/BeanDefinitionSpec.groovy | 46 +++++++++++++++++++ .../micronaut/docs/aop/around/AroundSpec.kt | 42 +++++++++++++++++ .../aop/around/NotNullMyInlineInnerExample.kt | 38 +++++++++++++++ .../aop/around/NotNullResultInnerExample.kt | 30 ++++++++++++ 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullMyInlineInnerExample.kt create mode 100644 test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullResultInnerExample.kt diff --git a/core-processor/src/main/java/io/micronaut/inject/ast/utils/EnclosedElementsQuery.java b/core-processor/src/main/java/io/micronaut/inject/ast/utils/EnclosedElementsQuery.java index de79c306f80..bbed19e0f2f 100644 --- a/core-processor/src/main/java/io/micronaut/inject/ast/utils/EnclosedElementsQuery.java +++ b/core-processor/src/main/java/io/micronaut/inject/ast/utils/EnclosedElementsQuery.java @@ -214,10 +214,13 @@ private Collection getAllElements Set addedFromClassElements = new LinkedHashSet<>(); classElements: for (N element : classElements) { - String elementName = getElementName(element); - for (Predicate namePredicate : result.getNamePredicates()) { - if (!namePredicate.test(elementName)) { - continue classElements; + List> namePredicates = result.getNamePredicates(); + if (!namePredicates.isEmpty()) { + String elementName = getElementName(element); + for (Predicate namePredicate : namePredicates) { + if (!namePredicate.test(elementName)) { + continue classElements; + } } } diff --git a/inject-kotlin/src/main/kotlin/io/micronaut/kotlin/processing/visitor/KotlinVisitorContext.kt b/inject-kotlin/src/main/kotlin/io/micronaut/kotlin/processing/visitor/KotlinVisitorContext.kt index 30739038516..02d337625f5 100644 --- a/inject-kotlin/src/main/kotlin/io/micronaut/kotlin/processing/visitor/KotlinVisitorContext.kt +++ b/inject-kotlin/src/main/kotlin/io/micronaut/kotlin/processing/visitor/KotlinVisitorContext.kt @@ -25,6 +25,8 @@ import com.google.devtools.ksp.symbol.KSNode import io.micronaut.core.convert.ArgumentConversionContext import io.micronaut.core.convert.value.MutableConvertibleValues import io.micronaut.core.convert.value.MutableConvertibleValuesMap +import io.micronaut.core.reflect.ClassUtils +import io.micronaut.core.reflect.ReflectionUtils import io.micronaut.core.util.StringUtils import io.micronaut.expressions.context.DefaultExpressionCompilationContextFactory import io.micronaut.expressions.context.ExpressionCompilationContextFactory @@ -63,6 +65,17 @@ internal open class KotlinVisitorContext( elementAnnotationMetadataFactory = KotlinElementAnnotationMetadataFactory(false, annotationMetadataBuilder) expressionCompilationContextFactory = DefaultExpressionCompilationContextFactory(this) + + try { + // Workaround for bug in KSP https://github.com/google/ksp/issues/1493 + val resolverImplClass = ClassUtils.forName("com.google.devtools.ksp.processing.impl.ResolverImpl", javaClass.classLoader).orElseThrow() + val kotlinTypeMapperClass = ClassUtils.forName("org.jetbrains.kotlin.codegen.state.KotlinTypeMapper", javaClass.classLoader).orElseThrow() + val kotlinTypeMapperInstance = ReflectionUtils.getFieldValue(resolverImplClass, "typeMapper", resolver).orElseThrow() + ReflectionUtils.setField(kotlinTypeMapperClass, "useOldManglingRulesForFunctionAcceptingInlineClass", kotlinTypeMapperInstance, false) + } catch (e: Exception) { + e.printStackTrace(); + // Ignore + } } override fun get( diff --git a/inject-kotlin/src/test/groovy/io/micronaut/kotlin/processing/beans/BeanDefinitionSpec.groovy b/inject-kotlin/src/test/groovy/io/micronaut/kotlin/processing/beans/BeanDefinitionSpec.groovy index aad3df3571c..6603811e7c2 100644 --- a/inject-kotlin/src/test/groovy/io/micronaut/kotlin/processing/beans/BeanDefinitionSpec.groovy +++ b/inject-kotlin/src/test/groovy/io/micronaut/kotlin/processing/beans/BeanDefinitionSpec.groovy @@ -19,6 +19,7 @@ import spock.lang.Specification import static io.micronaut.annotation.processing.test.KotlinCompiler.buildBeanDefinition import static io.micronaut.annotation.processing.test.KotlinCompiler.buildBeanDefinitionReference import static io.micronaut.annotation.processing.test.KotlinCompiler.buildContext +import static io.micronaut.annotation.processing.test.KotlinCompiler.buildInterceptedBeanDefinition import static io.micronaut.annotation.processing.test.KotlinCompiler.getBean import static io.micronaut.annotation.processing.test.KotlinCompiler.getBeanDefinition @@ -1008,4 +1009,49 @@ interface Deserializer { deserializerTypeParam.isTypeVariable() (deserializerTypeParam instanceof GenericPlaceholder) } + + void "test inline class return type"() { + given: + BeanDefinition definition = buildInterceptedBeanDefinition('test.NotNullMyInlineInnerExample', ''' +package test + +import io.micronaut.aop.Around +import kotlin.annotation.AnnotationRetention.RUNTIME +import kotlin.annotation.AnnotationTarget.CLASS +import kotlin.annotation.AnnotationTarget.FILE +import kotlin.annotation.AnnotationTarget.FUNCTION +import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER +import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER +import jakarta.inject.Singleton + +@Singleton +open class NotNullMyInlineInnerExample { + + @NotNull + open fun doWork(taskName: String?) : MyResult { + println("Doing job: $taskName") + return MyResult(taskName!!) + } +} + +@JvmInline +value class MyResult(val task: String) { + init { + require(task.isNotEmpty()) { "Should not be empty" } + } +} + +@MustBeDocumented +@Retention(RUNTIME) +@Target(CLASS, FILE, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER) +@Around +annotation class NotNull + ''') + + when: + def doWorkMethod = definition.getBeanType().getDeclaredMethods()[1] + def supertypeMethods = definition.getBeanType().getSuperclass().getDeclaredMethods() + then: + supertypeMethods.collect { it.name}.contains(doWorkMethod.name) + } } diff --git a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/AroundSpec.kt b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/AroundSpec.kt index 04e38d277e1..13de4fd2178 100644 --- a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/AroundSpec.kt +++ b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/AroundSpec.kt @@ -20,4 +20,46 @@ class AroundSpec: AnnotationSpec() { applicationContext.close() } // end::test[] + + // tag::resultinline[] + @Test + fun testNotNullResultInline() { + val applicationContext = ApplicationContext.run() + val exampleBean = applicationContext.getBean(NotNullResultInnerExample::class.java) + + val exception = shouldThrow { + exampleBean.doWork(null) + } + exception.message shouldBe "Null parameter [taskName] not allowed" + + val taskName = exampleBean.doWork("test").getOrNull() + + taskName shouldBe "test" + + applicationContext.close() + } + // end::resultinline[] + + // tag::myinline[] + @Test + fun testNotNullMyInline() { + val applicationContext = ApplicationContext.run() + val exampleBean = applicationContext.getBean(NotNullMyInlineInnerExample::class.java) + + val exception1 = shouldThrow { + exampleBean.doWork(null) + } + exception1.message shouldBe "Null parameter [taskName] not allowed" + + val exception2 = shouldThrow { + exampleBean.doWork("") + } + exception2.message shouldBe "Should not be empty" + + val taskName = exampleBean.doWork("test").task + taskName shouldBe "test" + + applicationContext.close() + } + // end::myinline[] } diff --git a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullMyInlineInnerExample.kt b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullMyInlineInnerExample.kt new file mode 100644 index 00000000000..89533fc1610 --- /dev/null +++ b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullMyInlineInnerExample.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2017-2020 original authors + * + * 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 io.micronaut.docs.aop.around + +// tag::example[] +import jakarta.inject.Singleton + +@Singleton +open class NotNullMyInlineInnerExample { + + @NotNull + open fun doWork(taskName: String?) : MyResult { + println("Doing job: $taskName") + return MyResult(taskName!!) + } +} + +@JvmInline +value class MyResult(val task: String) { + init { + require(task.isNotEmpty()) { "Should not be empty" } + } +} + +// end::example[] diff --git a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullResultInnerExample.kt b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullResultInnerExample.kt new file mode 100644 index 00000000000..9d1d14bec6e --- /dev/null +++ b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/aop/around/NotNullResultInnerExample.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2020 original authors + * + * 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 io.micronaut.docs.aop.around + +// tag::example[] +import jakarta.inject.Singleton + +@Singleton +open class NotNullResultInnerExample { + + @NotNull + open fun doWork(taskName: String?) : Result { + println("Doing job: $taskName") + return Result.success(taskName!!) + } +} +// end::example[]