Skip to content

Instantly share code, notes, and snippets.

@raulraja
Created November 5, 2019 12:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raulraja/34078fcce7809db1833f4b89c154bd30 to your computer and use it in GitHub Desktop.
Save raulraja/34078fcce7809db1833f4b89c154bd30 to your computer and use it in GitHub Desktop.
[toString] replacement, originally discussed in https://kotlinlang.slack.com/archives/C7L3JB43G/p1572884683059400
package arrow.meta.plugins.helloWorld
import arrow.meta.Meta
import arrow.meta.Plugin
import arrow.meta.invoke
import arrow.meta.phases.analysis.ElementScope
import arrow.meta.quotes.ClassScope
import arrow.meta.quotes.Scope
import arrow.meta.quotes.Transform
import arrow.meta.quotes.classOrObject
import arrow.meta.quotes.func
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtNamedFunction
val Meta.toStringReplacement: Plugin
get() =
"toStringReplacement" {
meta(
/**
* Replaces [toString] with a custom impl when [toString already exists as an override]
*/
func(KtNamedFunction::toStringPresent) { toString ->
Transform.replace(
replacing = toString,
newDeclaration = toStringReplacement()
)
},
/**
* Adds [toString] with a custom impl to all data classes that have no [toString] method
*/
classOrObject(KtClass::dataClassInheritsToString) { dataClass ->
Transform.replace(
replacing = dataClass,
newDeclaration = dataClassWithAddedToString(ctx)
)
}
)
}
/**
* Used when a data class does not have [toString]
*/
private fun ClassScope.dataClassWithAddedToString(elementScope: ElementScope): ClassScope =
elementScope.run {
"""|$`@annotationEntries` $kind $name $`(typeParameters)` $`(valueParameters)` : $supertypes {
| $body
| ${toStringReplacement()}
|}
|""".`class`
}
/**
* The [toString] replacement function
*/
private fun ElementScope.toStringReplacement(): Scope<KtNamedFunction> =
"""|override fun toString(): String =
| "replaced!"
|""".function.synthetic
/**
* Matches all data classes that do not include [toString] as an override
*/
private fun KtClass.dataClassInheritsToString(): Boolean =
isData() && declarations.filterIsInstance<KtNamedFunction>().all { !it.toStringPresent() }
/**
* A [toString] function is considered when it's in a data class and it's an override
*/
private fun KtNamedFunction.toStringPresent(): Boolean =
name == "toString" &&
(psiOrParent as? KtClass)?.isData() == true &&
modifierList?.hasModifier(KtModifierKeywordToken.keywordModifier("override")) == true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment