rewrite-kotlin icon indicating copy to clipboard operation
rewrite-kotlin copied to clipboard

Kotlin external classpath artifact fail to parse

Open smwhit opened this issue 1 year ago • 3 comments

I am using

  • OpenRewrite v8.30.0
  • Gradle plugin v8.30.0
  • rewrite-kotlin v8.30.0

How are you running OpenRewrite?

Using openrewrite with kotlin/gradle plugin, single module

Following on from slack conversation at https://rewriteoss.slack.com/archives/C01A843MWG5/p1721744738864449?thread_ts=1721135504.974819&cid=C01A843MWG5

What is the smallest, simplest way to reproduce the problem?

Based on the rewrite starter recipe, I am trying to write a simple replacement that references the jar io.kotest:kotest-property-jvm, however it fails on parsing

package com.yourorg;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;
import org.openrewrite.kotlin.KotlinIsoVisitor;
import org.openrewrite.kotlin.KotlinTemplate;

@Value
@EqualsAndHashCode(callSuper = false)
public class MigrateUUIDArbUsage extends Recipe {
    @Override
    public String getDisplayName() {
        // language=markdown
        return "Convert Arb uuid usages`";
    }

    @Override
    public String getDescription() {
        return "Convert arb uuid usages.";
    }

    private static MethodMatcher MATCHER = new MethodMatcher("io.kotest.property.arbitrary.ArbsKt next(..)");

    @Override
    public TreeVisitor<?, ExecutionContext> getVisitor() {
        return Preconditions.check(new UsesMethod<>("io.kotest.property.arbitrary.ArbsKt next(..)", null),
                new KotlinIsoVisitor<ExecutionContext>() {
                    @Override
                    public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
//                        System.out.println(TreeVisitingPrinter.printTree(getCursor()));
                        J.MethodInvocation m = super.visitMethodInvocation(method, ctx);

                        if (!MATCHER.matches(m)) {
                            return m;
                        }

                        if (m.getSelect() instanceof J.Identifier && ((J.Identifier) m.getSelect()).getSimpleName().equals("uuidArb")) {
                            maybeAddImport("io.kotest.property.Arb", false);
                            maybeAddImport("io.kotest.property.arbitrary.UUIDVersion", false);
                            maybeAddImport("io.kotest.property.arbitrary.next", false);

                            m = KotlinTemplate.builder("Arb.uuid(UUIDVersion.V4, false).next()")
                                            .doBeforeParseTemplate(t -> System.out.println(t))
                                            .imports(
                                            "io.kotest.property.arbitrary.next",
                                            "io.kotest.property.arbitrary.UUIDVersion",
                                            "io.kotest.property.Arb")
                                            .javaParser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true)
                                            .classpath(
                                                   "kotest-property-jvm"
                                            )
                                    )
                                    .build()
                                    .apply(getCursor(), m.getCoordinates().replace());
                        }
                        return m;
                    }
                });
    }
}
package com.yourorg

import org.junit.jupiter.api.Test
import org.openrewrite.DocumentExample
import org.openrewrite.kotlin.Assertions
import org.openrewrite.kotlin.KotlinParser
import org.openrewrite.test.RecipeSpec
import org.openrewrite.test.RewriteTest

class ArbTransformTest: RewriteTest {
    override fun defaults(spec: RecipeSpec) {
        spec.recipe(MigrateUUIDArbUsage())
            .parser(
                KotlinParser.builder() //.parser(
                //JavaParser.fromJavaVersion()
                    .classpath(
                        "kotest-property-jvm",
                        "kotest-property",
                        "arbs",
                        "junit-jupiter-api")
            )
    }

    @DocumentExample
    @Test
    fun kotlinFoo() {
        rewriteRun(
            Assertions.kotlin(
                """
                import io.kotest.property.arbitrary.next
                import org.junit.jupiter.api.Assertions
                import org.junit.jupiter.api.Test
                import org.smw.arbs.uuidArb
                
                class TestTest {
                    @Test
                    fun arbTest() {
                        val uuid = uuidArb.next()
                        Assertions.assertEquals(uuid, uuid)
                    }
                }

            """.trimIndent(), """
                import io.kotest.property.Arb
                import io.kotest.property.arbitrary.UUIDVersion
                import io.kotest.property.arbitrary.next
                import org.junit.jupiter.api.Assertions
                import org.junit.jupiter.api.Test
                
                class TestTest {
                    @Test
                    fun arbTest() {
                        val uuid = Arb.uuid(UUIDVersion.V4, false).next()
                        Assertions.assertEquals(uuid, uuid)
                    }
                }

            """.trimIndent()
            )
        )
    }
}

Error when running the test is

import io.kotest.property.arbitrary.next
import io.kotest.property.arbitrary.UUIDVersion
import io.kotest.property.Arb
import org.openrewrite.java.internal.template.__M__;
import org.openrewrite.java.internal.template.__P__;
class Template {
var o : Object =
/*__TEMPLATE__*/Arb.uuid(UUIDVersion.V4, false).next()/*__TEMPLATE_STOP__*/
;
}

java.lang.IllegalStateException: LST contains missing or invalid type information
Identifier->MethodInvocation->MethodInvocation->NamedVariable->VariableDeclarations->Block->MethodDeclaration->Block->ClassDeclaration->CompilationUnit
/*~~(Identifier type is missing or malformed)~~>*/Arb

	at org.openrewrite.kotlin.Assertions.assertValidTypes(Assertions.java:235)
	at org.openrewrite.kotlin.Assertions.validateTypes(Assertions.java:56)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:508)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:132)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:127)
	at com.yourorg.ArbTransformTest.kotlinFoo(ArbTransformTest.kt:27)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)


The artifact is included in the project via testRuntimeOnly("io.kotest:kotest-property:5.9.1")

If I add spec.typeValidationOptions(TypeValidation.none()) to the test, it passes, ideally would like to avoid this.

Are you interested in contributing a fix to OpenRewrite?

Happy to help where I can

smwhit avatar Jul 24 '24 10:07 smwhit