Nathan Bedell
08/08/2021, 1:22 AM/** Kotlin compiler plugin to automatically derive a Buildable instance for any class
* annotated Buildable. */
val Meta.genBuildable: CliPlugin get() =
"GenBuildable" {
meta(
classDeclaration(
ctx = this,
match = {
// ...
},
map = { (c, d) ->
val dataClass = classAsDataClass(c)!!
Transform.replace(
replacing = c,
newDeclaration = c
.text.`class`.syntheticScope.value!!.also { it ->
it.addDeclaration(
generatePartialClass(dataClass)
.toString()
.declaration<KtDeclaration>()
.value!!
)
it.addDeclaration(
generateCtx(dataClass)
.toString()
.declaration<KtDeclaration>()
.value!!
)
it
}.text
.`class`
)
}
)
)
}
I've tried this, as well as a few similar things. However, every time I try to run my plugin, I get the error:
java.lang.IllegalArgumentException: Missing extension point: org.jetbrains.kotlin.com.intellij.treeCopyHandler in container org.jetbrains.kotlin.com.intellij.core.CoreApplicationEnvironment$1@757c71a1
at org.jetbrains.kotlin.com.intellij.openapi.extensions.impl.ExtensionsAreaImpl.getExtensionPoint(ExtensionsAreaImpl.java:260)
I didn't see any examples like what I'm trying to do in the arrow-meta-examples
, so I was wondering if someone could point me in the right direction with this. I'm planning on making this an open-source project when finished, so I'd be happy to provide the full source code if someone wants to take a closer look -- but any help would be much appreciated!raulraja
08/08/2021, 10:28 AMderiving
in Kotlin data classes is that most of the derivable type classes like Show, Eq etc already have corresponding members that will get called as special operators like (EQEQ) that are already emitted based on the structure by data classes.Nathan Bedell
08/08/2021, 8:51 PM@DeriveBuildable
data class MyData(
val arg1: String
val arg2: Int?
)
I want to transform the AST (or, as you say, it may be better to do this in the backend IR phase) so that MyData
has a class body implementing the Buildable
interface, as well as the two inner interfaces of Buildable.
The motivation for this is to automatically derive a "Partial" version of a data type, where the fields may be null, with a monoid operation for combining these partial data types, together with a build
operation that attempts to turn a partial data type into a non-partial version, if all of the relevant fields are non-null.raulraja
08/08/2021, 10:01 PMTransform.newSources
or with an IR transformation.
If we follow the model in your gist then you are just generating a type class based on the class structure. You could just source code generation in this case if you are gonna consume it ad-hoc like that and what you declare is also seen in the same module in resolution. You’d be able to refer to the symbols just like in your gist.
If instead you want MyData
to directly implement Buildable<MyData>
to be consumed in a different module from where its compiled you can use irClass
and mutate the tree before codegen.
Once you decide how to proceed if you want to share a repo where this is happening we can look into it and help you with any apis for codegen or IR 🙂raulraja
08/08/2021, 10:06 PMNathan Bedell
08/08/2021, 10:24 PMMyData
implementing Buildable<MyData>
directly, I think now that I think about it, generating a typeclass for it would be just as work-able. So I think for now I will try the Transform.newSources
approach instead, and see how that goes.raulraja
08/08/2021, 10:27 PMNathan Bedell
08/08/2021, 10:32 PMDataN
interfaces (with operator fun componentN()
methods), and a lot of boilerplate duplicated to make functions working on Data2
, Data3
... DataN
.
So if Kotlin brought back tuples, that'd certainly be helpful as well!Nathan Bedell
10/12/2021, 11:23 PMTransform.replace
has the disadvantage of showing up red-lined in the IDE. I moved to using Transform.newSources
in my plugin as suggested -- and I now have the plugin working the way that I want, but the generated extension methods are still showing up as red-lined in the IDE.
Is this a known issue, or am I doing something wrong here?
https://github.com/Sintrastes/buildable-ktraulraja
10/13/2021, 8:10 AMmeta
folder and our gradle plugin would automatically add to the source set. If you want to add the source sets manually for the time being https://youtrack.jetbrains.com/issue/KT-16874 or similar allow you through gradle add those as generate sources.