Skip to content

Commit

Permalink
Implement KotlinElementFactory.newEnumConstantElement (#9774)
Browse files Browse the repository at this point in the history
  • Loading branch information
altro3 authored Aug 24, 2023
1 parent 287b670 commit 2f35acb
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import io.micronaut.kotlin.processing.visitor.AbstractKotlinElement
import io.micronaut.kotlin.processing.visitor.AbstractKotlinMethodElement
import io.micronaut.kotlin.processing.visitor.KotlinClassNativeElement
import io.micronaut.kotlin.processing.visitor.KotlinClassElement
import io.micronaut.kotlin.processing.visitor.KotlinEnumConstantElement
import io.micronaut.kotlin.processing.visitor.KotlinFieldElement
import io.micronaut.kotlin.processing.visitor.KotlinGenericPlaceholderElement
import io.micronaut.kotlin.processing.visitor.KotlinParameterElement
Expand Down Expand Up @@ -152,8 +153,15 @@ internal class KotlinElementAnnotationMetadataFactory(
}

override fun lookupForField(fieldElement: FieldElement): CachedAnnotationMetadata? {
val kotlinFieldElement = fieldElement as KotlinFieldElement
val owner = kotlinFieldElement.owningType as KotlinClassElement
val kotlinFieldElement = fieldElement as AbstractKotlinElement<*>
val owner: KotlinClassElement
if (kotlinFieldElement is KotlinFieldElement) {
owner = kotlinFieldElement.owningType as KotlinClassElement
} else if (kotlinFieldElement is KotlinEnumConstantElement) {
owner = kotlinFieldElement.owningType as KotlinClassElement
} else {
throw RuntimeException("Unknown field element type ${fieldElement.javaClass}")
}
return metadataBuilder.lookupOrBuild(
Key2(
getClassDefinitionCacheKey(owner),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package io.micronaut.kotlin.processing.visitor

import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import io.micronaut.core.annotation.AnnotationUtil
Expand All @@ -30,7 +31,7 @@ import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory

internal class KotlinElementFactory(
private val visitorContext: KotlinVisitorContext
) : ElementFactory<Any, KSClassDeclaration, KSFunctionDeclaration, KSPropertyDeclaration> {
) : ElementFactory<Any, KSClassDeclaration, KSFunctionDeclaration, KSDeclaration> {

fun newClassElement(
type: KSClassDeclaration
Expand Down Expand Up @@ -111,22 +112,32 @@ internal class KotlinElementFactory(

override fun newFieldElement(
owningClass: ClassElement,
field: KSPropertyDeclaration,
field: KSDeclaration,
elementAnnotationMetadataFactory: ElementAnnotationMetadataFactory
): FieldElement {
return KotlinFieldElement(
owningClass,
field,
field as KSPropertyDeclaration,
elementAnnotationMetadataFactory,
visitorContext
)
}

override fun newEnumConstantElement(
owningClass: ClassElement?,
enumConstant: KSPropertyDeclaration?,
elementAnnotationMetadataFactory: ElementAnnotationMetadataFactory?
owningClass: ClassElement,
enumConstant: KSDeclaration,
elementAnnotationMetadataFactory: ElementAnnotationMetadataFactory
): EnumConstantElement {
TODO("Not yet implemented")

if (owningClass !is KotlinEnumElement) {
throw IllegalArgumentException("Declaring class must be a KotlinEnumElement");
}

return KotlinEnumConstantElement(
owningClass,
enumConstant as KSClassDeclaration,
elementAnnotationMetadataFactory,
visitorContext
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2017-2023 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
*
* https://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.kotlin.processing.visitor

import com.google.devtools.ksp.symbol.KSClassDeclaration
import io.micronaut.core.annotation.AnnotationMetadata
import io.micronaut.inject.ast.ClassElement
import io.micronaut.inject.ast.ElementModifier
import io.micronaut.inject.ast.EnumConstantElement
import io.micronaut.inject.ast.FieldElement
import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory

internal class KotlinEnumConstantElement(
private val owningClass: ClassElement,
val declaration: KSClassDeclaration,
elementAnnotationMetadataFactory: ElementAnnotationMetadataFactory,
visitorContext: KotlinVisitorContext,
) : AbstractKotlinElement<KotlinEnumConstantNativeElement>(
KotlinEnumConstantNativeElement(declaration),
elementAnnotationMetadataFactory,
visitorContext
), EnumConstantElement {

override fun copyThis() =
KotlinEnumConstantElement(
owningClass,
declaration,
elementAnnotationMetadataFactory,
visitorContext
)

override fun getDeclaringType(): ClassElement {
return owningClass
}

override fun getType(): ClassElement {
return owningClass
}

override fun getModifiers(): MutableSet<ElementModifier> = EnumConstantElement.ENUM_CONSTANT_MODIFIERS

override fun getName(): String {
return nativeType.element.toString()
}

override fun isPackagePrivate(): Boolean {
return false
}

override fun isAbstract(): Boolean {
return false
}

override fun isStatic(): Boolean {
return true
}

override fun isPublic(): Boolean {
return true
}

override fun isPrivate(): Boolean {
return false
}

override fun isFinal(): Boolean {
return true
}

override fun withAnnotationMetadata(annotationMetadata: AnnotationMetadata) =
super<AbstractKotlinElement>.withAnnotationMetadata(annotationMetadata) as FieldElement

override fun isProtected(): Boolean {
return false
}

override fun isPrimitive(): Boolean {
return false
}

override fun isArray(): Boolean {
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ internal class KotlinEnumElement(
.map { ksClassDeclaration -> ksClassDeclaration.simpleName.asString() }
.toList()

override fun elements() = declaration.declarations
.filterIsInstance<KSClassDeclaration>()
.map { ksClassDeclaration ->
KotlinEnumConstantElement(
this,
ksClassDeclaration,
elementAnnotationMetadataFactory,
visitorContext
)
}
.toList()

override fun getDefaultConstructor(): Optional<MethodElement> = Optional.empty()

override fun getPrimaryConstructor(): Optional<MethodElement> =
Expand All @@ -52,5 +64,4 @@ internal class KotlinEnumElement(
visitorContext,
resolvedTypeArguments
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ class KotlinFieldNativeElement(
}
}

class KotlinEnumConstantNativeElement(
val declaration: KSClassDeclaration,
) : KotlinNativeElement(declaration) {

override fun toString(): String {
return "KotlinEnumConstantNativeElement(declaration=$declaration)"
}
}

class KotlinMethodParameterNativeElement(
val declaration: KSValueParameter,
val method: KotlinNativeElement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,66 @@ import io.micronaut.inject.ast.PropertyElement
import io.micronaut.inject.ast.WildcardElement
import io.micronaut.kotlin.processing.visitor.AllElementsVisitor
import io.micronaut.kotlin.processing.visitor.KotlinClassElement
import io.micronaut.kotlin.processing.visitor.KotlinEnumElement
import jakarta.validation.Valid
import spock.lang.PendingFeature

class ClassElementSpec extends AbstractKotlinCompilerSpec {

void "test visit enum"() {

given:
AllElementsVisitor.VISITED_CLASS_ELEMENTS.clear()
AllElementsVisitor.VISITED_ELEMENTS.clear()
AllElementsVisitor.VISITED_METHOD_ELEMENTS.clear()
AllElementsVisitor.WRITE_FILE = true
AllElementsVisitor.WRITE_IN_METAINF = true

when:
def definition = buildBeanDefinition('test.MyBean', '''
package test
import io.micronaut.core.annotation.*
import io.micronaut.http.annotation.*
import io.micronaut.http.*
@Controller("/hello")
class HelloController {
@Get
@Produces(MediaType.TEXT_PLAIN)
fun index(@QueryValue("channels") channels: Channel?) = ""
@Introspected
enum class Channel {
SYSTEM1,
SYSTEM2
}
}
@jakarta.inject.Singleton
class MyBean {}
''')
then:
definition

AllElementsVisitor.VISITED_CLASS_ELEMENTS.size() == 3
def enumEl = AllElementsVisitor.VISITED_CLASS_ELEMENTS.find {
it.name == 'test.HelloController$Channel'
}

enumEl
def enmConsts = ((KotlinEnumElement) enumEl).elements()
enmConsts
enmConsts.size() == 2
enmConsts.find {
it.name == "SYSTEM1"
} != null
enmConsts.find {
it.name == "SYSTEM2"
} != null
}

void "test visitGeneratedFile"() {
given:
AllElementsVisitor.VISITED_CLASS_ELEMENTS.clear()
Expand Down

0 comments on commit 2f35acb

Please sign in to comment.