Skip to content

Commit

Permalink
Inner KSP classes workround and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed Aug 15, 2023
1 parent 0d05528 commit 8e21c42
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,13 @@ private <T extends io.micronaut.inject.ast.Element> Collection<T> getAllElements
Set<T> addedFromClassElements = new LinkedHashSet<>();
classElements:
for (N element : classElements) {
String elementName = getElementName(element);
for (Predicate<String> namePredicate : result.getNamePredicates()) {
if (!namePredicate.test(elementName)) {
continue classElements;
List<Predicate<String>> namePredicates = result.getNamePredicates();
if (!namePredicates.isEmpty()) {
String elementName = getElementName(element);
for (Predicate<String> namePredicate : namePredicates) {
if (!namePredicate.test(elementName)) {
continue classElements;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <T : Any?> get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1008,4 +1009,49 @@ interface Deserializer<T> {
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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<IllegalArgumentException> {
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<IllegalArgumentException> {
exampleBean.doWork(null)
}
exception1.message shouldBe "Null parameter [taskName] not allowed"

val exception2 = shouldThrow<IllegalArgumentException> {
exampleBean.doWork("")
}
exception2.message shouldBe "Should not be empty"

val taskName = exampleBean.doWork("test").task
taskName shouldBe "test"

applicationContext.close()
}
// end::myinline[]
}
Original file line number Diff line number Diff line change
@@ -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[]
Original file line number Diff line number Diff line change
@@ -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<String> {
println("Doing job: $taskName")
return Result.success(taskName!!)
}
}
// end::example[]

0 comments on commit 8e21c42

Please sign in to comment.