How to use KotlinPoet to get correct TypeName for PropertySpec
Asked Answered
M

2

8

Using KotlinPoet, in order to generate a PropertySpec for adding properties to classes and constructors, you need a TypeName object.

The TypeMirror.asTypeName() KotlinPoet function is deprecated, because it won't always work correctly for Kotlin types.

But I can't find a single example of how to get a correct TypeName for a Kotlin class (e.g. kotlin.String) using the kotlinpoet-metadata APIs, the way the deprecation message says.

The docs for kotlinpoet-metadata APIs also seem completely broken (go to https://square.github.io/kotlinpoet/interop-kotlinx-metadata/#interop-with-kotlinx-metadata and click anything under the APIs section)

Does anyone have an example of how to replace TypeMirror.asTypeName() with some kotlinpoet-metadata code to get a TypeName, so that I can create a PropertySpec?

Metchnikoff answered 13/4, 2021 at 16:15 Comment(5)
Do you know the class you're referring to in advance, or is the class determined by your annotation processor and it's only available as a TypeMirror?Pierian
the class is provided in the processor as an annotated element, so I only have a type mirror. I know in this case that the current breakage is with a String. asTypeName always produces java.lang.String, but I need kotlin.String.Metchnikoff
Correct links to API docs:interop-kotlinx-metadata-classinspectors-elements,interop-kotlinx-metadata-classinspectors-reflect,interop-kotlinx-metadata-core,interop-kotlinx-metadata-specsOrvah
Also there is a missing link in Overview section: KotlinPoet-metadata-specsOrvah
For when the class is determined by your annotation processor and it's only available as a TypeMirror, are there any available examples? Links seem to 404. Thanks!Highmuckamuck
G
3

Not very sure if this aligns with the intention of the deprecation message, but this is what I got it to work.

I first had to add kotlinpoet-metadata-specs.

implementation("com.squareup:kotlinpoet:1.7.1")
implementation("com.squareup:kotlinpoet-metadata:1.7.1")
implementation("com.squareup:kotlinpoet-metadata-specs:1.7.1")

Then use a util method from com.squareup.kotlinpoet.metadata.specs.internal.ClassInspectorUtil to create className.

val packageName = getPackage(element).qualifiedName.toString()
val typeMetadata = element.getAnnotation(Metadata::class.java)
val kmClass = typeMetadata.toImmutableKmClass()
val className = ClassInspectorUtil.createClassName(kmClass.name)

then use

val funSpec = FunSpec.builder("allNullableSet")
                     .receiver(className)
                     .returns(Boolean::class.java)
                     .addStatement(statement)
                     .build()
Glyptic answered 5/6, 2021 at 9:29 Comment(2)
Thanks this is useful, but ClassInspectorUtil is marked as internal in kotlinpoet. I wonder if there is a way to get a TypeName from an ImmutableKmClass using the public API?Binford
This also won't work for properties, since only classes get Metadata annotations. In this case, for constructor parameters, I can use: annotatedElement.enclosingElement.getAnnotation(Metadata::class.java).toImmutableKmClass().constructors[0].parameters to get the ctor parameters, but they are ImmutableKmValueParameters, not classes. Any way to get a TypeName for an ImmutableKmValueParameter? I can maybe pose a new question for this case...Metchnikoff
A
0

I have found a way to get the TypeName of a TypeElement in my AbstractProcessor, thanks to having access to its processingEnv:

val kmClass = (typeElement.kotlinClassMetadata() as KotlinClassMetadata.Class).toKmClass()
val elementName: TypeName = ClassName(processingEnv.elementUtils.getPackageOf(typeElement).toString(), kmClass.name.substringAfterLast("/"))

It should also be doable without processingEnv by splitting the kmClass.name manually.

Aishaaisle answered 18/2, 2023 at 6:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.