Kotlin Recipe Declarative String Arguments Instantiate as Empty String
As of OpenRewrite 8.50.0, String options passed declaratively into Kotlin recipes are being instantiated as "".
Debugging a bit, it looks like this issue was caused by this change to the Recipe Loader, which is now delegating recipe instantiation to RecipeIntrospectionUtils.constructRecipe, which sets Kotlin String arguments to "" here. That logic appears to have been added purely for validation, and was not intended to instantiate recipe args.
- See https://github.com/openrewrite/rewrite/issues/3508
What version of OpenRewrite are you using?
8.54.0
How are you running OpenRewrite?
Unit Tests in private repo.
What is the smallest, simplest way to reproduce the problem?
Declarative Yaml
type: specs.openrewrite.org/v1beta/recipe
name: net.sample.kotlin.KotlinReplaceAllRecipeWrapper
displayName: Kotlin Replace All Wrapper
description: Tests a simple Kotlin recipe that replaces all text with the provided option.
recipeList:
- net.sample.kotlin.KotlinReplaceAllRecipe:
replacementText: "REPLACEMENT_TEXT"
Imperative Kotlin Recipe
package net.sample.kotlin
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import org.openrewrite.*
import org.openrewrite.text.PlainText
import org.openrewrite.text.PlainTextVisitor
class KotlinReplaceAllRecipe @JsonCreator constructor(
@Option(
displayName = "Replacement Text",
description = "Resulting text after the replacement occurs."
) @JsonProperty("replacementText") val replacementText: String
): Recipe() {
override fun getDisplayName() = "Tests a simple Kotlin Recipe With Options"
override fun getDescription() = "A test case for recipe authoring in Kotlin."
override fun getVisitor(): TreeVisitor<*, ExecutionContext> =
object: PlainTextVisitor<ExecutionContext>() {
override fun visitText(text: PlainText, p: ExecutionContext): PlainText {
return super.visitText(text.withText(replacementText), p)
}
}
}
Recipe Test
package net.sample.kotlin
import org.junit.jupiter.api.Test
import org.openrewrite.test.RecipeSpec
import org.openrewrite.test.RewriteTest
import org.openrewrite.test.SourceSpecs.text
class KotlinReplaceAllRecipeTest: RewriteTest {
override fun defaults(spec: RecipeSpec) {
spec.recipeFromResources("net.sample.kotlin.KotlinReplaceAllRecipeWrapper")
}
@Test
fun `replaces all text with provided replacement argument text`() = rewriteRun(text("Some Text", "REPLACEMENT_TEXT"))
}
Test Output
Expected :"REPLACEMENT_TEXT"
Actual :""
What did you expect to see?
"REPLACEMENT_TEXT"
What did you see instead?
""
@knutwannheden any insights here given your work on
- https://github.com/openrewrite/rewrite/pull/5234 ?
https://github.com/openrewrite/rewrite/blob/d7bb97c1369a8a3bbfdf7de8ad0ec841044c3f1b/rewrite-core/src/main/java/org/openrewrite/internal/RecipeIntrospectionUtils.java#L123-L127